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;
};