added byte and ubyte types to SKSL

created new GMs for skinning

Bug: skia:
Change-Id: I15fb2bd02fba8beb6dd2dd3f3716da016ea92192
Reviewed-on: https://skia-review.googlesource.com/140241
Commit-Queue: Ruiqi Mao <ruiqimao@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
diff --git a/gm/convexpolyeffect.cpp b/gm/convexpolyeffect.cpp
index 78dcfe1..41d53e6 100644
--- a/gm/convexpolyeffect.cpp
+++ b/gm/convexpolyeffect.cpp
@@ -80,7 +80,11 @@
 
         Color color(fColor);
         sk_sp<GrGeometryProcessor> gp(GrDefaultGeoProcFactory::Make(
-                color, Coverage::kSolid_Type, LocalCoords::kUnused_Type, SkMatrix::I()));
+                target->caps().shaderCaps(),
+                color,
+                Coverage::kSolid_Type,
+                LocalCoords::kUnused_Type,
+                SkMatrix::I()));
 
         SkASSERT(gp->debugOnly_vertexStride() == sizeof(SkPoint));
         QuadHelper helper;
diff --git a/gm/skinning.cpp b/gm/skinning.cpp
new file mode 100644
index 0000000..101522f
--- /dev/null
+++ b/gm/skinning.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2018 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 "SkCanvas.h"
+#include "SkVertices.h"
+#include "SkPoint.h"
+#include <iostream>
+#include <vector>
+
+using namespace skiagm;
+
+static const int kCellSize = 60;
+static const int kColumnSize = 36;
+
+static const int kBoneCount = 7;
+static const SkMatrix kBones[] = {
+    SkMatrix::I(),
+    SkMatrix::MakeTrans(10, 0),
+    SkMatrix::MakeTrans(0, 10),
+    SkMatrix::MakeTrans(-10, 0),
+    SkMatrix::MakeTrans(0, -10),
+    SkMatrix::MakeScale(0.5f),
+    SkMatrix::MakeScale(1.5f),
+};
+
+static const int kVertexCount = 4;
+static const SkPoint kPositions[] = {
+    { 0, 0 },
+    { 0, 30 },
+    { 30, 30 },
+    { 30, 0 },
+};
+static const SkColor kColors[] = {
+    0xFFFF0000,
+    0xFF00FF00,
+    0xFF0000FF,
+    0xFFFFFF00,
+};
+static const SkVertices::BoneIndices kBoneIndices[] = {
+    {{ 1, 0, 0, 0 }},
+    {{ 2, 1, 0, 0 }},
+    {{ 3, 2, 1, 0 }},
+    {{ 4, 3, 2, 1 }},
+};
+static const SkVertices::BoneWeights kBoneWeights[] = {
+    {{ 1.0f,  0.0f,  0.0f,  0.0f  }},
+    {{ 0.5f,  0.5f,  0.0f,  0.0f  }},
+    {{ 0.34f, 0.33f, 0.33f, 0.0f  }},
+    {{ 0.25f, 0.25f, 0.25f, 0.25f }},
+};
+
+static const int kIndexCount = 6;
+static const uint16_t kIndices[] = {
+    0, 1, 2,
+    2, 3, 0,
+};
+
+// Swap two SkMatrix pointers in place.
+static void swap(const SkMatrix** x, const SkMatrix** y) {
+    const SkMatrix* temp = *x;
+    *x = *y;
+    *y = temp;
+}
+
+class SkinningGM : public GM {
+
+public:
+    SkinningGM(bool deformUsingCPU)
+            : fPaint()
+            , fVertices(nullptr)
+            , fDeformUsingCPU(deformUsingCPU)
+    {}
+
+protected:
+    bool runAsBench() const override {
+        return true;
+    }
+
+    SkString onShortName() override {
+        SkString name("skinning");
+        if (fDeformUsingCPU) {
+            name.append("_cpu");
+        }
+        return name;
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(2400, 2400);
+    }
+
+    void onOnceBeforeDraw() override {
+        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                         kVertexCount,
+                                         kPositions,
+                                         nullptr,
+                                         kColors,
+                                         kBoneIndices,
+                                         kBoneWeights,
+                                         kIndexCount,
+                                         kIndices,
+                                         false); // Keep the vertices non-volatile.
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        // Set the initial position.
+        int xpos = kCellSize;
+        int ypos = kCellSize;
+
+        // Create the mutable set of bones.
+        const SkMatrix* bones[kBoneCount];
+        for (int i = 0; i < kBoneCount; i ++) {
+            bones[i] = &kBones[i];
+        }
+
+        // Draw the vertices.
+        drawPermutations(canvas, xpos, ypos, bones, 1);
+    }
+
+private:
+    void drawPermutations(SkCanvas* canvas,
+                          int& xpos,
+                          int& ypos,
+                          const SkMatrix** bones,
+                          int start) {
+        if (start == kBoneCount) {
+            // Reached the end of the permutations, so draw.
+            canvas->save();
+
+            // Copy the bones.
+            SkMatrix copiedBones[kBoneCount];
+            for (int i = 0; i < kBoneCount; i ++) {
+                copiedBones[i] = *bones[i];
+            }
+
+            // Set the position.
+            canvas->translate(xpos, ypos);
+
+            // Draw the vertices.
+            if (fDeformUsingCPU) {
+                // Deform with CPU.
+                std::vector<SkPoint> positions(kVertexCount);
+                for (int i = 0; i < kVertexCount; i ++) {
+                    const SkVertices::BoneIndices& indices = kBoneIndices[i];
+                    const SkVertices::BoneWeights& weights = kBoneWeights[i];
+
+                    // Apply deformations.
+                    SkPoint& result = positions[i];
+                    SkPoint transformed;
+                    for (uint32_t j = 0; j < 4; j ++) {
+                        // Get the bone attachment data.
+                        uint32_t index = indices.indices[j];
+                        float weight = weights.weights[j];
+
+                        // Skip the bone is there is no weight.
+                        if (weight == 0.0f) {
+                            continue;
+                        }
+                        SkASSERT(index != 0);
+
+                        // transformed = M * v
+                        copiedBones[index].mapPoints(&transformed, &kPositions[i], 1);
+
+                        // result += transformed * w
+                        result += transformed * weight;
+                    }
+                }
+
+                sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                                                  kVertexCount,
+                                                                  positions.data(),
+                                                                  nullptr,
+                                                                  kColors,
+                                                                  kIndexCount,
+                                                                  kIndices);
+                canvas->drawVertices(vertices.get(),
+                                     SkBlendMode::kSrc,
+                                     fPaint);
+            } else {
+                // Deform with GPU.
+                canvas->drawVertices(fVertices.get(),
+                                     copiedBones,
+                                     kBoneCount,
+                                     SkBlendMode::kSrc,
+                                     fPaint);
+            }
+
+            canvas->restore();
+
+            // Get a new position to draw the vertices.
+            xpos += kCellSize;
+            if (xpos > kCellSize * kColumnSize) {
+                xpos = kCellSize;
+                ypos += kCellSize;
+            }
+
+            return;
+        }
+
+        // Find all possible permutations within the given range.
+        for (int i = start; i < kBoneCount; i ++) {
+            // Swap the start and i-th elements.
+            swap(bones + start, bones + i);
+
+            // Find permutations of the sub array.
+            drawPermutations(canvas, xpos, ypos, bones, start + 1);
+
+            // Swap the elements back.
+            swap(bones + i, bones + start);
+        }
+    }
+
+private:
+    SkPaint fPaint;
+    sk_sp<SkVertices> fVertices;
+    bool fDeformUsingCPU;
+
+    typedef GM INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new SkinningGM(true);)
+DEF_GM(return new SkinningGM(false);)
diff --git a/gn/gm.gni b/gn/gm.gni
index 1b60d5a..111d68d 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -285,6 +285,7 @@
   "$_gm/skbug_4868.cpp",
   "$_gm/skbug_5321.cpp",
   "$_gm/skbug1719.cpp",
