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/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp
index 7b11980..52fed02 100644
--- a/src/core/SkBitmapDevice.cpp
+++ b/src/core/SkBitmapDevice.cpp
@@ -593,8 +593,8 @@
#endif
}
-void SkBitmapDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
- SkBlendMode bmode, const SkPaint& paint) {
+void SkBitmapDevice::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode bmode, const SkPaint& paint) {
BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
vertices->texCoords(), vertices->colors(), vertices->boneIndices(),
vertices->boneWeights(), bmode, vertices->indices(),
diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h
index a578d2c..65971ed 100644
--- a/src/core/SkBitmapDevice.h
+++ b/src/core/SkBitmapDevice.h
@@ -102,7 +102,7 @@
const SkPaint&, SkCanvas::SrcRectConstraint) override;
void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
- void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint& paint) override;
void drawDevice(SkBaseDevice*, int x, int y, const SkPaint&) override;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 1da6e28..fb9e499 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1678,16 +1678,16 @@
this->onDrawVerticesObject(vertices, nullptr, 0, mode, paint);
}
-void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkMatrix* bones, int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
+void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode mode, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
RETURN_ON_NULL(vertices);
SkASSERT(boneCount <= 80);
this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
}
-void SkCanvas::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
+void SkCanvas::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode mode, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
RETURN_ON_NULL(vertices);
SkASSERT(boneCount <= 80);
@@ -2576,7 +2576,7 @@
this->onDrawTextBlob(blob, x, y, paint);
}
-void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
int boneCount, SkBlendMode bmode, const SkPaint& paint) {
LOOPER_BEGIN(paint, nullptr)
diff --git a/src/core/SkColorSpaceXformCanvas.cpp b/src/core/SkColorSpaceXformCanvas.cpp
index 949dd4a..d0032b4 100644
--- a/src/core/SkColorSpaceXformCanvas.cpp
+++ b/src/core/SkColorSpaceXformCanvas.cpp
@@ -90,7 +90,8 @@
const SkPaint& paint) override {
fTarget->drawPoints(mode, count, pts, fXformer->apply(paint));
}
- void onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
+
+ void onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[], int boneCount,
SkBlendMode mode, const SkPaint& paint) override {
sk_sp<SkVertices> copy;
if (vertices->hasColors()) {
diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h
index e00ffdd..ad07e81 100644
--- a/src/core/SkDevice.h
+++ b/src/core/SkDevice.h
@@ -213,8 +213,8 @@
const SkRect& dst, const SkPaint&);
- virtual void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
- const SkPaint&) = 0;
+ virtual void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) = 0;
virtual void drawShadow(const SkPath&, const SkDrawShadowRec&);
virtual void drawGlyphRunList(const SkGlyphRunList& glyphRunList);
@@ -420,7 +420,7 @@
void drawPosText(const void*, size_t, const SkScalar[], int, const SkPoint&,
const SkPaint&) override {}
void drawDevice(SkBaseDevice*, int, int, const SkPaint&) override {}
- void drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
const SkPaint&) override {}
private:
diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h
index ca37c09..bf023bc 100644
--- a/src/core/SkDraw.h
+++ b/src/core/SkDraw.h
@@ -74,7 +74,7 @@
const SkColor colors[], const SkVertices::BoneIndices boneIndices[],
const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode,
const uint16_t indices[], int ptCount,
- const SkPaint& paint, const SkMatrix* bones, int boneCount) const;
+ const SkPaint& paint, const SkVertices::Bone bones[], int boneCount) const;
/**
* Overwrite the target with the path's coverage (i.e. its mask).
diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp
index 4d1ccea..b9320d4 100644
--- a/src/core/SkDraw_vertices.cpp
+++ b/src/core/SkDraw_vertices.cpp
@@ -167,7 +167,8 @@
const SkColor colors[], const SkVertices::BoneIndices boneIndices[],
const SkVertices::BoneWeights boneWeights[], SkBlendMode bmode,
const uint16_t indices[], int indexCount,
- const SkPaint& paint, const SkMatrix* bones, int boneCount) const {
+ const SkPaint& paint, const SkVertices::Bone bones[],
+ int boneCount) const {
SkASSERT(0 == vertexCount || vertices);
// abort early if there is nothing to draw
@@ -207,11 +208,9 @@
}
constexpr size_t kDefVertexCount = 16;
- constexpr size_t kDefBoneCount = 8;
constexpr size_t kOuterSize = sizeof(SkTriColorShader) +
sizeof(SkComposeShader) +
- (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount +
- sizeof(SkMatrix) * kDefBoneCount;
+ (2 * sizeof(SkPoint) + sizeof(SkPM4f)) * kDefVertexCount;
SkSTArenaAlloc<kOuterSize> outerAlloc;
// deform vertices using the skeleton if it is passed in
@@ -219,28 +218,21 @@
// allocate space for the deformed vertices
SkPoint* deformed = outerAlloc.makeArray<SkPoint>(vertexCount);
- // get the bone matrices
- SkMatrix* transformedBones = outerAlloc.makeArray<SkMatrix>(boneCount);
-
- // transform the bone matrices by the world transform
- transformedBones[0] = bones[0];
- for (int i = 1; i < boneCount; i ++) {
- transformedBones[i] = SkMatrix::Concat(bones[i], bones[0]);
- }
-
// deform the vertices
if (boneIndices && boneWeights) {
for (int i = 0; i < vertexCount; i ++) {
const SkVertices::BoneIndices& indices = boneIndices[i];
const SkVertices::BoneWeights& weights = boneWeights[i];
+ // apply the world transform
+ SkPoint worldPoint = bones[0].mapPoint(vertices[i]);
+
// apply bone deformations
- SkPoint result = SkPoint::Make(0.0f, 0.0f);
- SkPoint transformed;
+ deformed[i] = SkPoint::Make(0.0f, 0.0f);
for (uint32_t j = 0; j < 4; j ++) {
// get the attachment data
- uint32_t index = indices.indices[j];
- float weight = weights.weights[j];
+ uint32_t index = indices[j];
+ float weight = weights[j];
// skip the bone if there is no weight
if (weight == 0.0f) {
@@ -248,19 +240,14 @@
}
SkASSERT(index != 0);
- // transformed = M * v
- transformedBones[index].mapPoints(&transformed, &vertices[i], 1);
-
- // result += transformed * w
- result += transformed * weight;
+ // deformed += M * v * w
+ deformed[i] += bones[index].mapPoint(worldPoint) * weight;
}
-
- // set the deformed point
- deformed[i] = result;
}
} else {
// no bones, so only apply world transform
- const SkMatrix& worldTransform = bones[0];
+ SkMatrix worldTransform = SkMatrix::I();
+ worldTransform.setAffine(bones[0].values);
worldTransform.mapPoints(deformed, vertices, vertexCount);
}
diff --git a/src/core/SkLiteDL.cpp b/src/core/SkLiteDL.cpp
index ea3ca6d..d4e3901 100644
--- a/src/core/SkLiteDL.cpp
+++ b/src/core/SkLiteDL.cpp
@@ -455,7 +455,7 @@
SkBlendMode mode;
SkPaint paint;
void draw(SkCanvas* c, const SkMatrix&) const {
- c->drawVertices(vertices, pod<SkMatrix>(this), boneCount, mode, paint);
+ c->drawVertices(vertices, pod<SkVertices::Bone>(this), boneCount, mode, paint);
}
};
struct DrawAtlas final : Op {
@@ -660,9 +660,9 @@
void* pod = this->push<DrawPoints>(count*sizeof(SkPoint), mode, count, paint);
copy_v(pod, points,count);
}
-void SkLiteDL::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
- void* pod = this->push<DrawVertices>(boneCount * sizeof(SkMatrix),
+void SkLiteDL::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode mode, const SkPaint& paint) {
+ void* pod = this->push<DrawVertices>(boneCount * sizeof(SkVertices::Bone),
vertices,
boneCount,
mode,
diff --git a/src/core/SkLiteDL.h b/src/core/SkLiteDL.h
index aa32e02..ffcdb89 100644
--- a/src/core/SkLiteDL.h
+++ b/src/core/SkLiteDL.h
@@ -72,7 +72,7 @@
void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4],
SkBlendMode, const SkPaint&);
void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&);
- void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint&);
void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
SkBlendMode, const SkRect*, const SkPaint*);
diff --git a/src/core/SkLiteRecorder.cpp b/src/core/SkLiteRecorder.cpp
index 971e62c..fedfa99 100644
--- a/src/core/SkLiteRecorder.cpp
+++ b/src/core/SkLiteRecorder.cpp
@@ -175,8 +175,9 @@
const SkPaint& paint) {
fDL->drawPoints(mode, count, pts, paint);
}
-void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
- int boneCount, SkBlendMode mode, const SkPaint& paint) {
+void SkLiteRecorder::onDrawVerticesObject(const SkVertices* vertices,
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode mode, const SkPaint& paint) {
fDL->drawVertices(vertices, bones, boneCount, mode, paint);
}
void SkLiteRecorder::onDrawAtlas(const SkImage* atlas,
diff --git a/src/core/SkLiteRecorder.h b/src/core/SkLiteRecorder.h
index bc37546..5ae08fa 100644
--- a/src/core/SkLiteRecorder.h
+++ b/src/core/SkLiteRecorder.h
@@ -73,8 +73,8 @@
void onDrawPatch(const SkPoint[12], const SkColor[4],
const SkPoint[4], SkBlendMode, const SkPaint&) override;
void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
- void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
- const SkPaint&) override;
+ void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) override;
void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
int, SkBlendMode, const SkRect*, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/src/core/SkOverdrawCanvas.cpp b/src/core/SkOverdrawCanvas.cpp
index 0e20878..4e6b8c5 100644
--- a/src/core/SkOverdrawCanvas.cpp
+++ b/src/core/SkOverdrawCanvas.cpp
@@ -216,9 +216,9 @@
fList[0]->onDrawPoints(mode, count, points, this->overdrawPaint(paint));
}
-void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
- int boneCount, SkBlendMode blendMode,
- const SkPaint& paint) {
+void SkOverdrawCanvas::onDrawVerticesObject(const SkVertices* vertices,
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode blendMode, const SkPaint& paint) {
fList[0]->onDrawVerticesObject(vertices,
bones,
boneCount,
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f63abe7..4ee0eae 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -616,9 +616,9 @@
const SkPaint* paint = fPictureData->getPaint(reader);
const SkVertices* vertices = fPictureData->getVertices(reader);
const int boneCount = reader->readInt();
- const SkMatrix* bones = boneCount ?
- (const SkMatrix*) reader->skip(boneCount, sizeof(SkMatrix)) :
- nullptr;
+ const SkVertices::Bone* bones = boneCount ?
+ (const SkVertices::Bone*) reader->skip(boneCount, sizeof(SkVertices::Bone)) :
+ nullptr;
SkBlendMode bmode = reader->read32LE(SkBlendMode::kLastMode);
BREAK_ON_READ_ERROR(reader);
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index ca36e7e..81e5135 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -682,16 +682,17 @@
this->validate(initialOffset, size);
}
-void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
- int boneCount, SkBlendMode mode, const SkPaint& paint) {
+void SkPictureRecord::onDrawVerticesObject(const SkVertices* vertices,
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode mode, const SkPaint& paint) {
// op + paint index + vertices index + number of bones + bone matrices + mode
- size_t size = 5 * kUInt32Size + boneCount * sizeof(SkMatrix);
+ size_t size = 5 * kUInt32Size + boneCount * sizeof(SkVertices::Bone);
size_t initialOffset = this->addDraw(DRAW_VERTICES_OBJECT, &size);
this->addPaint(paint);
this->addVertices(vertices);
this->addInt(boneCount);
- fWriter.write(bones, boneCount * sizeof(SkMatrix));
+ fWriter.write(bones, boneCount * sizeof(SkVertices::Bone));
this->addInt(static_cast<uint32_t>(mode));
this->validate(initialOffset, size);
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index dc9f4d9..dcec581 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -196,8 +196,8 @@
void onDrawImageLattice(const SkImage*, const SkCanvas::Lattice& lattice, const SkRect& dst,
const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
- void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
- const SkPaint&) override;
+ void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) override;
void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/src/core/SkRecorder.cpp b/src/core/SkRecorder.cpp
index 1abd582..f7c8dd4 100644
--- a/src/core/SkRecorder.cpp
+++ b/src/core/SkRecorder.cpp
@@ -317,7 +317,7 @@
}
}
-void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkRecorder::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
int boneCount, SkBlendMode bmode, const SkPaint& paint) {
this->append<SkRecords::DrawVertices>(paint,
sk_ref_sp(const_cast<SkVertices*>(vertices)),
diff --git a/src/core/SkRecorder.h b/src/core/SkRecorder.h
index 7fed8ea..ce1131f 100644
--- a/src/core/SkRecorder.h
+++ b/src/core/SkRecorder.h
@@ -121,8 +121,8 @@
const SkPaint*) override;
void onDrawBitmapLattice(const SkBitmap&, const Lattice& lattice, const SkRect& dst,
const SkPaint*) override;
- void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
- const SkPaint&) override;
+ void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) override;
void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[],
int count, SkBlendMode, const SkRect* cull, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
index 4ec2ae6..cce68c9 100644
--- a/src/core/SkRecords.h
+++ b/src/core/SkRecords.h
@@ -344,7 +344,7 @@
RECORD(DrawVertices, kDraw_Tag|kHasPaint_Tag,
SkPaint paint;
sk_sp<SkVertices> vertices;
- PODArray<SkMatrix> bones;
+ PODArray<SkVertices::Bone> bones;
int boneCount;
SkBlendMode bmode);
RECORD(DrawShadowRec, kDraw_Tag,
diff --git a/src/core/SkVertices.cpp b/src/core/SkVertices.cpp
index 97a136c..a3a3989 100644
--- a/src/core/SkVertices.cpp
+++ b/src/core/SkVertices.cpp
@@ -207,6 +207,69 @@
return const_cast<uint16_t*>(fVertices->indices());
}
+/** Makes a copy of the SkVertices and applies a set of bones, then returns the deformed
+ vertices.
+
+ @param bones The bones to apply.
+ @param boneCount The number of bones.
+ @return The transformed SkVertices.
+*/
+sk_sp<SkVertices> SkVertices::applyBones(const SkVertices::Bone bones[], int boneCount) const {
+ // If there aren't any bones, then nothing changes.
+ // We don't check if the SkVertices object has bone indices/weights because there is the case
+ // where the object can have no indices/weights but still have a world transform applied.
+ if (!bones || !boneCount) {
+ return sk_ref_sp(this);
+ }
+ SkASSERT(boneCount >= 1);
+
+ // Copy the SkVertices.
+ sk_sp<SkVertices> copy = SkVertices::MakeCopy(this->mode(),
+ this->vertexCount(),
+ this->positions(),
+ this->texCoords(),
+ this->colors(),
+ nullptr,
+ nullptr,
+ this->indexCount(),
+ this->indices());
+
+ // Transform the positions.
+ for (int i = 0; i < this->vertexCount(); i++) {
+ SkPoint& position = copy->fPositions[i];
+
+ // Apply the world transform.
+ position = bones[0].mapPoint(position);
+
+ // Apply the bone deformations.
+ if (boneCount > 1) {
+ SkASSERT(this->boneIndices());
+ SkASSERT(this->boneWeights());
+
+ SkPoint result = SkPoint::Make(0.0f, 0.0f);
+ const SkVertices::BoneIndices& indices = this->boneIndices()[i];
+ const SkVertices::BoneWeights& weights = this->boneWeights()[i];
+ for (int j = 0; j < 4; j++) {
+ int index = indices[j];
+ float weight = weights[j];
+ if (index == 0 || weight == 0.0f) {
+ continue;
+ }
+ SkASSERT(index < boneCount);
+
+ // result += M * v * w.
+ result += bones[index].mapPoint(position) * weight;
+ }
+ position = result;
+ }
+ }
+
+ // Recalculate the bounds.
+ copy->fBounds.set(copy->fPositions, copy->fVertexCnt);
+
+ return copy;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
diff --git a/src/gpu/GrDefaultGeoProcFactory.cpp b/src/gpu/GrDefaultGeoProcFactory.cpp
index 027296c..9d61fb3 100644
--- a/src/gpu/GrDefaultGeoProcFactory.cpp
+++ b/src/gpu/GrDefaultGeoProcFactory.cpp
@@ -31,8 +31,9 @@
kBonesAttribute_GPFlag = 0x10,
};
-static constexpr int kMaxBones = 80; // Due to GPU memory limitations, only up to 80 bone
- // matrices are accepted.
+static constexpr int kNumVec2sPerBone = 3; // Our bone matrices are 3x2 matrices passed in as
+ // vec2s in column major order, and thus there are 3
+ // vec2s per bone.
class DefaultGeoProc : public GrGeometryProcessor {
public:
@@ -111,21 +112,36 @@
}
// Setup bone transforms
+ // NOTE: This code path is currently unused. Benchmarks have found that for all
+ // reasonable cases of skinned vertices, the overhead involved in copying and uploading
+ // bone data makes performing the transformations on the CPU faster than doing so on
+ // the GPU. This is being kept here in case that changes.
const char* transformedPositionName = gp.fInPosition.name();
if (gp.hasBones()) {
+ // Set up the uniform for the bones.
const char* vertBonesUniformName;
fBonesUniform = uniformHandler->addUniformArray(kVertex_GrShaderFlag,
- kFloat3x3_GrSLType,
+ kFloat2_GrSLType,
"Bones",
- kMaxBones,
+ kMaxBones * kNumVec2sPerBone,
&vertBonesUniformName);
+
+ // Set up the bone application function.
+ SkString applyBoneFunctionName;
+ this->emitApplyBoneFunction(vertBuilder,
+ vertBonesUniformName,
+ &applyBoneFunctionName);
+
+ // Apply the world transform to the position first.
vertBuilder->codeAppendf(
- "float3 originalPosition = %s[0] * float3(%s, 1);"
- "float2 transformedPosition = float2(0);"
+ "float2 worldPosition = %s(0, %s);"
+ "float2 transformedPosition = float2(0, 0);"
"for (int i = 0; i < 4; i++) {",
- vertBonesUniformName,
+ applyBoneFunctionName.c_str(),
gp.fInPosition.name());
+ // If the GPU supports unsigned integers, then we can read the index. Otherwise,
+ // we have to estimate it given the float representation.
if (args.fShaderCaps->unsignedSupport()) {
vertBuilder->codeAppendf(
" byte index = %s[i];",
@@ -136,12 +152,13 @@
gp.fInBoneIndices.name());
}
+ // Get the weight and apply the transformation.
vertBuilder->codeAppendf(
" float weight = %s[i];"
- " transformedPosition += (%s[index] * originalPosition * weight).xy;"
+ " transformedPosition += %s(index, worldPosition) * weight;"
"}",
gp.fInBoneWeights.name(),
- vertBonesUniformName);
+ applyBoneFunctionName.c_str());
transformedPositionName = "transformedPosition";
}
@@ -228,11 +245,41 @@
fColorSpaceHelper.setData(pdman, dgp.fColorSpaceXform.get());
if (dgp.hasBones()) {
- pdman.setMatrix3fv(fBonesUniform, dgp.boneCount(), dgp.bones());
+ pdman.set2fv(fBonesUniform, dgp.boneCount() * kNumVec2sPerBone, dgp.bones());
}
}
private:
+ void emitApplyBoneFunction(GrGLSLVertexBuilder* vertBuilder,
+ const char* vertBonesUniformName,
+ SkString* funcName) {
+ // The bone matrices are passed in as 3x2 matrices in column-major order as groups
+ // of 3 float2s. This code takes those float2s and performs the matrix operation on
+ // a given matrix and float2.
+ static const GrShaderVar gApplyBoneArgs[] = {
+ GrShaderVar("index", kByte_GrSLType),
+ GrShaderVar("vec", kFloat2_GrSLType),
+ };
+ SkString body;
+ body.appendf(
+ " float2 c0 = %s[index * 3];"
+ " float2 c1 = %s[index * 3 + 1];"
+ " float2 c2 = %s[index * 3 + 2];"
+ " float x = c0.x * vec.x + c1.x * vec.y + c2.x;"
+ " float y = c0.y * vec.x + c1.y * vec.y + c2.y;"
+ " return float2(x, y);",
+ vertBonesUniformName,
+ vertBonesUniformName,
+ vertBonesUniformName);
+ vertBuilder->emitFunction(kFloat2_GrSLType,
+ "applyBone",
+ SK_ARRAY_COUNT(gApplyBoneArgs),
+ gApplyBoneArgs,
+ body.c_str(),
+ funcName);
+ }
+
+ private:
SkMatrix fViewMatrix;
GrColor fColor;
uint8_t fCoverage;
@@ -336,13 +383,13 @@
GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DefaultGeoProc);
#if GR_TEST_UTILS
-static constexpr int kNumFloatsPerSkMatrix = 9;
+static constexpr int kNumFloatsPerBone = 6;
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,
+static constexpr float kTestBones[kTestBoneCount * kNumFloatsPerBone] = {
+ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
};
sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h
index 3c4bd8e..b5aef98 100644
--- a/src/gpu/GrDefaultGeoProcFactory.h
+++ b/src/gpu/GrDefaultGeoProcFactory.h
@@ -12,6 +12,8 @@
#include "GrGeometryProcessor.h"
#include "GrShaderCaps.h"
+constexpr int kMaxBones = 80; // Supports up to 80 bones per mesh.
+
/*
* A factory for creating default Geometry Processors which simply multiply position by the uniform
* view matrix and wire through color, coverage, UV coords if requested.
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 787bf7c..9664109 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -839,7 +839,7 @@
GrPaint&& paint,
const SkMatrix& viewMatrix,
sk_sp<SkVertices> vertices,
- const SkMatrix bones[],
+ const SkVertices::Bone bones[],
int boneCount,
GrPrimitiveType* overridePrimType) {
ASSERT_SINGLE_OWNER
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index b0756a0..808d442 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -215,7 +215,7 @@
GrPaint&& paint,
const SkMatrix& viewMatrix,
sk_sp<SkVertices> vertices,
- const SkMatrix bones[],
+ const SkVertices::Bone bones[],
int boneCount,
GrPrimitiveType* overridePrimType = nullptr);
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 1fe2ae4..6c3bd0a 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1484,7 +1484,7 @@
void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCount,
const SkPoint vertices[],
- const SkMatrix bones[], int boneCount,
+ const SkVertices::Bone bones[], int boneCount,
SkBlendMode bmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
@@ -1548,8 +1548,8 @@
&primitiveType);
}
-void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix bones[], int boneCount,
- SkBlendMode mode, const SkPaint& paint) {
+void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkVertices::Bone bones[],
+ int boneCount, SkBlendMode mode, const SkPaint& paint) {
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get());
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index a3233ad..555e25e 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -87,7 +87,7 @@
void drawPosText(const void* text, size_t len, const SkScalar pos[],
int scalarsPerPos, const SkPoint& offset, const SkPaint&) override;
void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
- void drawVertices(const SkVertices*, const SkMatrix bones[], int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint&) override;
void drawShadow(const SkPath&, const SkDrawShadowRec&) override;
void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
@@ -244,7 +244,7 @@
void drawStrokedLine(const SkPoint pts[2], const SkPaint&);
void wireframeVertices(SkVertices::VertexMode, int vertexCount, const SkPoint verts[],
- const SkMatrix bones[], int boneCount, SkBlendMode,
+ const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const uint16_t indices[], int indexCount, const SkPaint&);
static sk_sp<GrRenderTargetContext> MakeRenderTargetContext(GrContext*,
diff --git a/src/gpu/ops/GrDrawVerticesOp.cpp b/src/gpu/ops/GrDrawVerticesOp.cpp
index dd91452..04b537f 100644
--- a/src/gpu/ops/GrDrawVerticesOp.cpp
+++ b/src/gpu/ops/GrDrawVerticesOp.cpp
@@ -12,12 +12,10 @@
#include "SkGr.h"
#include "SkRectPriv.h"
-static constexpr int kNumFloatsPerSkMatrix = 9;
-
std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrContext* context,
GrPaint&& paint,
sk_sp<SkVertices> vertices,
- const SkMatrix bones[],
+ const SkVertices::Bone bones[],
int boneCount,
const SkMatrix& viewMatrix,
GrAAType aaType,
@@ -32,7 +30,7 @@
}
GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color,
- sk_sp<SkVertices> vertices, const SkMatrix bones[],
+ sk_sp<SkVertices> vertices, const SkVertices::Bone bones[],
int boneCount, GrPrimitiveType primitiveType, GrAAType aaType,
sk_sp<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix)
@@ -51,26 +49,23 @@
mesh.fColor = color;
mesh.fViewMatrix = viewMatrix;
mesh.fVertices = std::move(vertices);
- if (bones) {
- // Copy the bone data over in the format that the GPU would upload.
- mesh.fBones.reserve(boneCount * kNumFloatsPerSkMatrix);
- for (int i = 0; i < boneCount; i ++) {
- const SkMatrix& matrix = bones[i];
- mesh.fBones.push_back(matrix.get(SkMatrix::kMScaleX));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMSkewY));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp0));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMSkewX));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMScaleY));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp1));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMTransX));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMTransY));
- mesh.fBones.push_back(matrix.get(SkMatrix::kMPersp2));
- }
- }
mesh.fIgnoreTexCoords = false;
mesh.fIgnoreColors = false;
mesh.fIgnoreBones = false;
+ if (mesh.fVertices->hasBones() && bones) {
+ // Perform the transformations on the CPU instead of the GPU.
+ mesh.fVertices = mesh.fVertices->applyBones(bones, boneCount);
+ } else {
+ if (bones && boneCount > 1) {
+ // NOTE: This should never be used. All bone transforms are being done on the CPU
+ // instead of the GPU.
+
+ // Copy the bone data.
+ fBones.assign(bones, bones + boneCount);
+ }
+ }
+
fFlags = 0;
if (mesh.hasPerVertexColors()) {
fFlags |= kRequiresPerVertexColors_Flag;
@@ -85,7 +80,9 @@
// Special case for meshes with a world transform but no bone weights.
// These will be considered normal vertices draws without bones.
if (!mesh.fVertices->hasBones() && boneCount == 1) {
- mesh.fViewMatrix.preConcat(bones[0]);
+ SkMatrix worldTransform;
+ worldTransform.setAffine(bones[0].values);
+ mesh.fViewMatrix.preConcat(worldTransform);
}
IsZeroArea zeroArea;
@@ -101,7 +98,7 @@
SkRect bounds = SkRect::MakeEmpty();
const SkRect originalBounds = bones[0].mapRect(mesh.fVertices->bounds());
for (int i = 1; i < boneCount; i++) {
- const SkMatrix& matrix = bones[i];
+ const SkVertices::Bone& matrix = bones[i];
bounds.join(matrix.mapRect(originalBounds));
}
@@ -188,7 +185,9 @@
const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
- Bones bones(fMeshes[0].fBones.data(), fMeshes[0].fBones.size() / kNumFloatsPerSkMatrix);
+ // The bones are packed as 6 floats in column major order, so we can directly upload them to
+ // the GPU as groups of 3 vec2s.
+ Bones bones(reinterpret_cast<const float*>(fBones.data()), fBones.size());
*hasBoneAttribute = this->hasBones();
if (this->hasBones()) {
diff --git a/src/gpu/ops/GrDrawVerticesOp.h b/src/gpu/ops/GrDrawVerticesOp.h
index 07227e0..fd40e461 100644
--- a/src/gpu/ops/GrDrawVerticesOp.h
+++ b/src/gpu/ops/GrDrawVerticesOp.h
@@ -38,16 +38,16 @@
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
GrPaint&&,
sk_sp<SkVertices>,
- const SkMatrix bones[],
+ const SkVertices::Bone bones[],
int boneCount,
const SkMatrix& viewMatrix,
GrAAType,
sk_sp<GrColorSpaceXform>,
GrPrimitiveType* overridePrimType = nullptr);
- GrDrawVerticesOp(const Helper::MakeArgs&, GrColor, sk_sp<SkVertices>, const SkMatrix bones[],
- int boneCount, GrPrimitiveType, GrAAType, sk_sp<GrColorSpaceXform>,
- const SkMatrix& viewMatrix);
+ GrDrawVerticesOp(const Helper::MakeArgs&, GrColor, sk_sp<SkVertices>,
+ const SkVertices::Bone bones[], int boneCount, GrPrimitiveType, GrAAType,
+ sk_sp<GrColorSpaceXform>, const SkMatrix& viewMatrix);
const char* name() const override { return "DrawVerticesOp"; }
@@ -103,7 +103,6 @@
struct Mesh {
GrColor fColor; // Used if this->hasPerVertexColors() is false.
sk_sp<SkVertices> fVertices;
- std::vector<float> fBones; // Transformation matrices stored in GPU format.
SkMatrix fViewMatrix;
bool fIgnoreTexCoords;
bool fIgnoreColors;
@@ -118,7 +117,7 @@
}
bool hasBones() const {
- return fVertices->hasBones() && fBones.size() && !fIgnoreBones;
+ return fVertices->hasBones() && !fIgnoreBones;
}
};
@@ -152,6 +151,7 @@
Helper fHelper;
SkSTArray<1, Mesh, true> fMeshes;
+ std::vector<SkVertices::Bone> fBones; // Bone transformation matrices.
// GrPrimitiveType is more expressive than fVertices.mode() so it is used instead and we ignore
// the SkVertices mode (though fPrimitiveType may have been inferred from it).
GrPrimitiveType fPrimitiveType;
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 8664e50..fb208f8 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1375,7 +1375,7 @@
}
}
-void SkPDFDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+void SkPDFDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
const SkPaint&) {
if (this->hasEmptyClip()) {
return;
diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h
index 9a91790..0197b5a 100644
--- a/src/pdf/SkPDFDevice.h
+++ b/src/pdf/SkPDFDevice.h
@@ -97,7 +97,7 @@
const SkScalar pos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint&) override { SkASSERT(false); }
void drawGlyphRunList(const SkGlyphRunList& glyphRunList) override;
- void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint&) override;
void drawDevice(SkBaseDevice*, int x, int y,
const SkPaint&) override;
diff --git a/src/pipe/SkPipeCanvas.cpp b/src/pipe/SkPipeCanvas.cpp
index 258160c..bf06579 100644
--- a/src/pipe/SkPipeCanvas.cpp
+++ b/src/pipe/SkPipeCanvas.cpp
@@ -728,7 +728,7 @@
write_paint(writer, paint, kGeometry_PaintUsage);
}
-void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkPipeCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
int boneCount, SkBlendMode bmode, const SkPaint& paint) {
unsigned extra = static_cast<unsigned>(bmode);
@@ -737,7 +737,7 @@
// TODO: dedup vertices?
writer.writeDataAsByteArray(vertices->encode().get());
writer.write32(boneCount);
- writer.write(bones, sizeof(SkMatrix) * boneCount);
+ writer.write(bones, sizeof(SkVertices::Bone) * boneCount);
write_paint(writer, paint, kVertices_PaintUsage);
}
diff --git a/src/pipe/SkPipeCanvas.h b/src/pipe/SkPipeCanvas.h
index b39f809..a679491 100644
--- a/src/pipe/SkPipeCanvas.h
+++ b/src/pipe/SkPipeCanvas.h
@@ -135,8 +135,8 @@
const SkPaint*) override;
void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
const SkPaint*) override;
- void onDrawVerticesObject(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
- const SkPaint&) override;
+ void onDrawVerticesObject(const SkVertices*, const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode, const SkPaint&) override;
void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
diff --git a/src/pipe/SkPipeReader.cpp b/src/pipe/SkPipeReader.cpp
index 909276f..1d77779 100644
--- a/src/pipe/SkPipeReader.cpp
+++ b/src/pipe/SkPipeReader.cpp
@@ -567,7 +567,7 @@
vertices = SkVertices::Decode(data->data(), data->size());
}
int boneCount = reader.read32();
- const SkMatrix* bones = boneCount ? reader.skipT<SkMatrix>(boneCount) : nullptr;
+ const SkVertices::Bone* bones = boneCount ? reader.skipT<SkVertices::Bone>(boneCount) : nullptr;
if (vertices) {
canvas->drawVertices(vertices, bones, boneCount, bmode, read_paint(reader));
}
diff --git a/src/svg/SkSVGDevice.cpp b/src/svg/SkSVGDevice.cpp
index 1f7ec36..593fe40 100644
--- a/src/svg/SkSVGDevice.cpp
+++ b/src/svg/SkSVGDevice.cpp
@@ -1040,7 +1040,7 @@
}
}
-void SkSVGDevice::drawVertices(const SkVertices*, const SkMatrix*, int, SkBlendMode,
+void SkSVGDevice::drawVertices(const SkVertices*, const SkVertices::Bone[], int, SkBlendMode,
const SkPaint&) {
// todo
}
diff --git a/src/svg/SkSVGDevice.h b/src/svg/SkSVGDevice.h
index 452229d..e57ef36 100644
--- a/src/svg/SkSVGDevice.h
+++ b/src/svg/SkSVGDevice.h
@@ -42,7 +42,7 @@
void drawTextOnPath(const void* text, size_t len,
const SkPath& path, const SkMatrix* matrix,
const SkPaint& paint) override;
- void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint& paint) override;
void drawDevice(SkBaseDevice*, int x, int y,
diff --git a/src/utils/SkLuaCanvas.cpp b/src/utils/SkLuaCanvas.cpp
index 441857f..edef7e3 100644
--- a/src/utils/SkLuaCanvas.cpp
+++ b/src/utils/SkLuaCanvas.cpp
@@ -314,8 +314,8 @@
this->INHERITED::onDrawDrawable(drawable, matrix);
}
-void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, const SkMatrix*, int, SkBlendMode,
- const SkPaint& paint) {
+void SkLuaCanvas::onDrawVerticesObject(const SkVertices*, const SkVertices::Bone[], int,
+ SkBlendMode, const SkPaint& paint) {
AUTO_LUA("drawVertices");
lua.pushPaint(paint, "paint");
}
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index 7d1e981..ac3a971 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -320,7 +320,7 @@
}
}
-void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
+void SkNWayCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkVertices::Bone bones[],
int boneCount, SkBlendMode bmode, const SkPaint& paint) {
Iter iter(fList);
while (iter.next()) {
diff --git a/src/utils/SkPaintFilterCanvas.cpp b/src/utils/SkPaintFilterCanvas.cpp
index 929488c..862bca7 100644
--- a/src/utils/SkPaintFilterCanvas.cpp
+++ b/src/utils/SkPaintFilterCanvas.cpp
@@ -173,9 +173,9 @@
}
}
-void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices, const SkMatrix* bones,
- int boneCount, SkBlendMode bmode,
- const SkPaint& paint) {
+void SkPaintFilterCanvas::onDrawVerticesObject(const SkVertices* vertices,
+ const SkVertices::Bone bones[], int boneCount,
+ SkBlendMode bmode, const SkPaint& paint) {
AutoPaintFilter apf(this, kVertices_Type, paint);
if (apf.shouldDraw()) {
this->SkNWayCanvas::onDrawVerticesObject(vertices, bones, boneCount, bmode, *apf.paint());
diff --git a/src/xps/SkXPSDevice.cpp b/src/xps/SkXPSDevice.cpp
index ff66e3b..e73b726 100644
--- a/src/xps/SkXPSDevice.cpp
+++ b/src/xps/SkXPSDevice.cpp
@@ -1156,7 +1156,7 @@
draw(this, &SkDraw::drawPoints, mode, count, points, paint, this);
}
-void SkXPSDevice::drawVertices(const SkVertices* v, const SkMatrix* bones, int boneCount,
+void SkXPSDevice::drawVertices(const SkVertices* v, const SkVertices::Bone bones[], int boneCount,
SkBlendMode blendMode, const SkPaint& paint) {
draw(this, &SkDraw::drawVertices, v->mode(), v->vertexCount(), v->positions(), v->texCoords(),
v->colors(), v->boneIndices(), v->boneWeights(), blendMode, v->indices(), v->indexCount(),
diff --git a/src/xps/SkXPSDevice.h b/src/xps/SkXPSDevice.h
index c3f27c4..877eb3b 100644
--- a/src/xps/SkXPSDevice.h
+++ b/src/xps/SkXPSDevice.h
@@ -99,7 +99,7 @@
void drawPosText(const void* text, size_t len,
const SkScalar pos[], int scalarsPerPos,
const SkPoint& offset, const SkPaint& paint) override;
- void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
+ void drawVertices(const SkVertices*, const SkVertices::Bone bones[], int boneCount, SkBlendMode,
const SkPaint&) override;
void drawDevice(SkBaseDevice*, int x, int y,
const SkPaint&) override;