added optimizations to speed up skinning

Docs-Preview: https://skia.org/?cl=145148
Bug: skia:
Change-Id: If27722105a1e8999f6440b6fd4044cc1f327827e
Reviewed-on: https://skia-review.googlesource.com/145148
Commit-Queue: Ruiqi Mao <ruiqimao@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/tools/viewer/NIMASlide.cpp b/tools/viewer/NIMASlide.cpp
index e8827cc..4d094c4 100644
--- a/tools/viewer/NIMASlide.cpp
+++ b/tools/viewer/NIMASlide.cpp
@@ -18,20 +18,6 @@
 using namespace sk_app;
 using namespace nima;
 
-// NIMA stores its matrices as 6 floats to represent translation and scale. This function takes
-// that format and converts it into a 3x3 matrix representation.
-static void nima_to_skmatrix(const float* nimaData, SkMatrix& matrix) {
-    matrix[0] = nimaData[0];
-    matrix[1] = nimaData[2];
-    matrix[2] = nimaData[4];
-    matrix[3] = nimaData[1];
-    matrix[4] = nimaData[3];
-    matrix[5] = nimaData[5];
-    matrix[6] = 0.0f;
-    matrix[7] = 0.0f;
-    matrix[8] = 1.0f;
-}
-
 // ImGui expects an array of const char* when displaying a ListBox. This function is for an
 // overload of ImGui::ListBox that takes a getter so that ListBox works with
 // std::vector<std::string>.
@@ -58,11 +44,8 @@
             , fVertices(nullptr)
             , fRenderFlags(0) {
         // Update the vertices and bones.
-        this->updateVertices();
+        this->updateVertices(true);
         this->updateBones();
-
-        // Update the vertices object.
-        this->updateVerticesObject(false, false);
     }
 
     void render(SkCanvas* canvas, uint32_t renderFlags) {
@@ -73,47 +56,39 @@
         bool useCache = renderFlags & kCache_RenderFlag;
         bool drawBounds = renderFlags & kBounds_RenderFlag;
 
+        // Don't use the cache if drawing in immediate mode.
+        useCache &= !useImmediate;
+
+        if (fActorImage->doesAnimationVertexDeform() || dirty) {
+            // These are vertices that transform beyond just bone transforms, so they must be
+            // updated every frame.
+            // If the render flags are dirty, reset the vertices object.
+            this->updateVertices(!useCache);
+        }
+
+        // Update the bones.
+        this->updateBones();
+
+        // Deform the bones in immediate mode.
+        sk_sp<SkVertices> vertices = fVertices;
         if (useImmediate) {
-            // Immediate mode transforms.
-            // Update the vertex data.
-            if (fActorImage->doesAnimationVertexDeform() && fActorImage->isVertexDeformDirty()) {
-                this->updateVertices();
-                fActorImage->isVertexDeformDirty(false);
-            }
-
-            // Update the vertices object.
-            this->updateVerticesObject(true, true); // Immediate mode vertices change every frame,
-                                                    // so they must be volatile.
-        } else {
-            // Backend transformations.
-            if (fActorImage->doesAnimationVertexDeform()) {
-                // These are vertices that transform beyond just bone transforms, so they must be
-                // updated every frame.
-                this->updateVertices();
-                this->updateVerticesObject(false, true);
-            } else if (dirty) {
-                // If the render flags are dirty, reset the vertices object.
-                this->updateVertices();
-                this->updateVerticesObject(false, !useCache);
-            }
-
-            // Update the bones.
-            this->updateBones();
+            vertices = fVertices->applyBones(fBones.data(), fBones.size());
         }
 
         // Draw the vertices object.
-        this->drawVerticesObject(canvas, !useImmediate);
+        this->drawVerticesObject(vertices.get(), canvas, !useImmediate);
 
+        // Draw the bounds.
         if (drawBounds && fActorImage->renderOpacity() > 0.0f) {
             // Get the bounds.
-            SkRect bounds = fVertices->bounds();
+            SkRect bounds = vertices->bounds();
 
             // Approximate bounds if not using immediate transforms.
             if (!useImmediate) {
-                const SkRect originalBounds = fBones[0].mapRect(fVertices->bounds());
+                const SkRect originalBounds = fBones[0].mapRect(vertices->bounds());
                 bounds = originalBounds;
                 for (size_t i = 1; i < fBones.size(); i++) {
-                    const SkMatrix& matrix = fBones[i];
+                    const SkVertices::Bone& matrix = fBones[i];
                     bounds.join(matrix.mapRect(originalBounds));
                 }
             }
@@ -129,7 +104,7 @@
     int drawOrder() const { return fActorImage->drawOrder(); }
 
 private:
-    void updateVertices() {
+    void updateVertices(bool isVolatile) {
         // Update whether the image is skinned.
         fSkinned = fActorImage->connectedBoneCount() > 0;
 
@@ -177,12 +152,24 @@
             fTexs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height());
             if (fSkinned) {
                 for (uint32_t k = 0; k < 4; k ++) {
-                    fBoneIdx[i].indices[k] = static_cast<uint32_t>(attrBoneIdx[k]);
-                    fBoneWgt[i].weights[k] = attrBoneWgt[k];
+                    fBoneIdx[i][k] = static_cast<uint32_t>(attrBoneIdx[k]);
+                    fBoneWgt[i][k] = attrBoneWgt[k];
                 }
             }
         }
         memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t));
