added skeletal animation support to GPU backend

Pulled from reverted CL: https://skia-review.googlesource.com/c/skia/+/139282

Bug: skia:
Change-Id: Iad8e834aa5bbd085b2dee679cb343c5736c57907
Reviewed-on: https://skia-review.googlesource.com/140383
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ruiqi Mao <ruiqimao@google.com>
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index e2bf87b..b1c423a 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -27,8 +27,12 @@
     kColorAttributeIsSkColor_GPFlag = 0x2,
     kLocalCoordAttribute_GPFlag     = 0x4,
     kCoverageAttribute_GPFlag       = 0x8,
+    kBonesAttribute_GPFlag          = 0x10,
 };
 
+static constexpr int kMaxBones = 100; // Due to GPU memory limitations, only up to 100 bone
+                                      // matrices are accepted.
+
 class DefaultGeoProc : public GrGeometryProcessor {
 public:
     static sk_sp<GrGeometryProcessor> Make(uint32_t gpTypeFlags,
@@ -37,10 +41,12 @@
                                            const SkMatrix& viewMatrix,
                                            const SkMatrix& localMatrix,
                                            bool localCoordsWillBeRead,
-                                           uint8_t coverage) {
+                                           uint8_t coverage,
+                                           const float* bones,
+                                           int boneCount) {
         return sk_sp<GrGeometryProcessor>(new DefaultGeoProc(
                 gpTypeFlags, color, std::move(colorSpaceXform), viewMatrix, localMatrix, coverage,
-                localCoordsWillBeRead));
+                localCoordsWillBeRead, bones, boneCount));
     }
 
     const char* name() const override { return "DefaultGeometryProcessor"; }
@@ -52,6 +58,9 @@
     bool localCoordsWillBeRead() const { return fLocalCoordsWillBeRead; }
     uint8_t coverage() const { return fCoverage; }
     bool hasVertexCoverage() const { return fInCoverage.isInitialized(); }
+    const float* bones() const { return fBones; }
+    int boneCount() const { return fBoneCount; }
+    bool hasBones() const { return SkToBool(fBones); }
 
     class GLSLProcessor : public GrGLSLGeometryProcessor {
     public:
@@ -99,11 +108,37 @@
                                         &fColorUniform);
             }
 
+            // Setup bone transforms
+            const char* transformedPositionName = gp.fInPosition.name();
+            if (gp.hasBones()) {
+                const char* vertBonesUniformName;
+                fBonesUniform = uniformHandler->addUniformArray(kVertex_GrShaderFlag,
+                                                                kFloat3x3_GrSLType,
+                                                                "Bones",
+                                                                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;",
+                        vertBonesUniformName,
+                        gp.fInPosition.name(),
+                        gp.fInBoneIndices.name(),
+                        gp.fInBoneWeights.name(),
+                        vertBonesUniformName);
+                transformedPositionName = "transformedPosition";
+            }
+
             // Setup position
             this->writeOutputPosition(vertBuilder,
                                       uniformHandler,
                                       gpArgs,
-                                      gp.fInPosition.name(),
+                                      transformedPositionName,
                                       gp.viewMatrix(),
                                       &fViewMatrixUniform);
 
@@ -147,8 +182,8 @@
                                   GrProcessorKeyBuilder* b) {
             const DefaultGeoProc& def = gp.cast<DefaultGeoProc>();
             uint32_t key = def.fFlags;
-            key |= (def.coverage() == 0xff) ? 0x10 : 0;
-            key |= (def.localCoordsWillBeRead() && def.localMatrix().hasPerspective()) ? 0x20 : 0x0;
+            key |= (def.coverage() == 0xff) ? 0x20 : 0;
+            key |= (def.localCoordsWillBeRead() && def.localMatrix().hasPerspective()) ? 0x40 : 0x0;
             key |= ComputePosKey(def.viewMatrix()) << 20;
             b->add32(key);
             b->add32(GrColorSpaceXform::XformKey(def.fColorSpaceXform.get()));
@@ -180,6 +215,10 @@
             this->setTransformDataHelper(dgp.fLocalMatrix, pdman, &transformIter);
 
             fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
+
+            if (dgp.hasBones()) {
+                pdman.setMatrix3fv(fBonesUniform, dgp.boneCount(), dgp.bones());
+            }
         }
 
     private:
@@ -189,6 +228,7 @@
         UniformHandle fViewMatrixUniform;
         UniformHandle fColorUniform;
         UniformHandle fCoverageUniform;
+        UniformHandle fBonesUniform;
         GrGLSLColorSpaceXformHelper fColorSpaceHelper;
 
         typedef GrGLSLGeometryProcessor INHERITED;
@@ -209,7 +249,9 @@
                    const SkMatrix& viewMatrix,
                    const SkMatrix& localMatrix,
                    uint8_t coverage,
-                   bool localCoordsWillBeRead)
+                   bool localCoordsWillBeRead,
+                   const float* bones,
+                   int boneCount)
             : INHERITED(kDefaultGeoProc_ClassID)
             , fColor(color)
             , fViewMatrix(viewMatrix)