+  "$_gm/skinning.cpp",
   "$_gm/smallarc.cpp",
   "$_gm/smallpaths.cpp",
   "$_gm/spritebitmap.cpp",
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index 1490574..ee6bfd8 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -289,6 +289,14 @@
 enum GrSLType {
     kVoid_GrSLType,
     kBool_GrSLType,
+    kByte_GrSLType,
+    kByte2_GrSLType,
+    kByte3_GrSLType,
+    kByte4_GrSLType,
+    kUByte_GrSLType,
+    kUByte2_GrSLType,
+    kUByte3_GrSLType,
+    kUByte4_GrSLType,
     kShort_GrSLType,
     kShort2_GrSLType,
     kShort3_GrSLType,
@@ -389,6 +397,14 @@
         case kTexture2DRectSampler_GrSLType:
         case kBufferSampler_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kByte2_GrSLType:
+        case kByte3_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte_GrSLType:
+        case kUByte2_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
         case kShort_GrSLType:
         case kShort2_GrSLType:
         case kShort3_GrSLType:
@@ -417,6 +433,8 @@
         case kFloat_GrSLType:
         case kHalf_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kUByte_GrSLType:
         case kShort_GrSLType:
         case kUShort_GrSLType:
         case kInt_GrSLType:
@@ -425,6 +443,8 @@
 
         case kFloat2_GrSLType:
         case kHalf2_GrSLType:
+        case kByte2_GrSLType:
+        case kUByte2_GrSLType:
         case kShort2_GrSLType:
         case kUShort2_GrSLType:
         case kInt2_GrSLType:
@@ -433,6 +453,8 @@
 
         case kFloat3_GrSLType:
         case kHalf3_GrSLType:
+        case kByte3_GrSLType:
+        case kUByte3_GrSLType:
         case kShort3_GrSLType:
         case kUShort3_GrSLType:
         case kInt3_GrSLType:
@@ -440,6 +462,8 @@
 
         case kFloat4_GrSLType:
         case kHalf4_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte4_GrSLType:
         case kShort4_GrSLType:
         case kUShort4_GrSLType:
         case kInt4_GrSLType:
@@ -494,6 +518,14 @@
         case kUint2_GrSLType:
         case kBufferSampler_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kByte2_GrSLType:
+        case kByte3_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte_GrSLType:
+        case kUByte2_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
         case kShort_GrSLType:
         case kShort2_GrSLType:
         case kShort3_GrSLType:
@@ -540,6 +572,14 @@
         case kUint_GrSLType:
         case kUint2_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kByte2_GrSLType:
+        case kByte3_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte_GrSLType:
+        case kUByte2_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
         case kShort_GrSLType:
         case kShort2_GrSLType:
         case kShort3_GrSLType:
@@ -568,6 +608,14 @@
 
         case kVoid_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kByte2_GrSLType:
+        case kByte3_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte_GrSLType:
+        case kUByte2_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
         case kShort_GrSLType:
         case kShort2_GrSLType:
         case kShort3_GrSLType:
@@ -638,6 +686,14 @@
 
         case kVoid_GrSLType:
         case kBool_GrSLType:
+        case kByte_GrSLType:
+        case kByte2_GrSLType:
+        case kByte3_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte_GrSLType:
+        case kUByte2_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
         case kShort2_GrSLType:
         case kShort3_GrSLType:
         case kShort4_GrSLType:
@@ -669,6 +725,16 @@
     kInt3_GrVertexAttribType,   // vector of 3 32-bit ints
     kInt4_GrVertexAttribType,   // vector of 4 32-bit ints
 
+
+    kByte_GrVertexAttribType,  // signed byte
+    kByte2_GrVertexAttribType, // vector of 2 8-bit signed bytes
+    kByte3_GrVertexAttribType, // vector of 3 8-bit signed bytes
+    kByte4_GrVertexAttribType, // vector of 4 8-bit signed bytes
+    kUByte_GrVertexAttribType,  // unsigned byte
+    kUByte2_GrVertexAttribType, // vector of 2 8-bit unsigned bytes
+    kUByte3_GrVertexAttribType, // vector of 3 8-bit unsigned bytes
+    kUByte4_GrVertexAttribType, // vector of 4 8-bit unsigned bytes
+
     kUByte_norm_GrVertexAttribType,  // unsigned byte, e.g. coverage, 0 -> 0.0f, 255 -> 1.0f.
     kUByte4_norm_GrVertexAttribType, // vector of 4 unsigned bytes, e.g. colors, 0 -> 0.0f,
                                      // 255 -> 1.0f.
@@ -719,6 +785,22 @@
             return kInt3_GrSLType;
         case kInt4_GrVertexAttribType:
             return kInt4_GrSLType;
+        case kByte_GrVertexAttribType:
+            return kByte_GrSLType;
+        case kByte2_GrVertexAttribType:
+            return kByte_GrSLType;
+        case kByte3_GrVertexAttribType:
+            return kByte_GrSLType;
+        case kByte4_GrVertexAttribType:
+            return kByte4_GrSLType;
+        case kUByte_GrVertexAttribType:
+            return kUByte_GrSLType;
+        case kUByte2_GrVertexAttribType:
+            return kUByte_GrSLType;
+        case kUByte3_GrVertexAttribType:
+            return kUByte_GrSLType;
+        case kUByte4_GrVertexAttribType:
+            return kUByte4_GrSLType;
         case kInt_GrVertexAttribType:
             return kInt_GrSLType;
         case kUint_GrVertexAttribType:
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index c1f8118..cb98b09 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1682,7 +1682,7 @@
                             SkBlendMode mode, const SkPaint& paint) {
     TRACE_EVENT0("skia", TRACE_FUNC);
     RETURN_ON_NULL(vertices);
-    SkASSERT(boneCount <= 100);
+    SkASSERT(boneCount <= 80);
     this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
 }
 
@@ -1690,7 +1690,7 @@
                             SkBlendMode mode, const SkPaint& paint) {
     TRACE_EVENT0("skia", TRACE_FUNC);
     RETURN_ON_NULL(vertices);
-    SkASSERT(boneCount <= 100);
+    SkASSERT(boneCount <= 80);
     this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
 }
 
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index b1c423a..027296c 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -7,6 +7,7 @@
 
 #include "GrDefaultGeoProcFactory.h"
 
+#include "GrCaps.h"
 #include "SkRefCnt.h"
 #include "glsl/GrGLSLColorSpaceXformHelper.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
@@ -30,12 +31,13 @@
     kBonesAttribute_GPFlag          = 0x10,
 };
 
-static constexpr int kMaxBones = 100; // Due to GPU memory limitations, only up to 100 bone
-                                      // matrices are accepted.
+static constexpr int kMaxBones = 80; // Due to GPU memory limitations, only up to 80 bone
+                                     // matrices are accepted.
 
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
-    static sk_sp<GrGeometryProcessor> Make(uint32_t gpTypeFlags,
+    static sk_sp<GrGeometryProcessor> Make(const GrShaderCaps* shaderCaps,
+                                           uint32_t gpTypeFlags,
                                            GrColor color,
                                            sk_sp<GrColorSpaceXform> colorSpaceXform,
                                            const SkMatrix& viewMatrix,
@@ -45,8 +47,8 @@
                                            const float* bones,
                                            int boneCount) {
         return sk_sp<GrGeometryProcessor>(new DefaultGeoProc(
-                gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix, coverage,
-                localCoordsWillBeRead, bones, boneCount));
+                shaderCaps, gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix,
+                coverage, localCoordsWillBeRead, bones, boneCount));
     }
 
     const char* name() const override { return "DefaultGeometryProcessor"; }
@@ -118,17 +120,26 @@
                                                                 kMaxBones,
                                                                 &vertBonesUniformName);
                 vertBuilder->codeAppendf(
-                        "float2 transformedPosition = (%s[0] * float3(%s, 1)).xy;"
-                        "float3x3 influence = float3x3(0);"
-                        "for (int i = 0; i < 4; i++) {"
-                        "   int index = %s[i];"
-                        "   float weight = %s[i];"
-                        "   influence += %s[index] * weight;"
-                        "}"
-                        "transformedPosition = (influence * float3(transformedPosition, 1)).xy;",
+                        "float3 originalPosition = %s[0] * float3(%s, 1);"
+                        "float2 transformedPosition = float2(0);"
+                        "for (int i = 0; i < 4; i++) {",
                         vertBonesUniformName,
-                        gp.fInPosition.name(),
-                        gp.fInBoneIndices.name(),
+                        gp.fInPosition.name());
+
+                if (args.fShaderCaps->unsignedSupport()) {
+                    vertBuilder->codeAppendf(
+                        "    byte index = %s[i];",
+                        gp.fInBoneIndices.name());
+                } else {
+                    vertBuilder->codeAppendf(
+                        "    byte index = byte(floor(%s[i] * 255 + 0.5));",
+                        gp.fInBoneIndices.name());
+                }
+
+                vertBuilder->codeAppendf(
+                        "    float weight = %s[i];"
+                        "    transformedPosition += (%s[index] * originalPosition * weight).xy;"
+                        "}",
                         gp.fInBoneWeights.name(),
                         vertBonesUniformName);
                 transformedPositionName = "transformedPosition";
@@ -243,7 +254,8 @@
     }
 
 private:
-    DefaultGeoProc(uint32_t gpTypeFlags,
+    DefaultGeoProc(const GrShaderCaps* shaderCaps,
+                   uint32_t gpTypeFlags,
                    GrColor color,
                    sk_sp<GrColorSpaceXform> colorSpaceXform,
                    const SkMatrix& viewMatrix,
@@ -278,9 +290,13 @@
         }
         if (fFlags & kBonesAttribute_GPFlag) {
             SkASSERT(bones && (boneCount > 0));
-            fInBoneIndices = {"inBoneIndices", kInt4_GrVertexAttribType};
+            // GLSL 1.10 and 1.20 don't support integer attributes.
+            GrVertexAttribType indicesAttribType =
+                    shaderCaps->unsignedSupport() ? kByte4_GrVertexAttribType :
+                                                    kUByte4_norm_GrVertexAttribType;
+            fInBoneIndices = {"inBoneIndices", indicesAttribType};
             ++cnt;
-            fInBoneWeights = {"inBoneWeights", kFloat4_GrVertexAttribType};
+            fInBoneWeights = {"inBoneWeights", kUByte4_norm_GrVertexAttribType};
             ++cnt;
         }
         this->setVertexAttributeCnt(cnt);
@@ -347,7 +363,8 @@
         flags |= kBonesAttribute_GPFlag;
     }
 