+
+        // Update the vertices object.
+        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                         vertexCount,
+                                         fPositions.data(),
+                                         fTexs.data(),
+                                         nullptr,
+                                         fBoneIdx.data(),
+                                         fBoneWgt.data(),
+                                         fIndices.size(),
+                                         fIndices.data(),
+                                         isVolatile);
     }
 
     void updateBones() {
@@ -195,59 +182,24 @@
             if (fSkinned) {
                 numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize;
             }
-            fBones.assign(numMatrices, SkMatrix());
+
+            // Initialize all matrices to the identity matrix.
+            fBones.assign(numMatrices, {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }});
         }
 
         if (fSkinned) {
             // Update the matrices.
             float* matrixData = fActorImage->boneInfluenceMatrices();
-            for (uint32_t i = 1; i < fBones.size(); i ++) {
-                SkMatrix& matrix = fBones[i];
-                float* data = matrixData + i * kNIMAMatrixSize;
-                nima_to_skmatrix(data, matrix);
-            }
+            memcpy(fBones.data(), matrixData, fBones.size() * kNIMAMatrixSize * sizeof(float));
         }
 
         // Set the zero matrix to be the world transform.
-        nima_to_skmatrix(fActorImage->worldTransform().values(), fBones[0]);
+        memcpy(fBones.data(),
+               fActorImage->worldTransform().values(),
+               kNIMAMatrixSize * sizeof(float));
     }
 
-    void updateVerticesObject(bool applyDeforms, bool isVolatile) {
-        std::vector<SkPoint>* positions = &fPositions;
-
-        // Apply deforms if requested.
-        uint32_t vertexCount = fPositions.size();
-        std::vector<SkPoint> deformedPositions;
-        if (applyDeforms) {
-            positions = &deformedPositions;
-            deformedPositions.reserve(vertexCount);
-            for (uint32_t i = 0; i < vertexCount; i ++) {
-                Vec2D nimaPoint(fPositions[i].x(), fPositions[i].y());
-                uint32_t* boneIdx = nullptr;
-                float* boneWgt = nullptr;
-                if (fSkinned) {
-                    boneIdx = fBoneIdx[i].indices;
-                    boneWgt = fBoneWgt[i].weights;
-                }
-                nimaPoint = this->deform(nimaPoint, boneIdx, boneWgt);
-                deformedPositions.push_back(SkPoint::Make(nimaPoint[0], nimaPoint[1]));
-            }
-        }
-
-        // Update the vertices object.
-        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
-                                         vertexCount,
-                                         positions->data(),
-                                         fTexs.data(),
-                                         nullptr,
-                                         fBoneIdx.data(),
-                                         fBoneWgt.data(),
-                                         fIndices.size(),
-                                         fIndices.data(),
-                                         isVolatile);
-    }
-
-    void drawVerticesObject(SkCanvas* canvas, bool useBones) const {
+    void drawVerticesObject(SkVertices* vertices, SkCanvas* canvas, bool useBones) const {
         // Determine the blend mode.
         SkBlendMode blendMode;
         switch (fActorImage->blendMode()) {
@@ -278,48 +230,15 @@
 
         // Draw the vertices.
         if (useBones) {
-            canvas->drawVertices(fVertices, fBones.data(), fBones.size(), blendMode, *fPaint);
+            canvas->drawVertices(vertices, fBones.data(), fBones.size(), blendMode, *fPaint);
         } else {
-            canvas->drawVertices(fVertices, blendMode, *fPaint);
+            canvas->drawVertices(vertices, blendMode, *fPaint);
         }
 
         // Reset the opacity.
         fPaint->setAlpha(255);
     }
 
-    Vec2D deform(const Vec2D& position, uint32_t* boneIdx, float* boneWgt) const {
-        float px = position[0], py = position[1];
-        float px2 = px, py2 = py;
-        float influence[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
-
-        // Apply the world transform.
-        Mat2D worldTransform = fActorImage->worldTransform();
-        px2 = worldTransform[0] * px + worldTransform[2] * py + worldTransform[4];
-        py2 = worldTransform[1] * px + worldTransform[3] * py + worldTransform[5];
-
-        // Apply deformations based on bone offsets.
-        if (boneIdx && boneWgt) {
-            float* matrices = fActorImage->boneInfluenceMatrices();
-
-            for (uint32_t i = 0; i < 4; i ++) {
-                uint32_t index = boneIdx[i];
-                float weight = boneWgt[i];
-                for (int j = 0; j < 6; j ++) {
-                    influence[j] += matrices[index * 6 + j] * weight;
-                }
-            }
-
-            px = influence[0] * px2 + influence[2] * py2 + influence[4];
-            py = influence[1] * px2 + influence[3] * py2 + influence[5];
-        } else {
-            px = px2;
-            py = py2;
-        }
-
-        // Return the transformed position.
-        return Vec2D(px, py);
-    }
-
 private:
     ActorImage* fActorImage;
     SkImage*    fTexture;
@@ -332,8 +251,8 @@
     std::vector<SkVertices::BoneWeights> fBoneWgt;
     std::vector<uint16_t>                fIndices;
 
-    std::vector<SkMatrix> fBones;
-    sk_sp<SkVertices>     fVertices;
+    std::vector<SkVertices::Bone> fBones;
+    sk_sp<SkVertices>             fVertices;
 
     uint32_t fRenderFlags;
 };