@@ -217,7 +259,9 @@
             , fCoverage(coverage)
             , fFlags(gpTypeFlags)
             , fLocalCoordsWillBeRead(localCoordsWillBeRead)
-            , fColorSpaceXform(std::move(colorSpaceXform)) {
+            , fColorSpaceXform(std::move(colorSpaceXform))
+            , fBones(bones)
+            , fBoneCount(boneCount) {
         fInPosition = {"inPosition", kFloat2_GrVertexAttribType};
         int cnt = 1;
         if (fFlags & kColorAttribute_GPFlag) {
@@ -232,17 +276,32 @@
             fInCoverage = {"inCoverage", kHalf_GrVertexAttribType};
             ++cnt;
         }
+        if (fFlags & kBonesAttribute_GPFlag) {
+            SkASSERT(bones && (boneCount > 0));
+            fInBoneIndices = {"inBoneIndices", kInt4_GrVertexAttribType};
+            ++cnt;
+            fInBoneWeights = {"inBoneWeights", kFloat4_GrVertexAttribType};
+            ++cnt;
+        }
         this->setVertexAttributeCnt(cnt);
     }
 
     const Attribute& onVertexAttribute(int i) const override {
-        return IthInitializedAttribute(i, fInPosition, fInColor, fInLocalCoords, fInCoverage);
+        return IthInitializedAttribute(i,
+                                       fInPosition,
+                                       fInColor,
+                                       fInLocalCoords,
+                                       fInCoverage,
+                                       fInBoneIndices,
+                                       fInBoneWeights);
     }
 
     Attribute fInPosition;
     Attribute fInColor;
     Attribute fInLocalCoords;
     Attribute fInCoverage;
+    Attribute fInBoneIndices;
+    Attribute fInBoneWeights;
     GrColor fColor;
     SkMatrix fViewMatrix;
     SkMatrix fLocalMatrix;
@@ -250,6 +309,8 @@
     uint32_t fFlags;
     bool fLocalCoordsWillBeRead;
     sk_sp<GrColorSpaceXform> fColorSpaceXform;
+    const float* fBones;
+    int fBoneCount;
 
     GR_DECLARE_GEOMETRY_PROCESSOR_TEST
 
@@ -259,6 +320,15 @@
 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
 
 #if GR_TEST_UTILS
+static constexpr int kNumFloatsPerSkMatrix = 9;
+static constexpr int kTestBoneCount = 4;
+static constexpr float kTestBones[kTestBoneCount * kNumFloatsPerSkMatrix] = {
+    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+    1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+};
+
 sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
     uint32_t flags = 0;
     if (d->fRandom->nextBool()) {
@@ -273,6 +343,9 @@
     if (d->fRandom->nextBool()) {
         flags |= kLocalCoordAttribute_GPFlag;
     }
+    if (d->fRandom->nextBool()) {
+        flags |= kBonesAttribute_GPFlag;
+    }
 
     return DefaultGeoProc::Make(flags,
                                 GrRandomColor(d->fRandom),
@@ -280,7 +353,9 @@
                                 GrTest::TestMatrix(d->fRandom),
                                 GrTest::TestMatrix(d->fRandom),
                                 d->fRandom->nextBool(),
-                                GrRandomCoverage(d->fRandom));
+                                GrRandomCoverage(d->fRandom),
+                                kTestBones,
+                                kTestBoneCount);
 }
 #endif
 
@@ -307,7 +382,9 @@
                                 viewMatrix,
                                 localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
                                 localCoordsWillBeRead,
-                                inCoverage);
+                                inCoverage,
+                                nullptr,
+                                0);
 }
 
 sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
@@ -330,3 +407,33 @@
     LocalCoords inverted(LocalCoords::kUsePosition_Type, &invert);
     return Make(color, coverage, inverted, SkMatrix::I());
 }
+
+sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeWithBones(const Color& color,
+                                                                  const Coverage& coverage,
+                                                                  const LocalCoords& localCoords,
+                                                                  const Bones& bones,
+                                                                  const SkMatrix& viewMatrix) {
+    uint32_t flags = 0;
+    if (Color::kPremulGrColorAttribute_Type == color.fType) {
+        flags |= kColorAttribute_GPFlag;
+    } else if (Color::kUnpremulSkColorAttribute_Type == color.fType) {
+        flags |= kColorAttribute_GPFlag | kColorAttributeIsSkColor_GPFlag;
+    }
+    flags |= coverage.fType == Coverage::kAttribute_Type ? kCoverageAttribute_GPFlag : 0;
+    flags |= localCoords.fType == LocalCoords::kHasExplicit_Type ? kLocalCoordAttribute_GPFlag : 0;
+    flags |= kBonesAttribute_GPFlag;
+
+    uint8_t inCoverage = coverage.fCoverage;
+    bool localCoordsWillBeRead = localCoords.fType != LocalCoords::kUnused_Type;
+
+    GrColor inColor = color.fColor;
+    return DefaultGeoProc::Make(flags,
+                                inColor,
+                                color.fColorSpaceXform,
+                                viewMatrix,
+                                localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
+                                localCoordsWillBeRead,
+                                inCoverage,
+                                bones.fBones,
+                                bones.fBoneCount);
+}