-    return DefaultGeoProc::Make(flags,
+    return DefaultGeoProc::Make(d->caps()->shaderCaps(),
+                                flags,
                                 GrRandomColor(d->fRandom),
                                 GrTest::TestColorXform(d->fRandom),
                                 GrTest::TestMatrix(d->fRandom),
@@ -359,7 +376,8 @@
 }
 #endif
 
-sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::Make(const Color& color,
+sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::Make(const GrShaderCaps* shaderCaps,
+                                                         const Color& color,
                                                          const Coverage& coverage,
                                                          const LocalCoords& localCoords,
                                                          const SkMatrix& viewMatrix) {
@@ -376,7 +394,8 @@
     bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
 
     GrColor inColor = color.fColor;
-    return DefaultGeoProc::Make(flags,
+    return DefaultGeoProc::Make(shaderCaps,
+                                flags,
                                 inColor,
                                 color.fColorSpaceXform,
                                 viewMatrix,
@@ -388,6 +407,7 @@
 }
 
 sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
+                                                                     const GrShaderCaps* shaderCaps,
                                                                      const Color& color,
                                                                      const Coverage& coverage,
                                                                      const LocalCoords& localCoords,
@@ -405,10 +425,11 @@
     }
 
     LocalCoords inverted(LocalCoords::kUsePosition_Type, &invert);
-    return Make(color, coverage, inverted, SkMatrix::I());
+    return Make(shaderCaps, color, coverage, inverted, SkMatrix::I());
 }
 
-sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeWithBones(const Color& color,
+sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeWithBones(const GrShaderCaps* shaderCaps,
+                                                                  const Color& color,
                                                                   const Coverage& coverage,
                                                                   const LocalCoords& localCoords,
                                                                   const Bones& bones,
@@ -427,7 +448,8 @@
     bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
 
     GrColor inColor = color.fColor;
-    return DefaultGeoProc::Make(flags,
+    return DefaultGeoProc::Make(shaderCaps,
+                                flags,
                                 inColor,
                                 color.fColorSpaceXform,
                                 viewMatrix,
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index 52db807..3c4bd8e 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -10,6 +10,7 @@
 
 #include "GrColorSpaceXform.h"
 #include "GrGeometryProcessor.h"
+#include "GrShaderCaps.h"
 
 /*
  * A factory for creating default Geometry Processors which simply multiply position by the uniform
@@ -127,7 +128,8 @@
         int fBoneCount;
     };
 
-    sk_sp<GrGeometryProcessor> Make(const Color&,
+    sk_sp<GrGeometryProcessor> Make(const GrShaderCaps*,
+                                    const Color&,
                                     const Coverage&,
                                     const LocalCoords&,
                                     const SkMatrix& viewMatrix);
@@ -137,7 +139,8 @@
      * attribute. The view matrix must still be provided to compute correctly transformed
      * coordinates for GrFragmentProcessors. It may fail if the view matrix is not invertible.
      */
-    sk_sp<GrGeometryProcessor> MakeForDeviceSpace(const Color&,
+    sk_sp<GrGeometryProcessor> MakeForDeviceSpace(const GrShaderCaps*,
+                                                  const Color&,
                                                   const Coverage&,
                                                   const LocalCoords&,
                                                   const SkMatrix& viewMatrix);
@@ -147,7 +150,8 @@
      * deformation of vertices using matrices that are passed in. This should only be called from
      * GrDrawVerticesOp.
      */
-    sk_sp<GrGeometryProcessor> MakeWithBones(const Color&,
+    sk_sp<GrGeometryProcessor> MakeWithBones(const GrShaderCaps*,
+                                             const Color&,
                                              const Coverage&,
                                              const LocalCoords&,
                                              const Bones&,
diff --git a/src/gpu/GrPrimitiveProcessor.h b/src/gpu/GrPrimitiveProcessor.h
index 6563ad9..e47d868 100644
--- a/src/gpu/GrPrimitiveProcessor.h
+++ b/src/gpu/GrPrimitiveProcessor.h
@@ -178,6 +178,22 @@
             return 3 * sizeof(int32_t);
         case kInt4_GrVertexAttribType:
             return 4 * sizeof(int32_t);
+        case kByte_GrVertexAttribType:
+            return 1 * sizeof(char);
+        case kByte2_GrVertexAttribType:
+            return 2 * sizeof(char);
+        case kByte3_GrVertexAttribType:
+            return 3 * sizeof(char);
+        case kByte4_GrVertexAttribType:
+            return 4 * sizeof(char);
+        case kUByte_GrVertexAttribType:
+            return 1 * sizeof(char);
+        case kUByte2_GrVertexAttribType:
+            return 2 * sizeof(char);
+        case kUByte3_GrVertexAttribType:
+            return 3 * sizeof(char);
+        case kUByte4_GrVertexAttribType:
+            return 4 * sizeof(char);
         case kUByte_norm_GrVertexAttribType:
             return 1 * sizeof(char);
         case kUByte4_norm_GrVertexAttribType:
diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h
index ee3681a..a599f1d 100644
--- a/src/gpu/GrShaderCaps.h
+++ b/src/gpu/GrShaderCaps.h
@@ -84,6 +84,8 @@
 
     bool halfIs32Bits() const { return fHalfIs32Bits; }
 
+    bool unsignedSupport() const { return fUnsignedSupport; }
+
     AdvBlendEqInteraction advBlendEqInteraction() const { return fAdvBlendEqInteraction; }
 
     bool mustEnableAdvBlendEqs() const {
@@ -252,6 +254,7 @@
     bool fFPManipulationSupport             : 1;
     bool fFloatIs32Bits                     : 1;
     bool fHalfIs32Bits                      : 1;
+    bool fUnsignedSupport                   : 1;
 
     // Used for specific driver bug work arounds
     bool fCanUseAnyFunctionInShader                   : 1;
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 1f208c8..5e34c7a 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -801,6 +801,9 @@
 
     shaderCaps->fFloatIs32Bits = is_float_fp32(ctxInfo, gli, GR_GL_HIGH_FLOAT);
     shaderCaps->fHalfIs32Bits = is_float_fp32(ctxInfo, gli, GR_GL_MEDIUM_FLOAT);
+
+    // Unsigned integers only supported in and after GLSL 1.30.
+    shaderCaps->fUnsignedSupport = ctxInfo.glslGeneration() >= k130_GrGLSLGeneration;
 }
 
 bool GrGLCaps::hasPathRenderingSupport(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli) {
diff --git a/src/gpu/gl/GrGLVertexArray.cpp b/src/gpu/gl/GrGLVertexArray.cpp
index 68bcf21..deca17d 100644
--- a/src/gpu/gl/GrGLVertexArray.cpp
+++ b/src/gpu/gl/GrGLVertexArray.cpp
@@ -41,6 +41,22 @@
             return {false, 3, GR_GL_INT};
         case kInt4_GrVertexAttribType:
             return {false, 4, GR_GL_INT};
+        case kByte_GrVertexAttribType:
+            return {false, 1, GR_GL_BYTE};
+        case kByte2_GrVertexAttribType:
+            return {false, 2, GR_GL_BYTE};
+        case kByte3_GrVertexAttribType:
+            return {false, 3, GR_GL_BYTE};
+        case kByte4_GrVertexAttribType:
+            return {false, 4, GR_GL_BYTE};
+        case kUByte_GrVertexAttribType:
+            return {false, 1, GR_GL_UNSIGNED_BYTE};
+        case kUByte2_GrVertexAttribType:
+            return {false, 2, GR_GL_UNSIGNED_BYTE};
+        case kUByte3_GrVertexAttribType:
+            return {false, 3, GR_GL_UNSIGNED_BYTE};
+        case kUByte4_GrVertexAttribType:
+            return {false, 4, GR_GL_UNSIGNED_BYTE};
         case kUByte_norm_GrVertexAttribType:
             return {true, 1, GR_GL_UNSIGNED_BYTE};
         case kUByte4_norm_GrVertexAttribType:
@@ -85,6 +101,22 @@
             return true;
         case kInt4_GrVertexAttribType:
             return true;
+        case kByte_GrVertexAttribType:
+            return true;
+        case kByte2_GrVertexAttribType:
+            return true;
+        case kByte3_GrVertexAttribType:
+            return true;
+        case kByte4_GrVertexAttribType:
+            return true;
+        case kUByte_GrVertexAttribType:
+            return true;
+        case kUByte2_GrVertexAttribType:
+            return true;
+        case kUByte3_GrVertexAttribType:
+            return true;
+        case kUByte4_GrVertexAttribType:
+            return true;
         case kUByte_norm_GrVertexAttribType:
             return false;
         case kUByte4_norm_GrVertexAttribType:
diff --git a/src/gpu/glsl/GrGLSL.cpp b/src/gpu/glsl/GrGLSL.cpp
index 67fe299..807fe94 100644
--- a/src/gpu/glsl/GrGLSL.cpp
+++ b/src/gpu/glsl/GrGLSL.cpp
@@ -85,6 +85,22 @@
             return "ushort3";
         case kUShort4_GrSLType:
             return "ushort4";
+        case kByte_GrSLType:
+            return "byte";
+        case kByte2_GrSLType:
+            return "byte2";
+        case kByte3_GrSLType:
+            return "byte3";
+        case kByte4_GrSLType:
+            return "byte4";
+        case kUByte_GrSLType:
+            return "ubyte";
+        case kUByte2_GrSLType:
+            return "ubyte2";
+        case kUByte3_GrSLType:
+            return "ubyte3";
+        case kUByte4_GrSLType:
+            return "ubyte4";
         case kTexture2D_GrSLType:
             return "texture2D";
         case kSampler_GrSLType:
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index d3dc543..2527be9 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -242,6 +242,9 @@
     shaderCaps->fFloatIs32Bits = true;
     shaderCaps->fHalfIs32Bits = false;
 
+    // Metal supports unsigned integers.
+    shaderCaps->fUnsignedSupport = true;
+
     shaderCaps->fMaxVertexSamplers =
     shaderCaps->fMaxFragmentSamplers = 16;
     // For now just cap at the per stage max. If we hit this limit we can come back to adjust this
diff --git a/src/gpu/ops/GrAAConvexPathRenderer.cpp b/src/gpu/ops/GrAAConvexPathRenderer.cpp
index 7f54357..3d475b9 100644
--- a/src/gpu/ops/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAAConvexPathRenderer.cpp
@@ -723,7 +723,8 @@
     }
 }
 
-static sk_sp<GrGeometryProcessor> make_lines_only_gp(bool tweakAlphaForCoverage,
+static sk_sp<GrGeometryProcessor> make_lines_only_gp(const GrShaderCaps* shaderCaps,
+                                                     bool tweakAlphaForCoverage,
                                                      const SkMatrix& viewMatrix,
                                                      bool usesLocalCoords) {
     using namespace GrDefaultGeoProcFactory;
@@ -736,7 +737,10 @@
     }
     LocalCoords::Type localCoordsType =
             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
-    return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
+    return MakeForDeviceSpace(shaderCaps,
+                              Color::kPremulGrColorAttribute_Type,
+                              coverageType,
+                              localCoordsType,
                               viewMatrix);
 }
 
@@ -790,7 +794,8 @@
 private:
     void prepareLinesOnlyDraws(Target* target) {
         // Setup GrGeometryProcessor
-        sk_sp<GrGeometryProcessor> gp(make_lines_only_gp(fHelper.compatibleWithAlphaAsCoverage(),
+        sk_sp<GrGeometryProcessor> gp(make_lines_only_gp(target->caps().shaderCaps(),
+                                                         fHelper.compatibleWithAlphaAsCoverage(),
                                                          fPaths.back().fViewMatrix,
                                                          fHelper.usesLocalCoords()));
         if (!gp) {
diff --git a/src/gpu/ops/GrAAFillRectOp.cpp b/src/gpu/ops/GrAAFillRectOp.cpp
index 0d97de49..592c0b2 100644
--- a/src/gpu/ops/GrAAFillRectOp.cpp
+++ b/src/gpu/ops/GrAAFillRectOp.cpp
@@ -255,7 +255,8 @@
         }
 
         sk_sp<GrGeometryProcessor> gp =
-                GrDefaultGeoProcFactory::Make(color, coverageType, lc, SkMatrix::I());
+                GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(), color, coverageType,
+                                              lc, SkMatrix::I());
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
diff --git a/src/gpu/ops/GrAAHairLinePathRenderer.cpp b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
index bed8cbf..d6f3c06 100644
--- a/src/gpu/ops/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/ops/GrAAHairLinePathRenderer.cpp
@@ -951,7 +951,8 @@
             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
                                                               : LocalCoords::kUnused_Type);
             localCoords.fMatrix = geometryProcessorLocalM;
-            lineGP = GrDefaultGeoProcFactory::Make(color, Coverage::kAttribute_Type, localCoords,
+            lineGP = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+                                                   color, Coverage::kAttribute_Type, localCoords,
                                                    *geometryProcessorViewM);
         }
 
diff --git a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
index 958f0f9..75662d3 100644
--- a/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
+++ b/src/gpu/ops/GrAALinearizingConvexPathRenderer.cpp
@@ -112,7 +112,8 @@
     }
 }
 
-static sk_sp<GrGeometryProcessor> create_lines_only_gp(bool tweakAlphaForCoverage,
+static sk_sp<GrGeometryProcessor> create_lines_only_gp(const GrShaderCaps* shaderCaps,
+                                                       bool tweakAlphaForCoverage,
                                                        const SkMatrix& viewMatrix,
                                                        bool usesLocalCoords) {
     using namespace GrDefaultGeoProcFactory;
@@ -125,7 +126,10 @@
     }
     LocalCoords::Type localCoordsType =
             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
-    return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
+    return MakeForDeviceSpace(shaderCaps,
+                              Color::kPremulGrColorAttribute_Type,
+                              coverageType,
+                              localCoordsType,
                               viewMatrix);
 }
 
@@ -240,7 +244,8 @@
     void onPrepareDraws(Target* target) override {
         auto pipe = fHelper.makePipeline(target);
         // Setup GrGeometryProcessor
-        sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(fHelper.compatibleWithAlphaAsCoverage(),
+        sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(target->caps().shaderCaps(),
+                                                           fHelper.compatibleWithAlphaAsCoverage(),
                                                            this->viewMatrix(),
                                                            fHelper.usesLocalCoords()));
         if (!gp) {
diff --git a/src/gpu/ops/GrAAStrokeRectOp.cpp b/src/gpu/ops/GrAAStrokeRectOp.cpp
index 1b2ab8e..92d1c01 100644
--- a/src/gpu/ops/GrAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrAAStrokeRectOp.cpp
@@ -94,7 +94,8 @@
     }
 }
 
-static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage,
+static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(const GrShaderCaps* shaderCaps,
+                                                        bool tweakAlphaForCoverage,
                                                         const SkMatrix& viewMatrix,
                                                         bool usesLocalCoords) {
     using namespace GrDefaultGeoProcFactory;
@@ -107,7 +108,10 @@
     }
     LocalCoords::Type localCoordsType =
             usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
-    return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
+    return MakeForDeviceSpace(shaderCaps,
+                              Color::kPremulGrColorAttribute_Type,
+                              coverageType,
+                              localCoordsType,
                               viewMatrix);
 }
 
@@ -258,7 +262,8 @@
 }  // anonymous namespace
 
 void AAStrokeRectOp::onPrepareDraws(Target* target) {
-    sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(fHelper.compatibleWithAlphaAsCoverage(),
+    sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(target->caps().shaderCaps(),
+                                                        fHelper.compatibleWithAlphaAsCoverage(),
                                                         this->viewMatrix(),
                                                         fHelper.usesLocalCoords()));
     if (!gp) {
diff --git a/src/gpu/ops/GrDashOp.cpp b/src/gpu/ops/GrDashOp.cpp
index 76203e5..61fad24 100644
--- a/src/gpu/ops/GrDashOp.cpp
+++ b/src/gpu/ops/GrDashOp.cpp
@@ -392,7 +392,10 @@
             Color color(this->color());
             LocalCoords::Type localCoordsType =
                     fUsesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
-            gp = MakeForDeviceSpace(color, Coverage::kSolid_Type, localCoordsType,
+            gp = MakeForDeviceSpace(target->caps().shaderCaps(),
+                                    color,
+                                    Coverage::kSolid_Type,
+                                    localCoordsType,
                                     this->viewMatrix());
         }
 
diff --git a/src/gpu/ops/GrDefaultPathRenderer.cpp b/src/gpu/ops/GrDefaultPathRenderer.cpp
index 9fb1027..07e40f0 100644
--- a/src/gpu/ops/GrDefaultPathRenderer.cpp
+++ b/src/gpu/ops/GrDefaultPathRenderer.cpp
@@ -405,7 +405,11 @@
             Coverage coverage(this->coverage());
             LocalCoords localCoords(fHelper.usesLocalCoords() ? LocalCoords::kUsePosition_Type
                                                               : LocalCoords::kUnused_Type);
-            gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, this->viewMatrix());
+            gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+                                               color,
+                                               coverage,
+                                               localCoords,
+                                               this->viewMatrix());
         }
 
         SkASSERT(gp->debugOnly_vertexStride() == sizeof(SkPoint));
diff --git a/src/gpu/ops/GrDrawAtlasOp.cpp b/src/gpu/ops/GrDrawAtlasOp.cpp
index 0f94d7e..d280ad9 100644
--- a/src/gpu/ops/GrDrawAtlasOp.cpp
+++ b/src/gpu/ops/GrDrawAtlasOp.cpp
@@ -13,7 +13,8 @@
 #include "SkRandom.h"
 #include "SkRectPriv.h"
 
-static sk_sp<GrGeometryProcessor> make_gp(bool hasColors,
+static sk_sp<GrGeometryProcessor> make_gp(const GrShaderCaps* shaderCaps,
+                                          bool hasColors,
                                           GrColor color,
                                           const SkMatrix& viewMatrix) {
     using namespace GrDefaultGeoProcFactory;
@@ -22,7 +23,7 @@
         gpColor.fType = Color::kPremulGrColorAttribute_Type;
     }
 
-    return GrDefaultGeoProcFactory::Make(gpColor, Coverage::kSolid_Type,
+    return GrDefaultGeoProcFactory::Make(shaderCaps, gpColor, Coverage::kSolid_Type,
                                          LocalCoords::kHasExplicit_Type, viewMatrix);
 }
 
@@ -119,7 +120,10 @@
 
 void GrDrawAtlasOp::onPrepareDraws(Target* target) {
     // Setup geometry processor
-    sk_sp<GrGeometryProcessor> gp(make_gp(this->hasColors(), this->color(), this->viewMatrix()));
+    sk_sp<GrGeometryProcessor> gp(make_gp(target->caps().shaderCaps(),
+                                          this->hasColors(),
+                                          this->color(),
+                                          this->viewMatrix()));
 
     int instanceCount = fGeoData.count();
     size_t vertexStride =
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index 83a703f..b5d16ea 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -152,7 +152,8 @@
     return result;
 }
 
-sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
+sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(const GrShaderCaps* shaderCaps,
+                                                    bool* hasColorAttribute,
                                                     bool* hasLocalCoordAttribute,
                                                     bool* hasBoneAttribute) const {
     using namespace GrDefaultGeoProcFactory;
@@ -191,13 +192,15 @@
     *hasBoneAttribute = this->hasBones();
 
     if (this->hasBones()) {
-        return GrDefaultGeoProcFactory::MakeWithBones(color,
+        return GrDefaultGeoProcFactory::MakeWithBones(shaderCaps,
+                                                      color,
                                                       Coverage::kSolid_Type,
                                                       localCoordsType,
                                                       bones,
                                                       vm);
     } else {
-        return GrDefaultGeoProcFactory::Make(color,
+        return GrDefaultGeoProcFactory::Make(shaderCaps,
+                                             color,
                                              Coverage::kSolid_Type,
                                              localCoordsType,
                                              vm);
@@ -205,7 +208,8 @@
 }
 
 void GrDrawVerticesOp::onPrepareDraws(Target* target) {
-    if (fMeshes[0].fVertices->isVolatile()) {
+    bool hasMapBufferSupport = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
+    if (fMeshes[0].fVertices->isVolatile() || !hasMapBufferSupport) {
         this->drawVolatile(target);
     } else {
         this->drawNonVolatile(target);
@@ -216,7 +220,8 @@
     bool hasColorAttribute;
     bool hasLocalCoordsAttribute;
     bool hasBoneAttribute;
-    sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute,
+    sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
+                                                 &hasColorAttribute,
                                                  &hasLocalCoordsAttribute,
                                                  &hasBoneAttribute);
 
@@ -224,7 +229,7 @@
     size_t vertexStride = sizeof(SkPoint) +
                           (hasColorAttribute ? sizeof(uint32_t) : 0) +
                           (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0) +
-                          (hasBoneAttribute ? 4 * (sizeof(uint32_t) + sizeof(float)) : 0);
+                          (hasBoneAttribute ? 4 * (sizeof(int8_t) + sizeof(uint8_t)) : 0);
     SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
     // Allocate buffers.
@@ -265,7 +270,8 @@
     bool hasColorAttribute;
     bool hasLocalCoordsAttribute;
     bool hasBoneAttribute;
-    sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute,
+    sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
+                                                 &hasColorAttribute,
                                                  &hasLocalCoordsAttribute,
                                                  &hasBoneAttribute);
 
@@ -300,7 +306,7 @@
     size_t vertexStride = sizeof(SkPoint) +
                           (hasColorAttribute ? sizeof(uint32_t) : 0) +
                           (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0) +
-                          (hasBoneAttribute ? 4 * (sizeof(uint32_t) + sizeof(float)) : 0);
+                          (hasBoneAttribute ? 4 * (sizeof(int8_t) + sizeof(uint8_t)) : 0);
     SkASSERT(vertexStride == gp->debugOnly_vertexStride());
 
     // Allocate vertex buffer.
@@ -417,7 +423,7 @@
             }
             size_t boneIndexOffset = offset;
             if (hasBoneAttribute) {
-                offset += 4 * sizeof(uint32_t);
+                offset += 4 * sizeof(int8_t);
             }
             size_t boneWeightOffset = offset;
 
@@ -445,10 +451,10 @@
                     const SkVertices::BoneIndices& indices = boneIndices[j];
                     const SkVertices::BoneWeights& weights = boneWeights[j];
                     for (int k = 0; k < 4; k++) {
-                        size_t indexOffset = boneIndexOffset + sizeof(uint32_t) * k;
-                        size_t weightOffset = boneWeightOffset + sizeof(float) * k;
-                        *(uint32_t*)((intptr_t)verts + indexOffset) = indices.indices[k];
-                        *(float*)((intptr_t)verts + weightOffset) = weights.weights[k];
+                        size_t indexOffset = boneIndexOffset + sizeof(int8_t) * k;
+                        size_t weightOffset = boneWeightOffset + sizeof(uint8_t) * k;
+                        *(int8_t*)((intptr_t)verts + indexOffset) = indices.indices[k];
+                        *(uint8_t*)((intptr_t)verts + weightOffset) = weights.weights[k] * 255.0f;
                     }
                 }
                 verts = (void*)((intptr_t)verts + vertexStride);
diff --git a/src/gpu/ops/GrDrawVerticesOp.h b/src/gpu/ops/GrDrawVerticesOp.h
index 9b638b8..7695dd9 100644
--- a/src/gpu/ops/GrDrawVerticesOp.h
+++ b/src/gpu/ops/GrDrawVerticesOp.h
@@ -86,7 +86,8 @@
                       const GrBuffer* indexBuffer,
                       int firstIndex);
 
-    sk_sp<GrGeometryProcessor> makeGP(bool* hasColorAttribute,
+    sk_sp<GrGeometryProcessor> makeGP(const GrShaderCaps* shaderCaps,
+                                      bool* hasColorAttribute,
                                       bool* hasLocalCoordAttribute,
                                       bool* hasBoneAttribute) const;
 
diff --git a/src/gpu/ops/GrNonAAFillRectOp.cpp b/src/gpu/ops/GrNonAAFillRectOp.cpp
index 1b76d4a..c554232 100644
--- a/src/gpu/ops/GrNonAAFillRectOp.cpp
+++ b/src/gpu/ops/GrNonAAFillRectOp.cpp
@@ -29,13 +29,17 @@
 
     The vertex attrib order is always pos, color, [local coords].
  */
-static sk_sp<GrGeometryProcessor> make_gp() {
+static sk_sp<GrGeometryProcessor> make_gp(const GrShaderCaps* shaderCaps) {
     using namespace GrDefaultGeoProcFactory;
-    return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type,
-                                         LocalCoords::kHasExplicit_Type, SkMatrix::I());
+    return GrDefaultGeoProcFactory::Make(shaderCaps,
+                                         Color::kPremulGrColorAttribute_Type,
+                                         Coverage::kSolid_Type,
+                                         LocalCoords::kHasExplicit_Type,
+                                         SkMatrix::I());
 }
 
-static sk_sp<GrGeometryProcessor> make_perspective_gp(const SkMatrix& viewMatrix,
+static sk_sp<GrGeometryProcessor> make_perspective_gp(const GrShaderCaps* shaderCaps,
+                                                      const SkMatrix& viewMatrix,
                                                       bool hasExplicitLocalCoords,
                                                       const SkMatrix* localMatrix) {
     SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
@@ -50,15 +54,16 @@
         LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
                                                        : LocalCoords::kUsePosition_Type,
                                 localMatrix);
-        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+        return GrDefaultGeoProcFactory::Make(shaderCaps, Color::kPremulGrColorAttribute_Type,
                                              Coverage::kSolid_Type, localCoords, viewMatrix);
     } else if (hasExplicitLocalCoords) {
         LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
-        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+        return GrDefaultGeoProcFactory::Make(shaderCaps, Color::kPremulGrColorAttribute_Type,
                                              Coverage::kSolid_Type, localCoords, SkMatrix::I());
     } else {
         LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
-        return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
+        return GrDefaultGeoProcFactory::MakeForDeviceSpace(shaderCaps,
+                                                           Color::kPremulGrColorAttribute_Type,
                                                            Coverage::kSolid_Type, localCoords,
                                                            viewMatrix);
     }
@@ -174,7 +179,7 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp = make_gp();
+        sk_sp<GrGeometryProcessor> gp = make_gp(target->caps().shaderCaps());
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
@@ -304,7 +309,10 @@
 private:
     void onPrepareDraws(Target* target) override {
         sk_sp<GrGeometryProcessor> gp = make_perspective_gp(
-                fViewMatrix, fHasLocalRect, fHasLocalMatrix ? &fLocalMatrix : nullptr);
+                target->caps().shaderCaps(),
+                fViewMatrix,
+                fHasLocalRect,
+                fHasLocalMatrix ? &fLocalMatrix : nullptr);
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
diff --git a/src/gpu/ops/GrNonAAStrokeRectOp.cpp b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
index 357d923..21636b2 100644
--- a/src/gpu/ops/GrNonAAStrokeRectOp.cpp
+++ b/src/gpu/ops/GrNonAAStrokeRectOp.cpp
@@ -147,7 +147,8 @@
             LocalCoords::Type localCoordsType = fHelper.usesLocalCoords()
                                                         ? LocalCoords::kUsePosition_Type
                                                         : LocalCoords::kUnused_Type;
-            gp = GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType,
+            gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(), color,
+                                               Coverage::kSolid_Type, localCoordsType,
                                                fViewMatrix);
         }
 
diff --git a/src/gpu/ops/GrRegionOp.cpp b/src/gpu/ops/GrRegionOp.cpp
index 852cd46..d0c24e6 100644
--- a/src/gpu/ops/GrRegionOp.cpp
+++ b/src/gpu/ops/GrRegionOp.cpp
@@ -19,10 +19,12 @@
 static const int kVertsPerInstance = 4;
 static const int kIndicesPerInstance = 6;
 
-static sk_sp<GrGeometryProcessor> make_gp(const SkMatrix& viewMatrix) {
+static sk_sp<GrGeometryProcessor> make_gp(const GrShaderCaps* shaderCaps,
+                                          const SkMatrix& viewMatrix) {
     using namespace GrDefaultGeoProcFactory;
-    return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type,
-                                         LocalCoords::kUsePosition_Type, viewMatrix);
+    return GrDefaultGeoProcFactory::Make(shaderCaps, Color::kPremulGrColorAttribute_Type,
+                                         Coverage::kSolid_Type, LocalCoords::kUsePosition_Type,
+                                         viewMatrix);
 }
 
 static void tesselate_region(intptr_t vertices,
@@ -109,7 +111,7 @@
 
 private:
     void onPrepareDraws(Target* target) override {
-        sk_sp<GrGeometryProcessor> gp = make_gp(fViewMatrix);
+        sk_sp<GrGeometryProcessor> gp = make_gp(target->caps().shaderCaps(), fViewMatrix);
         if (!gp) {
             SkDebugf("Couldn't create GrGeometryProcessor\n");
             return;
diff --git a/src/gpu/ops/GrTessellatingPathRenderer.cpp b/src/gpu/ops/GrTessellatingPathRenderer.cpp
index 475197e..2fea9cb 100644
--- a/src/gpu/ops/GrTessellatingPathRenderer.cpp
+++ b/src/gpu/ops/GrTessellatingPathRenderer.cpp
@@ -335,10 +335,12 @@
                 coverageType = Coverage::kSolid_Type;
             }
             if (fAntiAlias) {
-                gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType,
+                gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(target->caps().shaderCaps(),
+                                                                 color, coverageType,
                                                                  localCoordsType, fViewMatrix);
             } else {
-                gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType,
+                gp = GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+                                                   color, coverageType, localCoordsType,
                                                    fViewMatrix);
             }
         }
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 8181569..8503bea 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -399,6 +399,9 @@
     shaderCaps->fFloatIs32Bits = true;
     shaderCaps->fHalfIs32Bits = false;
 
+    // SPIR-V supports unsigned integers.
+    shaderCaps->fUnsignedSupport = true;
+
     shaderCaps->fMaxVertexSamplers =
     shaderCaps->fMaxGeometrySamplers =
     shaderCaps->fMaxFragmentSamplers = SkTMin(
diff --git a/src/gpu/vk/GrVkPipeline.cpp b/src/gpu/vk/GrVkPipeline.cpp
index 91f42ad..60e7f43 100644
--- a/src/gpu/vk/GrVkPipeline.cpp
+++ b/src/gpu/vk/GrVkPipeline.cpp
@@ -34,6 +34,22 @@
             return VK_FORMAT_R32G32B32_SINT;
         case kInt4_GrVertexAttribType:
             return VK_FORMAT_R32G32B32A32_SINT;
+        case kByte_GrVertexAttribType:
+            return VK_FORMAT_R8_SINT;
+        case kByte2_GrVertexAttribType:
+            return VK_FORMAT_R8G8_SINT;
+        case kByte3_GrVertexAttribType:
+            return VK_FORMAT_R8G8B8_SINT;
+        case kByte4_GrVertexAttribType:
+            return VK_FORMAT_R8G8B8A8_SINT;
+        case kUByte_GrVertexAttribType:
+            return VK_FORMAT_R8_UINT;
+        case kUByte2_GrVertexAttribType:
+            return VK_FORMAT_R8G8_UINT;
+        case kUByte3_GrVertexAttribType:
+            return VK_FORMAT_R8G8B8_UINT;
+        case kUByte4_GrVertexAttribType:
+            return VK_FORMAT_R8G8B8A8_UINT;
         case kUByte_norm_GrVertexAttribType:
             return VK_FORMAT_R8_UNORM;
         case kUByte4_norm_GrVertexAttribType:
diff --git a/src/gpu/vk/GrVkUniformHandler.cpp b/src/gpu/vk/GrVkUniformHandler.cpp
index d92d8d2..848d52a 100644
--- a/src/gpu/vk/GrVkUniformHandler.cpp
+++ b/src/gpu/vk/GrVkUniformHandler.cpp
@@ -18,6 +18,17 @@
 // https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/html/vkspec.html#interfaces-resources-layout
 uint32_t grsltype_to_alignment_mask(GrSLType type) {
     switch(type) {
+        case kByte_GrSLType: // fall through
+        case kUByte_GrSLType:
+            return 0x0;
+        case kByte2_GrSLType: // fall through
+        case kUByte2_GrSLType:
+            return 0x1;
+        case kByte3_GrSLType: // fall through
+        case kByte4_GrSLType:
+        case kUByte3_GrSLType:
+        case kUByte4_GrSLType:
+            return 0x3;
         case kShort_GrSLType: // fall through
         case kUShort_GrSLType:
             return 0x1;
@@ -80,6 +91,22 @@
 /** Returns the size in bytes taken up in vulkanbuffers for GrSLTypes. */
 static inline uint32_t grsltype_to_vk_size(GrSLType type) {
     switch(type) {
+        case kByte_GrSLType:
+            return sizeof(int8_t);
+        case kByte2_GrSLType:
+            return 2 * sizeof(int8_t);
+        case kByte3_GrSLType:
+            return 3 * sizeof(int8_t);
+        case kByte4_GrSLType:
+            return 4 * sizeof(int8_t);
+        case kUByte_GrSLType:
+            return sizeof(uint8_t);
+        case kUByte2_GrSLType:
+            return 2 * sizeof(uint8_t);
+        case kUByte3_GrSLType:
+            return 3 * sizeof(uint8_t);
+        case kUByte4_GrSLType:
+            return 4 * sizeof(uint8_t);
         case kShort_GrSLType:
             return sizeof(int16_t);
         case kShort2_GrSLType:
diff --git a/src/gpu/vk/GrVkVaryingHandler.cpp b/src/gpu/vk/GrVkVaryingHandler.cpp
index c8c56c2..d210aba 100644
--- a/src/gpu/vk/GrVkVaryingHandler.cpp
+++ b/src/gpu/vk/GrVkVaryingHandler.cpp
@@ -30,14 +30,20 @@
         case kInt2_GrSLType:
         case kShort2_GrSLType:
         case kUShort2_GrSLType:
+        case kByte2_GrSLType:
+        case kUByte2_GrSLType:
             return 1;
         case kInt3_GrSLType:
         case kShort3_GrSLType:
         case kUShort3_GrSLType:
+        case kByte3_GrSLType:
+        case kUByte3_GrSLType:
             return 1;
         case kInt4_GrSLType:
         case kShort4_GrSLType:
         case kUShort4_GrSLType:
+        case kByte4_GrSLType:
+        case kUByte4_GrSLType:
             return 1;
         case kFloat2x2_GrSLType:
         case kHalf2x2_GrSLType:
@@ -60,9 +66,11 @@
              return 1;
         case kInt_GrSLType: // fall through
         case kShort_GrSLType:
+        case kByte_GrSLType:
              return 1;
-        case kUint_GrSLType:
-        case kUShort_GrSLType: // fall through
+        case kUint_GrSLType: // fall through
+        case kUShort_GrSLType:
+        case kUByte_GrSLType:
              return 1;
         case kTexture2D_GrSLType:
              return 0;
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index fe2dc70..be40208 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -177,7 +177,9 @@
     } else if (type.kind() == Type::kEnum_Kind) {
         this->write("%d");
         fFormatArgs.push_back("(int) " + cppCode);
-    } else if (type == *fContext.fInt4_Type || type == *fContext.fShort4_Type) {
+    } else if (type == *fContext.fInt4_Type ||
+               type == *fContext.fShort4_Type ||
+               type == *fContext.fByte4_Type) {
         this->write(type.name() + "(%d, %d, %d, %d)");
         fFormatArgs.push_back(cppCode + ".left()");
         fFormatArgs.push_back(cppCode + ".top()");
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 0cf55c2..feaafeb 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -100,6 +100,14 @@
     ADD_TYPE(UShort2);
     ADD_TYPE(UShort3);
     ADD_TYPE(UShort4);
+    ADD_TYPE(Byte);
+    ADD_TYPE(Byte2);
+    ADD_TYPE(Byte3);
+    ADD_TYPE(Byte4);
+    ADD_TYPE(UByte);
+    ADD_TYPE(UByte2);
+    ADD_TYPE(UByte3);
+    ADD_TYPE(UByte4);
     ADD_TYPE(Bool);
     ADD_TYPE(Bool2);
     ADD_TYPE(Bool3);
@@ -149,6 +157,8 @@
     ADD_TYPE(UVec);
     ADD_TYPE(SVec);
     ADD_TYPE(USVec);
+    ADD_TYPE(ByteVec);
+    ADD_TYPE(UByteVec);
     ADD_TYPE(BVec);
 
     ADD_TYPE(Sampler1D);
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 61dd804..ef2aef0 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -49,6 +49,14 @@
     , fShort2_Type(new Type("short2", *fShort_Type, 2))
     , fShort3_Type(new Type("short3", *fShort_Type, 3))
     , fShort4_Type(new Type("short4", *fShort_Type, 4))
+    , fUByte_Type(new Type("ubyte", Type::kUnsigned_NumberKind, 0))
+    , fUByte2_Type(new Type("ubyte2", *fUByte_Type, 2))
+    , fUByte3_Type(new Type("ubyte3", *fUByte_Type, 3))
+    , fUByte4_Type(new Type("ubyte4", *fUByte_Type, 4))
+    , fByte_Type(new Type("byte", Type::kSigned_NumberKind, 0))
+    , fByte2_Type(new Type("byte2", *fByte_Type, 2))
+    , fByte3_Type(new Type("byte3", *fByte_Type, 3))
+    , fByte4_Type(new Type("byte4", *fByte_Type, 4))
     , fBool_Type(new Type("bool", Type::kNonnumeric_NumberKind, -1))
     , fBool2_Type(new Type("bool2", *fBool_Type, 2))
     , fBool3_Type(new Type("bool3", *fBool_Type, 3))
@@ -180,6 +188,10 @@
                                      fShort3_Type.get(), fShort4_Type.get() }))
     , fUSVec_Type(new Type("$usvec", { fInvalid_Type.get(), fUShort2_Type.get(),
                                        fUShort3_Type.get(), fUShort4_Type.get() }))
+    , fByteVec_Type(new Type("$bytevec", { fInvalid_Type.get(), fByte2_Type.get(),
+                                     fByte3_Type.get(), fByte4_Type.get() }))
+    , fUByteVec_Type(new Type("$ubytevec", { fInvalid_Type.get(), fUByte2_Type.get(),
+                                       fUByte3_Type.get(), fUByte4_Type.get() }))
     , fBVec_Type(new Type("$bvec", { fInvalid_Type.get(), fBool2_Type.get(),
                                      fBool3_Type.get(), fBool4_Type.get() }))
     , fSkCaps_Type(new Type("$sk_Caps"))
@@ -230,6 +242,16 @@
     const std::unique_ptr<Type> fShort3_Type;
     const std::unique_ptr<Type> fShort4_Type;
 
+    const std::unique_ptr<Type> fUByte_Type;
+    const std::unique_ptr<Type> fUByte2_Type;
+    const std::unique_ptr<Type> fUByte3_Type;
+    const std::unique_ptr<Type> fUByte4_Type;
+
+    const std::unique_ptr<Type> fByte_Type;
+    const std::unique_ptr<Type> fByte2_Type;
+    const std::unique_ptr<Type> fByte3_Type;
+    const std::unique_ptr<Type> fByte4_Type;
+
     const std::unique_ptr<Type> fBool_Type;
     const std::unique_ptr<Type> fBool2_Type;
     const std::unique_ptr<Type> fBool3_Type;
@@ -328,6 +350,8 @@
     const std::unique_ptr<Type> fUVec_Type;
     const std::unique_ptr<Type> fSVec_Type;
     const std::unique_ptr<Type> fUSVec_Type;
+    const std::unique_ptr<Type> fByteVec_Type;
+    const std::unique_ptr<Type> fUByteVec_Type;
 
     const std::unique_ptr<Type> fBVec_Type;
 
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index aa4f825..5f6fedc 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -82,10 +82,14 @@
             else if (component == *fContext.fDouble_Type) {
                 result = "dvec";
             }
-            else if (component == *fContext.fInt_Type || component == *fContext.fShort_Type) {
+            else if (component == *fContext.fInt_Type ||
+                     component == *fContext.fShort_Type ||
+                     component == *fContext.fByte_Type) {
                 result = "ivec";
             }
-            else if (component == *fContext.fUInt_Type || component == *fContext.fUShort_Type) {
+            else if (component == *fContext.fUInt_Type ||
+                     component == *fContext.fUShort_Type ||
+                     component == *fContext.fUByte_Type) {
                 result = "uvec";
             }
             else if (component == *fContext.fBool_Type) {
@@ -134,6 +138,12 @@
             else if (type == *fContext.fUShort_Type) {
                 return "uint";
             }
+            else if (type == *fContext.fByte_Type) {
+                return "int";
+            }
+            else if (type == *fContext.fUByte_Type) {
+                return "uint";
+            }
             else {
                 return type.name();
             }
@@ -857,7 +867,9 @@
         this->write(to_string(i.fValue & 0xffffffff) + "u");
     } else if (i.fType == *fContext.fUShort_Type) {
         this->write(to_string(i.fValue & 0xffff) + "u");
-     } else {
+    } else if (i.fType == *fContext.fUByte_Type) {
+        this->write(to_string(i.fValue & 0xff) + "u");
+    } else {
         this->write(to_string((int32_t) i.fValue));
     }
 }
@@ -1018,7 +1030,8 @@
     if (usesPrecisionModifiers()) {
         switch (type.kind()) {
             case Type::kScalar_Kind:
-                if (type == *fContext.fShort_Type || type == *fContext.fUShort_Type) {
+                if (type == *fContext.fShort_Type || type == *fContext.fUShort_Type ||
+                    type == *fContext.fByte_Type || type == *fContext.fUByte_Type) {
                     if (fProgram.fSettings.fForceHighPrecision ||
                             fProgram.fSettings.fCaps->incompleteShortIntPrecision()) {
                         return "highp ";
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index d6a92d3..be0f1ad 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -33,7 +33,9 @@
         return "float";
     } else if (type == *context.fFloat2_Type || type == *context.fHalf2_Type) {
         return "SkPoint";
-    } else if (type == *context.fInt4_Type || type == *context.fShort4_Type) {
+    } else if (type == *context.fInt4_Type ||
+               type == *context.fShort4_Type ||
+               type == *context.fByte4_Type) {
         return "SkIRect";
     } else if (type == *context.fFloat4_Type || type == *context.fHalf4_Type) {
         return "SkRect";
diff --git a/src/sksl/SkSLJIT.cpp b/src/sksl/SkSLJIT.cpp
index 57286b5..115a6be 100644
--- a/src/sksl/SkSLJIT.cpp
+++ b/src/sksl/SkSLJIT.cpp
@@ -190,13 +190,13 @@
             if (type.name() == "float4" || type.name() == "half4") {
                 return fFloat32Vector4Type;
             }
-            if (type.name() == "int2" || type.name() == "short2") {
+            if (type.name() == "int2" || type.name() == "short2" || type.name == "byte2") {
                 return fInt32Vector2Type;
             }
-            if (type.name() == "int3" || type.name() == "short3") {
+            if (type.name() == "int3" || type.name() == "short3" || type.name == "byte3") {
                 return fInt32Vector3Type;
             }
-            if (type.name() == "int4" || type.name() == "short4") {
+            if (type.name() == "int4" || type.name() == "short4" || type.name == "byte3") {
                 return fInt32Vector4Type;
             }
             // fall through
@@ -402,9 +402,9 @@
     if (type.kind() == Type::kVector_Kind) {
         return this->typeKind(type.componentType());
     }
-    if (type.fName == "int" || type.fName == "short") {
+    if (type.fName == "int" || type.fName == "short" || type.fName == "byte") {
         return JIT::kInt_TypeKind;
-    } else if (type.fName == "uint" || type.fName == "ushort") {
+    } else if (type.fName == "uint" || type.fName == "ushort" || type.fName == "ubyte") {
         return JIT::kUInt_TypeKind;
     } else if (type.fName == "float" || type.fName == "double") {
         return JIT::kFloat_TypeKind;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 84a15f1..3357faa 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -154,14 +154,16 @@
     if (type.kind() == Type::kVector_Kind) {
         return is_signed(context, type.componentType());
     }
-    return type == *context.fInt_Type || type == *context.fShort_Type;
+    return type == *context.fInt_Type || type == *context.fShort_Type ||
+           type == *context.fByte_Type;
 }
 
 static bool is_unsigned(const Context& context, const Type& type) {
     if (type.kind() == Type::kVector_Kind) {
         return is_unsigned(context, type.componentType());
     }
-    return type == *context.fUInt_Type || type == *context.fUShort_Type;
+    return type == *context.fUInt_Type || type == *context.fUShort_Type ||
+           type == *context.fUByte_Type;
 }
 
 static bool is_bool(const Context& context, const Type& type) {
@@ -433,20 +435,22 @@
     if (type == *fContext.fHalf_Type) {
         return *fContext.fFloat_Type;
     }
-    if (type == *fContext.fShort_Type) {
+    if (type == *fContext.fShort_Type || type == *fContext.fByte_Type) {
         return *fContext.fInt_Type;
     }
-    if (type == *fContext.fUShort_Type) {
+    if (type == *fContext.fUShort_Type || type == *fContext.fUByte_Type) {
         return *fContext.fUInt_Type;
     }
     if (type.kind() == Type::kMatrix_Kind || type.kind() == Type::kVector_Kind) {
         if (type.componentType() == *fContext.fHalf_Type) {
             return fContext.fFloat_Type->toCompound(fContext, type.columns(), type.rows());
         }
-        if (type.componentType() == *fContext.fShort_Type) {
+        if (type.componentType() == *fContext.fShort_Type ||
+            type.componentType() == *fContext.fByte_Type) {
             return fContext.fInt_Type->toCompound(fContext, type.columns(), type.rows());
         }
-        if (type.componentType() == *fContext.fUShort_Type) {
+        if (type.componentType() == *fContext.fUShort_Type ||
+            type.componentType() == *fContext.fUByte_Type) {
             return fContext.fUInt_Type->toCompound(fContext, type.columns(), type.rows());
         }
     }
@@ -1298,31 +1302,47 @@
                     if (c.fArguments.size() == 1) {
                         return vec;
                     }
-                } else if (src == *fContext.fInt_Type || src == *fContext.fShort_Type) {
+                } else if (src == *fContext.fInt_Type ||
+                           src == *fContext.fShort_Type ||
+                           src == *fContext.fByte_Type) {
                     op = SpvOpConvertSToF;
-                } else if (src == *fContext.fUInt_Type || src == *fContext.fUShort_Type) {
+                } else if (src == *fContext.fUInt_Type ||
+                           src == *fContext.fUShort_Type ||
+                           src == *fContext.fUByte_Type) {
                     op = SpvOpConvertUToF;
                 } else {
                     SkASSERT(false);
                 }
-            } else if (dst == *fContext.fInt_Type || dst == *fContext.fShort_Type) {
+            } else if (dst == *fContext.fInt_Type ||
+                       dst == *fContext.fShort_Type ||
+                       dst == *fContext.fByte_Type) {
                 if (src == *fContext.fFloat_Type || src == *fContext.fHalf_Type) {
                     op = SpvOpConvertFToS;
-                } else if (src == *fContext.fInt_Type || src == *fContext.fShort_Type) {
+                } else if (src == *fContext.fInt_Type ||
+                           src == *fContext.fShort_Type ||
+                           src == *fContext.fByte_Type) {
                     if (c.fArguments.size() == 1) {
                         return vec;
                     }
-                } else if (src == *fContext.fUInt_Type || src == *fContext.fUShort_Type) {
+                } else if (src == *fContext.fUInt_Type ||
+                           src == *fContext.fUShort_Type ||
+                           src == *fContext.fUByte_Type) {
                     op = SpvOpBitcast;
                 } else {
                     SkASSERT(false);
                 }
-            } else if (dst == *fContext.fUInt_Type || dst == *fContext.fUShort_Type) {
+            } else if (dst == *fContext.fUInt_Type ||
+                       dst == *fContext.fUShort_Type ||
+                       dst == *fContext.fUByte_Type) {
                 if (src == *fContext.fFloat_Type || src == *fContext.fHalf_Type) {
                     op = SpvOpConvertFToS;
-                } else if (src == *fContext.fInt_Type || src == *fContext.fShort_Type) {
+                } else if (src == *fContext.fInt_Type ||
+                           src == *fContext.fShort_Type ||
+                           src == *fContext.fByte_Type) {
                     op = SpvOpBitcast;
-                } else if (src == *fContext.fUInt_Type || src == *fContext.fUShort_Type) {
+                } else if (src == *fContext.fUInt_Type ||
+                           src == *fContext.fUShort_Type ||
+                           src == *fContext.fUByte_Type) {
                     if (c.fArguments.size() == 1) {
                         return vec;
                     }
@@ -1391,9 +1411,13 @@
     }
     if (c.fType == *fContext.fFloat_Type || c.fType == *fContext.fHalf_Type) {
         return this->writeFloatConstructor(c, out);
-    } else if (c.fType == *fContext.fInt_Type || c.fType == *fContext.fShort_Type) {
+    } else if (c.fType == *fContext.fInt_Type ||
+               c.fType == *fContext.fShort_Type ||
+               c.fType == *fContext.fByte_Type) {
         return this->writeIntConstructor(c, out);
-    } else if (c.fType == *fContext.fUInt_Type || c.fType == *fContext.fUShort_Type) {
+    } else if (c.fType == *fContext.fUInt_Type ||
+               c.fType == *fContext.fUShort_Type ||
+               c.fType == *fContext.fUByte_Type) {
         return this->writeUIntConstructor(c, out);
     }
     switch (c.fType.kind()) {
diff --git a/src/sksl/ir/SkSLSwizzle.h b/src/sksl/ir/SkSLSwizzle.h
index b90b78d..e713a32 100644
--- a/src/sksl/ir/SkSLSwizzle.h
+++ b/src/sksl/ir/SkSLSwizzle.h
@@ -56,6 +56,12 @@
             case 3: return *context.fShort3_Type;
             case 4: return *context.fShort4_Type;
         }
+    } else if (base == *context.fByte_Type) {
+        switch (count) {
+            case 2: return *context.fByte2_Type;
+            case 3: return *context.fByte3_Type;
+            case 4: return *context.fByte4_Type;
+        }
     } else if (base == *context.fUInt_Type) {
         switch (count) {
             case 2: return *context.fUInt2_Type;
@@ -68,6 +74,12 @@
             case 3: return *context.fUShort3_Type;
             case 4: return *context.fUShort4_Type;
         }
+    } else if (base == *context.fUByte_Type) {
+        switch (count) {
+            case 2: return *context.fUByte2_Type;
+            case 3: return *context.fUByte3_Type;
+            case 4: return *context.fUByte4_Type;
+        }
     } else if (base == *context.fBool_Type) {
         switch (count) {
             case 2: return *context.fBool2_Type;
diff --git a/src/sksl/ir/SkSLType.cpp b/src/sksl/ir/SkSLType.cpp
index d569818..eb3a64f 100644
--- a/src/sksl/ir/SkSLType.cpp
+++ b/src/sksl/ir/SkSLType.cpp
@@ -169,6 +169,17 @@
                 }
             default: ABORT("unsupported row count (%d)", rows);
         }
+    } else if (*this == *context.fByte_Type) {
+        switch (rows) {
+            case 1:
+                switch (columns) {
+                    case 2: return *context.fByte2_Type;
+                    case 3: return *context.fByte3_Type;
+                    case 4: return *context.fByte4_Type;
+                    default: ABORT("unsupported vector column count (%d)", columns);
+                }
+            default: ABORT("unsupported row count (%d)", rows);
+        }
     } else if (*this == *context.fUInt_Type) {
         switch (rows) {
             case 1:
@@ -191,6 +202,17 @@
                 }
             default: ABORT("unsupported row count (%d)", rows);
         }
+    } else if (*this == *context.fUByte_Type) {
+        switch (rows) {
+            case 1:
+                switch (columns) {
+                    case 2: return *context.fUByte2_Type;
+                    case 3: return *context.fUByte3_Type;
+                    case 4: return *context.fUByte4_Type;
+                    default: ABORT("unsupported vector column count (%d)", columns);
+                }
+            default: ABORT("unsupported row count (%d)", rows);
+        }
     } else if (*this == *context.fBool_Type) {
         switch (rows) {
             case 1:
diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp
index ea871fb..efbc43a 100644
--- a/tests/OnFlushCallbackTest.cpp
+++ b/tests/OnFlushCallbackTest.cpp
@@ -99,7 +99,8 @@
         static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
 
         sk_sp<GrGeometryProcessor> gp =
-                GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
+                GrDefaultGeoProcFactory::Make(target->caps().shaderCaps(),
+                                              Color::kPremulGrColorAttribute_Type,
                                               Coverage::kSolid_Type,
                                               fHasLocalRect ? LocalCoords::kHasExplicit_Type
                                                             : LocalCoords::kUnused_Type,