added skeletal animation support to GPU backend

added caching of SkVertices

Docs-Preview: https://skia.org/?cl=138596
Bug: skia:
Change-Id: Ia750f55f5f6d0de250d9e9c5619f4d1ac856f9f5
Reviewed-on: https://skia-review.googlesource.com/138596
Reviewed-by: Brian Osman <brianosman@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Ruiqi Mao <ruiqimao@google.com>
This commit is contained in:
Ruiqi Mao 2018-07-03 11:38:15 -04:00 committed by Skia Commit-Bot
parent b7b73f5bbe
commit b6307340e8
28 changed files with 566 additions and 120 deletions

View File

@ -5772,6 +5772,7 @@ contains Shader, Blend_Mode mode combines Vertices_Colors with Shader.
The first element of bones should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
boneCount must be at most 100, and thus the size of bones should be at most 100.
#Param vertices triangle mesh to draw ##
#Param bones bone matrix data ##
@ -5821,6 +5822,7 @@ contains Shader, Blend_Mode mode combines Vertices_Colors with Shader.
The first element of bones should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
boneCount must be at most 100, and thus the size of bones should be at most 100.
#Param vertices triangle mesh to draw ##
#Param bones bone matrix data ##

View File

@ -6,7 +6,7 @@
*/
/* Generated by tools/bookmaker from include/core/SkCanvas.h and docs/SkCanvas_Reference.bmh
on 2018-07-02 10:27:08. Additional documentation and examples can be found at:
on 2018-07-03 11:34:22. Additional documentation and examples can be found at:
https://skia.org/user/api/SkCanvas_Reference
You may edit either file directly. Structural changes to public interfaces require
@ -2156,6 +2156,7 @@ public:
The first element of bones should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
boneCount must be at most 100, and thus the size of bones should be at most 100.
@param vertices triangle mesh to draw
@param bones bone matrix data
@ -2173,6 +2174,7 @@ public:
The first element of bones should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
boneCount must be at most 100, and thus the size of bones should be at most 100.
@param vertices triangle mesh to draw
@param bones bone matrix data

View File

@ -55,14 +55,16 @@ public:
const BoneIndices boneIndices[],
const BoneWeights boneWeights[],
int indexCount,
const uint16_t indices[]);
const uint16_t indices[],
bool isVolatile = true);
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[],
const BoneIndices boneIndices[],
const BoneWeights boneWeights[]) {
const BoneWeights boneWeights[],
bool isVolatile = true) {
return MakeCopy(mode,
vertexCount,
positions,
@ -71,7 +73,8 @@ public:
boneIndices,
boneWeights,
0,
nullptr);
nullptr,
isVolatile);
}
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
@ -79,7 +82,8 @@ public:
const SkPoint texs[],
const SkColor colors[],
int indexCount,
const uint16_t indices[]) {
const uint16_t indices[],
bool isVolatile = true) {
return MakeCopy(mode,
vertexCount,
positions,
@ -88,14 +92,16 @@ public:
nullptr,
nullptr,
indexCount,
indices);
indices,
isVolatile);
}
static sk_sp<SkVertices> MakeCopy(VertexMode mode, int vertexCount,
const SkPoint positions[],
const SkPoint texs[],
const SkColor colors[]) {
return MakeCopy(mode, vertexCount, positions, texs, colors, nullptr, nullptr);
const SkColor colors[],
bool isVolatile = true) {
return MakeCopy(mode, vertexCount, positions, texs, colors, nullptr, nullptr, isVolatile);
}
struct Sizes;
@ -104,6 +110,7 @@ public:
kHasTexCoords_BuilderFlag = 1 << 0,
kHasColors_BuilderFlag = 1 << 1,
kHasBones_BuilderFlag = 1 << 2,
kIsVolatile_BuilderFlag = 1 << 3,
};
class Builder {
public:
@ -114,6 +121,7 @@ public:
// if the builder is invalid, these will return 0
int vertexCount() const;
int indexCount() const;
bool isVolatile() const;
SkPoint* positions();
SkPoint* texCoords(); // returns null if there are no texCoords
SkColor* colors(); // returns null if there are no colors
@ -125,9 +133,9 @@ public:
sk_sp<SkVertices> detach();
private:
Builder(VertexMode mode, int vertexCount, int indexCount, const Sizes&);
Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
void init(VertexMode mode, int vertexCount, int indexCount, const Sizes&);
void init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile, const Sizes&);
// holds a partially complete object. only completed in detach()
sk_sp<SkVertices> fVertices;
@ -158,6 +166,8 @@ public:
int indexCount() const { return fIndexCnt; }
const uint16_t* indices() const { return fIndices; }
bool isVolatile() const { return fIsVolatile; }
// returns approximate byte size of the vertices object
size_t approximateSize() const;
@ -199,6 +209,8 @@ private:
int fVertexCnt;
int fIndexCnt;
bool fIsVolatile;
VertexMode fMode;
// below here is where the actual array data is stored.
};

View File

@ -2684,7 +2684,7 @@ the result with <a href='#Matrix'>Matrix</a>.
### Example
<div><fiddle-embed name="53c212c4f2449df0b0eedbc6227b6ab7"><div><a href='#SkCanvas_scale'>scale</a> followed by <a href='#SkCanvas_translate'>translate</a> produces different results from <a href='#SkCanvas_translate'>translate</a> followed
<div><fiddle-embed name="eb93d5fa66a5f7a10f4f9210494d7222"><div><a href='#SkCanvas_scale'>scale</a> followed by <a href='#SkCanvas_translate'>translate</a> produces different results from <a href='#SkCanvas_translate'>translate</a> followed
by <a href='#SkCanvas_scale'>scale</a>.
The blue stroke follows translate of (50, 50); a black
@ -4662,7 +4662,7 @@ and so on; or nullptr</td>
### Example
<div><fiddle-embed name="185746dc0faa6f1df30c4afe098646ff"></fiddle-embed></div>
<div><fiddle-embed name="ac93f30dff13f8a8bb31398de370863b"></fiddle-embed></div>
### See Also
@ -6412,7 +6412,7 @@ void <a href='#SkCanvas_drawPicture'>drawPicture</a>(const <a href='SkPicture_Re
### Example
<div><fiddle-embed name="9adda80b2dd1b08ec5ccf66da7c8bd91"></fiddle-embed></div>
<div><fiddle-embed name="759e4e5bac680838added8f70884dcdc"></fiddle-embed></div>
### See Also
@ -6537,6 +6537,7 @@ contains <a href='undocumented#Shader'>Shader</a>, <a href='SkBlendMode_Referenc
The first element of <a href='#SkCanvas_drawVertices_3_bones'>bones</a> should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
<a href='#SkCanvas_drawVertices_3_boneCount'>boneCount</a> must be at most 100, and thus the size of <a href='#SkCanvas_drawVertices_3_bones'>bones</a> should be at most 100.
### Parameters
@ -6581,6 +6582,7 @@ contains <a href='undocumented#Shader'>Shader</a>, <a href='SkBlendMode_Referenc
The first element of <a href='#SkCanvas_drawVertices_4_bones'>bones</a> should be an object to world space transformation matrix that
will be applied before performing mesh deformations. If no such transformation is needed,
it should be the identity matrix.
<a href='#SkCanvas_drawVertices_4_boneCount'>boneCount</a> must be at most 100, and thus the size of <a href='#SkCanvas_drawVertices_4_bones'>bones</a> should be at most 100.
### Parameters
@ -6773,7 +6775,7 @@ If <a href='#SkCanvas_drawAtlas_cullRect'>cullRect</a> is outside of <a href='#C
### Example
<div><fiddle-embed name="fe79a9c1ec350264eb9c7b2509dd3638"></fiddle-embed></div>
<div><fiddle-embed name="1df575f9b8132306ce0552a2554ed132"></fiddle-embed></div>
### See Also

View File

@ -1504,7 +1504,7 @@ If <a href='#SkIRect_makeInset_dy'>dy</a> is positive, <a href='#IRect'>IRect</a
### Example
<div><fiddle-embed name="a7958a4e0668f5cf805a8e78eb57f51d">
<div><fiddle-embed name="1db94b2c76e0a7a71856532335fa56b6">
#### Example Output
@ -2277,7 +2277,7 @@ true if <a href='#SkIRect_intersectNoEmptyCheck_a'>a</a> and <a href='#SkIRect_i
### Example
<div><fiddle-embed name="a98993a66616ae406d8bdc54adfb1411">
<div><fiddle-embed name="d35fbc9fdea71df8b8a12fd3da50d11c">
#### Example Output

View File

@ -2035,7 +2035,7 @@ integral rectangle from origin to <a href='#SkImageInfo_width'>width</a> and <a
### Example
<div><fiddle-embed name="a818be8945cd0c18f99ffe53e90afa48"></fiddle-embed></div>
<div><fiddle-embed name="3ce3db36235d80dbac4d39504cf756da"></fiddle-embed></div>
### See Also

View File

@ -2300,7 +2300,7 @@ partial or full <a href='#Image'>Image</a>, or nullptr
### Example
<div><fiddle-embed name="4eb2d95c9e9a66f05296e345bb68bd51"></fiddle-embed></div>
<div><fiddle-embed name="93669037c9eb9d142e7776b9f936fa96"></fiddle-embed></div>
### See Also

View File

@ -3203,7 +3203,7 @@ Matrix * S(sx, sy) = | D E F | | 0 sy 0 | = | D*sx E*sy F |
### Example
<div><fiddle-embed name="27a0ab44659201f1aa2ac7fea73368c2"></fiddle-embed></div>
<div><fiddle-embed name="3edbdea8e43d06086abf33ec4a9b415b"></fiddle-embed></div>
### See Also

View File

@ -2609,7 +2609,7 @@ Sets <a href='SkColor_Reference#Alpha'>Alpha</a> and RGB used when stroking and
### Example
<div><fiddle-embed name="9adda80b2dd1b08ec5ccf66da7c8bd91">
<div><fiddle-embed name="6e70f18300bd676a3c056ceb6b62f8df">
#### Example Output
@ -4477,7 +4477,7 @@ typographic height of text
### Example
<div><fiddle-embed name="29b98ebf58aa9fd1edfaabf9f4490b3a"></fiddle-embed></div>
<div><fiddle-embed name="983e2a71ba72d4ba8c945420040b8f1c"></fiddle-embed></div>
---

View File

@ -5570,7 +5570,7 @@ The format used for <a href='#Path'>Path</a> in memory is not guaranteed.
### Example
<div><fiddle-embed name="882e8e0103048009a25cfc20400492f7">
<div><fiddle-embed name="2c6aff73608cd198659db6d1eeaaae4f">
#### Example Output
@ -5753,7 +5753,7 @@ degenerate data.
### Example
<div><fiddle-embed name="882e8e0103048009a25cfc20400492f7"><div>Ignoring the actual <a href='#Verb'>Verbs</a> and replacing them with <a href='#Quad'>Quads</a> rounds the
<div><fiddle-embed name="2f53df9201769ab7e7c0e164a1334309"><div>Ignoring the actual <a href='#Verb'>Verbs</a> and replacing them with <a href='#Quad'>Quads</a> rounds the
path of the glyph.
</div></fiddle-embed></div>

View File

@ -492,7 +492,7 @@ reference to ImageInfo
### Example
<div><fiddle-embed name="ab9b3aef7896aee80b780789848fbba4">
<div><fiddle-embed name="6e0f558bf7fabc655041116288559134">
#### Example Output
@ -1109,7 +1109,7 @@ readable unsigned 8-bit pointer to pixels
### Example
<div><fiddle-embed name="2bffb6384cc20077e632e7d01da045ca">
<div><fiddle-embed name="9adda80b2dd1b08ec5ccf66da7c8bd91">
#### Example Output

View File

@ -1207,7 +1207,7 @@ Returns <a href='#Point'>Point</a> multiplied by scale.
### Example
<div><fiddle-embed name="2bffb6384cc20077e632e7d01da045ca"></fiddle-embed></div>
<div><fiddle-embed name="35b3bc675779de043706ae4817ee950c"></fiddle-embed></div>
### See Also

View File

@ -626,7 +626,7 @@ true if has identical geometry to <a href='undocumented#Oval'>Oval</a>
### Example
<div><fiddle-embed name="6ac569e40fb68c758319e85428b9ae95"><div>The first radii are scaled down proportionately until both x-axis and y-axis fit
<div><fiddle-embed name="ab9b3aef7896aee80b780789848fbba4"><div>The first radii are scaled down proportionately until both x-axis and y-axis fit
within the bounds. After scaling, x-axis radius is smaller than half the width;
left round rect is not an oval. The second radii are equal to half the
dimensions; right round rect is an oval.
@ -1042,7 +1042,7 @@ Otherwise, sets to <a href='#SkRRect_kSimple_Type'>kSimple Type</a>.
### Example
<div><fiddle-embed name="a7958a4e0668f5cf805a8e78eb57f51d"></fiddle-embed></div>
<div><fiddle-embed name="6ac569e40fb68c758319e85428b9ae95"></fiddle-embed></div>
### See Also
@ -1292,7 +1292,7 @@ bounding box
### Example
<div><fiddle-embed name="a7958a4e0668f5cf805a8e78eb57f51d"></fiddle-embed></div>
<div><fiddle-embed name="4577e2dcb086b241bb43d8b89ee0b0dd"></fiddle-embed></div>
### See Also

View File

@ -1171,7 +1171,7 @@ and <a href='#SkRect_sort'>sort</a> to reverse <a href='#SkRect_fTop'>fTop</a> a
### Example
<div><fiddle-embed name="795061764b10c9e05efb466c9cb60644">
<div><fiddle-embed name="a98993a66616ae406d8bdc54adfb1411">
#### Example Output

View File

@ -1014,7 +1014,7 @@ has no effect. Calling <a href='#SkSurface_makeImageSnapshot'>makeImageSnapshot<
### Example
<div><fiddle-embed name="8b8a4cd8a29d22bb9c5e63b70357bd65">
<div><fiddle-embed name="99a54b814ccab7d2b1143c88581649ff">
#### Example Output

View File

@ -4765,7 +4765,7 @@
"code": "void draw(SkCanvas* canvas) {\n // sk_sp<SkImage> image;\n SkImage* imagePtr = image.get();\n canvas->drawImage(imagePtr, 0, 0);\n SkPaint paint;\n canvas->drawImage(imagePtr, 80, 0, &paint);\n paint.setAlpha(0x80);\n canvas->drawImage(imagePtr, 160, 0, &paint);\n}\n",
"width": 256,
"height": 64,
"hash": "185746dc0faa6f1df30c4afe098646ff",
"hash": "ac93f30dff13f8a8bb31398de370863b",
"file": "SkCanvas_Reference",
"name": "SkCanvas::drawImage"
},
@ -5469,7 +5469,7 @@
"code": "void draw(SkCanvas* canvas) {\n canvas->scale(.5f, .5f);\n SkImageInfo imageInfo = source.info();\n SkIRect bounds = imageInfo.bounds();\n for (int x : { 0, bounds.width() } ) {\n for (int y : { 0, bounds.height() } ) {\n canvas->drawBitmap(source, x, y);\n }\n }\n}",
"width": 256,
"height": 64,
"hash": "a818be8945cd0c18f99ffe53e90afa48",
"hash": "3ce3db36235d80dbac4d39504cf756da",
"file": "SkImageInfo_Reference",
"name": "SkImageInfo::bounds()"
},

View File

@ -874,7 +874,8 @@ bool SkBlurMaskFilterImpl::directFilterRRectMaskGPU(GrContext* context,
}
paint.addCoverageFragmentProcessor(std::move(fp));
renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices));
renderTargetContext->drawVertices(clip, std::move(paint), viewMatrix, std::move(vertices),
nullptr, 0);
} else {
SkMatrix inverse;
if (!viewMatrix.invert(&inverse)) {

View File

@ -1715,6 +1715,7 @@ void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, const SkMatrix* b
SkBlendMode mode, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
RETURN_ON_NULL(vertices);
SkASSERT(boneCount <= 100);
this->onDrawVerticesObject(vertices.get(), bones, boneCount, mode, paint);
}
@ -1722,6 +1723,7 @@ void SkCanvas::drawVertices(const SkVertices* vertices, const SkMatrix* bones, i
SkBlendMode mode, const SkPaint& paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
RETURN_ON_NULL(vertices);
SkASSERT(boneCount <= 100);
this->onDrawVerticesObject(vertices, bones, boneCount, mode, paint);
}

View File

@ -95,16 +95,17 @@ SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
bool hasTexs = SkToBool(builderFlags & SkVertices::kHasTexCoords_BuilderFlag);
bool hasColors = SkToBool(builderFlags & SkVertices::kHasColors_BuilderFlag);
bool hasBones = SkToBool(builderFlags & SkVertices::kHasBones_BuilderFlag);
this->init(mode, vertexCount, indexCount,
bool isVolatile = SkToBool(builderFlags & SkVertices::kIsVolatile_BuilderFlag);
this->init(mode, vertexCount, indexCount, isVolatile,
SkVertices::Sizes(mode, vertexCount, indexCount, hasTexs, hasColors, hasBones));
}
SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount,
SkVertices::Builder::Builder(VertexMode mode, int vertexCount, int indexCount, bool isVolatile,
const SkVertices::Sizes& sizes) {
this->init(mode, vertexCount, indexCount, sizes);
this->init(mode, vertexCount, indexCount, isVolatile, sizes);
}
void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount,
void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount, bool isVolatile,
const SkVertices::Sizes& sizes) {
if (!sizes.isValid()) {
return; // fVertices will already be null
@ -128,6 +129,7 @@ void SkVertices::Builder::init(VertexMode mode, int vertexCount, int indexCount,
fVertices->fIndices = sizes.fISize ? (uint16_t*)ptr : nullptr;
fVertices->fVertexCnt = vertexCount;
fVertices->fIndexCnt = indexCount;
fVertices->fIsVolatile = isVolatile;
fVertices->fMode = mode;
// We defer assigning fBounds and fUniqueID until detach() is called
@ -171,6 +173,10 @@ int SkVertices::Builder::indexCount() const {
return fVertices ? fVertices->indexCount() : 0;
}
bool SkVertices::Builder::isVolatile() const {
return fVertices ? fVertices->isVolatile() : false;
}
SkPoint* SkVertices::Builder::positions() {
return fVertices ? const_cast<SkPoint*>(fVertices->positions()) : nullptr;
}
@ -208,7 +214,8 @@ sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
const SkColor colors[],
const BoneIndices boneIndices[],
const BoneWeights boneWeights[],
int indexCount, const uint16_t indices[]) {
int indexCount, const uint16_t indices[],
bool isVolatile) {
SkASSERT((!boneIndices && !boneWeights) || (boneIndices && boneWeights));
Sizes sizes(mode,
vertexCount,
@ -220,7 +227,7 @@ sk_sp<SkVertices> SkVertices::MakeCopy(VertexMode mode, int vertexCount,
return nullptr;
}
Builder builder(mode, vertexCount, indexCount, sizes);
Builder builder(mode, vertexCount, indexCount, isVolatile, sizes);
SkASSERT(builder.isValid());
sk_careful_memcpy(builder.positions(), pos, sizes.fVSize);
@ -247,15 +254,15 @@ size_t SkVertices::approximateSize() const {
///////////////////////////////////////////////////////////////////////////////////////////////////
// storage = packed | vertex_count | index_count | pos[] | texs[] | colors[] | boneIndices[] |
// boneWeights[] | indices[]
// storage = packed | vertex_count | index_count | is_volatile | pos[] | texs[] | colors[] |
// boneIndices[] | boneWeights[] | indices[]
// = header + arrays
#define kMode_Mask 0x0FF
#define kHasTexs_Mask 0x100
#define kHasColors_Mask 0x200
#define kHasBones_Mask 0x400
#define kHeaderSize (3 * sizeof(uint32_t))
#define kHeaderSize (4 * sizeof(uint32_t))
sk_sp<SkData> SkVertices::encode() const {
// packed has room for addtional flags in the future (e.g. versioning)
@ -288,6 +295,7 @@ sk_sp<SkData> SkVertices::encode() const {
writer.write32(packed);
writer.write32(fVertexCnt);
writer.write32(fIndexCnt);
writer.writeBool(fIsVolatile);
writer.write(fPositions, sizes.fVSize);
writer.write(fTexs, sizes.fTSize);
writer.write(fColors, sizes.fCSize);
@ -310,6 +318,7 @@ sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
const uint32_t packed = reader.readInt();
const int vertexCount = safe.checkGE(reader.readInt(), 0);
const int indexCount = safe.checkGE(reader.readInt(), 0);
const bool isVolatile = reader.readBool();
const VertexMode mode = safe.checkLE<VertexMode>(packed & kMode_Mask,
SkVertices::kLast_VertexMode);
if (!safe) {
@ -327,7 +336,7 @@ sk_sp<SkVertices> SkVertices::Decode(const void* data, size_t length) {
return nullptr;
}
Builder builder(mode, vertexCount, indexCount, sizes);
Builder builder(mode, vertexCount, indexCount, isVolatile, sizes);
reader.read(builder.positions(), sizes.fVSize);
reader.read(builder.texCoords(), sizes.fTSize);

View File

@ -27,8 +27,13 @@ enum GPFlag {
kColorAttributeIsSkColor_GPFlag = 0x2,
kLocalCoordAttribute_GPFlag = 0x4,
kCoverageAttribute_GPFlag = 0x8,
kBonesAttribute_GPFlag = 0x10,
};
static constexpr int kNumFloatsPerSkMatrix = 9;
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 +42,12 @@ public:
const SkMatrix& viewMatrix,
const SkMatrix& localMatrix,
bool localCoordsWillBeRead,
uint8_t coverage) {
uint8_t coverage,
const SkMatrix 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 +59,9 @@ public:
bool localCoordsWillBeRead() const { return fLocalCoordsWillBeRead; }
uint8_t coverage() const { return fCoverage; }
bool hasVertexCoverage() const { return fInCoverage.isInitialized(); }
const float* bones() const { return fBones.data(); }
int boneCount() const { return fBones.size() / kNumFloatsPerSkMatrix; }
bool hasBones() const { return fBones.size() > 0; }
class GLSLProcessor : public GrGLSLGeometryProcessor {
public:
@ -99,11 +109,37 @@ public:
&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 +183,8 @@ public:
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 +216,10 @@ public:
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 +229,7 @@ public:
UniformHandle fViewMatrixUniform;
UniformHandle fColorUniform;
UniformHandle fCoverageUniform;
UniformHandle fBonesUniform;
GrGLSLColorSpaceXformHelper fColorSpaceHelper;
typedef GrGLSLGeometryProcessor INHERITED;
@ -209,7 +250,9 @@ private:
const SkMatrix& viewMatrix,
const SkMatrix& localMatrix,
uint8_t coverage,
bool localCoordsWillBeRead)
bool localCoordsWillBeRead,
const SkMatrix* bones,
int boneCount)
: INHERITED(kDefaultGeoProc_ClassID)
, fColor(color)
, fViewMatrix(viewMatrix)
@ -217,7 +260,8 @@ private:
, fCoverage(coverage)
, fFlags(gpTypeFlags)
, fLocalCoordsWillBeRead(localCoordsWillBeRead)
, fColorSpaceXform(std::move(colorSpaceXform)) {
, fColorSpaceXform(std::move(colorSpaceXform))
, fBones() {
fInPosition = {"inPosition", kFloat2_GrVertexAttribType};
int cnt = 1;
if (fFlags & kColorAttribute_GPFlag) {
@ -232,17 +276,49 @@ private:
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);
// Set bone data
if (bones) {
fBones.reserve(boneCount * kNumFloatsPerSkMatrix);
for (int i = 0; i < boneCount; i ++) {
const SkMatrix& matrix = bones[i];
fBones.push_back(matrix.get(SkMatrix::kMScaleX));
fBones.push_back(matrix.get(SkMatrix::kMSkewY));
fBones.push_back(matrix.get(SkMatrix::kMPersp0));
fBones.push_back(matrix.get(SkMatrix::kMSkewX));
fBones.push_back(matrix.get(SkMatrix::kMScaleY));
fBones.push_back(matrix.get(SkMatrix::kMPersp1));
fBones.push_back(matrix.get(SkMatrix::kMTransX));
fBones.push_back(matrix.get(SkMatrix::kMTransY));
fBones.push_back(matrix.get(SkMatrix::kMPersp2));
}
}
}
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 +326,7 @@ private:
uint32_t fFlags;
bool fLocalCoordsWillBeRead;
sk_sp<GrColorSpaceXform> fColorSpaceXform;
std::vector<float> fBones;
GR_DECLARE_GEOMETRY_PROCESSOR_TEST
@ -273,6 +350,17 @@ sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
if (d->fRandom->nextBool()) {
flags |= kLocalCoordAttribute_GPFlag;
}
if (d->fRandom->nextBool()) {
flags |= kBonesAttribute_GPFlag;
}
int numBones = d->fRandom->nextRangeU(0, kMaxBones);
std::vector<SkMatrix> bones(numBones);
for (int i = 0; i < numBones; i++) {
for (int j = 0; j < kNumFloatsPerSkMatrix; j++) {
bones[i][j] = d->fRandom->nextF();
}
}
return DefaultGeoProc::Make(flags,
GrRandomColor(d->fRandom),
@ -280,7 +368,9 @@ sk_sp<GrGeometryProcessor> DefaultGeoProc::TestCreate(GrProcessorTestData* d) {
GrTest::TestMatrix(d->fRandom),
GrTest::TestMatrix(d->fRandom),
d->fRandom->nextBool(),
GrRandomCoverage(d->fRandom));
GrRandomCoverage(d->fRandom),
bones.data(),
numBones);
}
#endif
@ -307,7 +397,9 @@ sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::Make(const Color& color,
viewMatrix,
localCoords.fMatrix ? *localCoords.fMatrix : SkMatrix::I(),
localCoordsWillBeRead,
inCoverage);
inCoverage,
nullptr,
0);
}
sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
@ -330,3 +422,33 @@ sk_sp<GrGeometryProcessor> GrDefaultGeoProcFactory::MakeForDeviceSpace(
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);
}

View File

@ -54,7 +54,7 @@ namespace GrDefaultGeoProcFactory {
SkPoint fLocalCoord;
};
struct PositionColorLocalCoordCoverage {
struct PositionColorLocalCoordCoverageAttr {
SkPoint fPosition;
GrColor fColor;
SkPoint fLocalCoord;
@ -118,6 +118,15 @@ namespace GrDefaultGeoProcFactory {
const SkMatrix* fMatrix;
};
struct Bones {
Bones(const SkMatrix bones[], int boneCount)
: fBones(bones)
, fBoneCount(boneCount) {}
const SkMatrix* fBones;
int fBoneCount;
};
sk_sp<GrGeometryProcessor> Make(const Color&,
const Coverage&,
const LocalCoords&,
@ -132,6 +141,17 @@ namespace GrDefaultGeoProcFactory {
const Coverage&,
const LocalCoords&,
const SkMatrix& viewMatrix);
/*
* Use this factory to create a GrGeometryProcessor that supports skeletal animation through
* deformation of vertices using matrices that are passed in. This should only be called from
* GrDrawVerticesOp.
*/
sk_sp<GrGeometryProcessor> MakeWithBones(const Color&,
const Coverage&,
const LocalCoords&,
const Bones&,
const SkMatrix& viewMatrix);
};
#endif

View File

@ -844,6 +844,8 @@ void GrRenderTargetContext::drawVertices(const GrClip& clip,
GrPaint&& paint,
const SkMatrix& viewMatrix,
sk_sp<SkVertices> vertices,
const SkMatrix bones[],
int boneCount,
GrPrimitiveType* overridePrimType) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED
@ -855,7 +857,7 @@ void GrRenderTargetContext::drawVertices(const GrClip& clip,
SkASSERT(vertices);
GrAAType aaType = this->chooseAAType(GrAA::kNo, GrAllowMixedSamples::kNo);
std::unique_ptr<GrDrawOp> op = GrDrawVerticesOp::Make(
fContext, std::move(paint), std::move(vertices), viewMatrix, aaType,
fContext, std::move(paint), std::move(vertices), bones, boneCount, viewMatrix, aaType,
this->colorSpaceInfo().refColorSpaceXformFromSRGB(), overridePrimType);
this->addDrawOp(clip, std::move(op));
}

View File

@ -218,12 +218,16 @@ public:
* @param paint describes how to color pixels.
* @param viewMatrix transformation matrix
* @param vertices specifies the mesh to draw.
* @param bones bone deformation matrices.
* @param boneCount number of bone matrices.
* @param overridePrimType primitive type to draw. If NULL, derive prim type from vertices.
*/
void drawVertices(const GrClip&,
GrPaint&& paint,
const SkMatrix& viewMatrix,
sk_sp<SkVertices> vertices,
const SkMatrix bones[],
int boneCount,
GrPrimitiveType* overridePrimType = nullptr);
/**

View File

@ -365,7 +365,7 @@ void SkGpuDevice::drawPoints(SkCanvas::PointMode mode,
nullptr);
fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), *viewMatrix,
std::move(vertices), &primitiveType);
std::move(vertices), nullptr, 0, &primitiveType);
}
///////////////////////////////////////////////////////////////////////////////
@ -1480,7 +1480,9 @@ static bool init_vertices_paint(GrContext* context, const GrColorSpaceInfo& colo
}
void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCount,
const SkPoint vertices[], SkBlendMode bmode,
const SkPoint vertices[],
const SkMatrix bones[], int boneCount,
SkBlendMode bmode,
const uint16_t indices[], int indexCount,
const SkPaint& paint) {
ASSERT_SINGLE_OWNER
@ -1538,12 +1540,13 @@ void SkGpuDevice::wireframeVertices(SkVertices::VertexMode vmode, int vertexCoun
std::move(grPaint),
this->ctm(),
builder.detach(),
bones,
boneCount,
&primitiveType);
}
void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, int boneCount,
void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix bones[], int boneCount,
SkBlendMode mode, const SkPaint& paint) {
// TODO: GPU ANIMATION
ASSERT_SINGLE_OWNER
GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawVertices", fContext.get());
@ -1554,7 +1557,8 @@ void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones
if ((!hasTexs || !paint.getShader()) && !hasColors) {
// The dreaded wireframe mode. Fallback to drawVertices and go so slooooooow.
this->wireframeVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(),
mode, vertices->indices(), vertices->indexCount(), paint);
bones, boneCount, mode, vertices->indices(), vertices->indexCount(),
paint);
return;
}
if (!init_vertices_paint(fContext.get(), fRenderTargetContext->colorSpaceInfo(), paint,
@ -1562,7 +1566,8 @@ void SkGpuDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones
return;
}
fRenderTargetContext->drawVertices(this->clip(), std::move(grPaint), this->ctm(),
sk_ref_sp(const_cast<SkVertices*>(vertices)));
sk_ref_sp(const_cast<SkVertices*>(vertices)),
bones, boneCount);
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -91,7 +91,7 @@ public:
int scalarsPerPos, const SkPoint& offset, const SkPaint&) override;
void drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y,
const SkPaint& paint, SkDrawFilter* drawFilter) override;
void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
void drawVertices(const SkVertices*, const SkMatrix bones[], int boneCount, SkBlendMode,
const SkPaint&) override;
void drawShadow(const SkPath&, const SkDrawShadowRec&) override;
void drawAtlas(const SkImage* atlas, const SkRSXform[], const SkRect[],
@ -248,7 +248,8 @@ private:
void drawStrokedLine(const SkPoint pts[2], const SkPaint&);
void wireframeVertices(SkVertices::VertexMode, int vertexCount, const SkPoint verts[],
SkBlendMode, const uint16_t indices[], int indexCount, const SkPaint&);
const SkMatrix bones[], int boneCount, SkBlendMode,
const uint16_t indices[], int indexCount, const SkPaint&);
static sk_sp<GrRenderTargetContext> MakeRenderTargetContext(GrContext*,
SkBudgeted,

View File

@ -10,10 +10,13 @@
#include "GrDefaultGeoProcFactory.h"
#include "GrOpFlushState.h"
#include "SkGr.h"
#include "SkRectPriv.h"
std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrContext* context,
GrPaint&& paint,
sk_sp<SkVertices> vertices,
const SkMatrix bones[],
int boneCount,
const SkMatrix& viewMatrix,
GrAAType aaType,
sk_sp<GrColorSpaceXform> colorSpaceXform,
@ -22,13 +25,14 @@ std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrContext* context,
GrPrimitiveType primType = overridePrimType ? *overridePrimType
: SkVertexModeToGrPrimitiveType(vertices->mode());
return Helper::FactoryHelper<GrDrawVerticesOp>(context, std::move(paint), std::move(vertices),
primType, aaType, std::move(colorSpaceXform),
viewMatrix);
bones, boneCount, primType, aaType,
std::move(colorSpaceXform), viewMatrix);
}
GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color,
sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType,
GrAAType aaType, sk_sp<GrColorSpaceXform> colorSpaceXform,
sk_sp<SkVertices> vertices, const SkMatrix bones[],
int boneCount, GrPrimitiveType primitiveType, GrAAType aaType,
sk_sp<GrColorSpaceXform> colorSpaceXform,
const SkMatrix& viewMatrix)
: INHERITED(ClassID())
, fHelper(helperArgs, aaType)
@ -45,15 +49,28 @@ GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor c
mesh.fColor = color;
mesh.fViewMatrix = viewMatrix;
mesh.fVertices = std::move(vertices);
if (bones) {
mesh.fBones.assign(bones, bones + boneCount);
}
mesh.fIgnoreTexCoords = false;
mesh.fIgnoreColors = false;
mesh.fIgnoreBones = false;
fFlags = 0;
if (mesh.hasPerVertexColors()) {
fFlags |= kRequiresPerVertexColors_Flag;
}
if (mesh.hasExplicitLocalCoords()) {
fFlags |= kAnyMeshHasExplicitLocalCoords;
fFlags |= kAnyMeshHasExplicitLocalCoords_Flag;
}
if (mesh.hasBones()) {
fFlags |= kHasBones_Flag;
}
// 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]);
}
IsZeroArea zeroArea;
@ -62,7 +79,27 @@ GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor c
} else {
zeroArea = IsZeroArea::kNo;
}
this->setTransformedBounds(mesh.fVertices->bounds(), viewMatrix, HasAABloat::kNo, zeroArea);
if (this->hasBones()) {
// We don't know the bounds if there are deformations involved, so attempt to calculate
// the maximum possible.
SkRect bounds;
const SkRect& originalBounds = bones[0].mapRect(mesh.fVertices->bounds());
for (int i = 1; i < boneCount; i++) {
const SkMatrix& matrix = bones[i];
bounds.join(matrix.mapRect(originalBounds));
}
this->setTransformedBounds(bounds,
mesh.fViewMatrix,
HasAABloat::kNo,
zeroArea);
} else {
this->setTransformedBounds(mesh.fVertices->bounds(),
mesh.fViewMatrix,
HasAABloat::kNo,
zeroArea);
}
}
SkString GrDrawVerticesOp::dumpInfo() const {
@ -96,13 +133,14 @@ GrDrawOp::RequiresDstTexture GrDrawVerticesOp::finalize(const GrCaps& caps,
}
if (!fHelper.usesLocalCoords()) {
fMeshes[0].fIgnoreTexCoords = true;
fFlags &= ~kAnyMeshHasExplicitLocalCoords;
fFlags &= ~kAnyMeshHasExplicitLocalCoords_Flag;
}
return result;
}
sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
bool* hasLocalCoordAttribute) const {
bool* hasLocalCoordAttribute,
bool* hasBoneAttribute) const {
using namespace GrDefaultGeoProcFactory;
LocalCoords::Type localCoordsType;
if (fHelper.usesLocalCoords()) {
@ -132,26 +170,53 @@ sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
} else {
*hasColorAttribute = false;
};
const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
return GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType, vm);
Bones bones(fMeshes[0].fBones.data(), fMeshes[0].fBones.size());
*hasBoneAttribute = this->hasBones();
if (this->hasBones()) {
return GrDefaultGeoProcFactory::MakeWithBones(color,
Coverage::kSolid_Type,
localCoordsType,
bones,
vm);
} else {
return GrDefaultGeoProcFactory::Make(color,
Coverage::kSolid_Type,
localCoordsType,
vm);
}
}
void GrDrawVerticesOp::onPrepareDraws(Target* target) {
if (fMeshes[0].fVertices->isVolatile()) {
this->drawVolatile(target);
} else {
this->drawNonVolatile(target);
}
}
void GrDrawVerticesOp::drawVolatile(Target* target) {
bool hasColorAttribute;
bool hasLocalCoordsAttribute;
sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute, &hasLocalCoordsAttribute);
bool hasBoneAttribute;
sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute,
&hasLocalCoordsAttribute,
&hasBoneAttribute);
size_t vertexStride = sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
(hasLocalCoordsAttribute ? sizeof(SkPoint) : 0);
// Calculate the stride.
size_t vertexStride = sizeof(SkPoint) +
(hasColorAttribute ? sizeof(uint32_t) : 0) +
(hasLocalCoordsAttribute ? sizeof(SkPoint) : 0) +
(hasBoneAttribute ? 4 * (sizeof(uint32_t) + sizeof(float)) : 0);
SkASSERT(vertexStride == gp->debugOnly_vertexStride());
int instanceCount = fMeshes.count();
const GrBuffer* vertexBuffer;
int firstVertex;
// Allocate buffers.
const GrBuffer* vertexBuffer = nullptr;
int firstVertex = 0;
void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
if (!verts) {
SkDebugf("Could not allocate vertices\n");
return;
@ -159,37 +224,157 @@ void GrDrawVerticesOp::onPrepareDraws(Target* target) {
const GrBuffer* indexBuffer = nullptr;
int firstIndex = 0;
uint16_t* indices = nullptr;
if (this->isIndexed()) {
indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
if (!indices) {
SkDebugf("Could not allocate indices\n");
return;
}
}
// Fill the buffers.
this->fillBuffers(hasColorAttribute,
hasLocalCoordsAttribute,
hasBoneAttribute,
vertexStride,
verts,
indices);
// Draw the vertices.
this->drawVertices(target, gp.get(), vertexBuffer, firstVertex, indexBuffer, firstIndex);
}
void GrDrawVerticesOp::drawNonVolatile(Target* target) {
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
bool hasColorAttribute;
bool hasLocalCoordsAttribute;
bool hasBoneAttribute;
sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute,
&hasLocalCoordsAttribute,
&hasBoneAttribute);
int instanceCount = fMeshes.count();
SkASSERT(instanceCount == 1); // Non-volatile meshes should never combine.
// Get the resource provider.
GrResourceProvider* rp = target->resourceProvider();
// Generate keys for the buffers.
GrUniqueKey vertexKey, indexKey;
GrUniqueKey::Builder vertexKeyBuilder(&vertexKey, kDomain, instanceCount + 1);
GrUniqueKey::Builder indexKeyBuilder(&indexKey, kDomain, instanceCount + 1);
for (int i = 0; i < instanceCount; i ++) {
vertexKeyBuilder[i] = indexKeyBuilder[i] = fMeshes[i].fVertices->uniqueID();
}
vertexKeyBuilder[instanceCount] = 0;
indexKeyBuilder[instanceCount] = 1;
vertexKeyBuilder.finish();
indexKeyBuilder.finish();
// Try to grab data from the cache.
sk_sp<GrBuffer> vertexBuffer = rp->findByUniqueKey<GrBuffer>(vertexKey);
sk_sp<GrBuffer> indexBuffer = rp->findByUniqueKey<GrBuffer>(indexKey);
// Draw using the cached buffers if possible.
if (vertexBuffer) {
this->drawVertices(target, gp.get(), vertexBuffer.get(), 0, indexBuffer.get(), 0);
return;
}
// Calculate the stride.
size_t vertexStride = sizeof(SkPoint) +
(hasColorAttribute ? sizeof(uint32_t) : 0) +
(hasLocalCoordsAttribute ? sizeof(SkPoint) : 0) +
(hasBoneAttribute ? 4 * (sizeof(uint32_t) + sizeof(float)) : 0);
SkASSERT(vertexStride == gp->debugOnly_vertexStride());
// Allocate vertex buffer.
vertexBuffer.reset(rp->createBuffer(fVertexCount * vertexStride,
kVertex_GrBufferType,
kStatic_GrAccessPattern,
0));
void* verts = vertexBuffer ? vertexBuffer->map() : nullptr;
if (!verts) {
SkDebugf("Could not allocate vertices\n");
return;
}
// Allocate index buffer.
uint16_t* indices = nullptr;
if (this->isIndexed()) {
indexBuffer.reset(rp->createBuffer(fIndexCount * sizeof(uint16_t),
kIndex_GrBufferType,
kStatic_GrAccessPattern,
0));
indices = indexBuffer ? static_cast<uint16_t*>(indexBuffer->map()) : nullptr;
if (!indices) {
SkDebugf("Could not allocate indices\n");
return;
}
}
// Fill the buffers.
this->fillBuffers(hasColorAttribute,
hasLocalCoordsAttribute,
hasBoneAttribute,
vertexStride,
verts,
indices);
// Unmap the buffers.
vertexBuffer->unmap();
if (indexBuffer) {
indexBuffer->unmap();
}
// Cache the buffers.
rp->assignUniqueKeyToResource(vertexKey, vertexBuffer.get());
rp->assignUniqueKeyToResource(indexKey, indexBuffer.get());
// Draw the vertices.
this->drawVertices(target, gp.get(), vertexBuffer.get(), 0, indexBuffer.get(), 0);
}
void GrDrawVerticesOp::fillBuffers(bool hasColorAttribute,
bool hasLocalCoordsAttribute,
bool hasBoneAttribute,
size_t vertexStride,
void* verts,
uint16_t* indices) const {
int instanceCount = fMeshes.count();
// Copy data into the buffers.
int vertexOffset = 0;
// We have a fast case below for uploading the vertex data when the matrix is translate
// only and there are colors but not local coords.
bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute;
// only and there are colors but not local coords. Fast case does not apply when there are bone
// transformations.
bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute && !hasBoneAttribute;
for (int i = 0; i < instanceCount; i++) {
// Get each mesh.
const Mesh& mesh = fMeshes[i];
// Copy data into the index buffer.
if (indices) {
int indexCount = mesh.fVertices->indexCount();
for (int j = 0; j < indexCount; ++j) {
*indices++ = mesh.fVertices->indices()[j] + vertexOffset;
}
}
// Copy data into the vertex buffer.
int vertexCount = mesh.fVertices->vertexCount();
const SkPoint* positions = mesh.fVertices->positions();
const SkColor* colors = mesh.fVertices->colors();
const SkPoint* localCoords = mesh.fVertices->texCoords();
const SkVertices::BoneIndices* boneIndices = mesh.fVertices->boneIndices();
const SkVertices::BoneWeights* boneWeights = mesh.fVertices->boneWeights();
bool fastMesh = (!this->hasMultipleViewMatrices() ||
mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
mesh.hasPerVertexColors();
if (fastAttrs && fastMesh) {
// Fast case.
struct V {
SkPoint fPos;
uint32_t fColor;
@ -207,9 +392,19 @@ void GrDrawVerticesOp::onPrepareDraws(Target* target) {
}
verts = v + vertexCount;
} else {
// Normal case.
static constexpr size_t kColorOffset = sizeof(SkPoint);
size_t localCoordOffset =
hasColorAttribute ? kColorOffset + sizeof(uint32_t) : kColorOffset;
size_t offset = kColorOffset;
if (hasColorAttribute) {
offset += sizeof(uint32_t);
}
size_t localCoordOffset = offset;
if (hasLocalCoordsAttribute) {
offset += sizeof(SkPoint);
}
size_t boneIndexOffset = offset;
offset += 4 * sizeof(uint32_t);
size_t boneWeightOffset = offset;
for (int j = 0; j < vertexCount; ++j) {
if (this->hasMultipleViewMatrices()) {
@ -231,22 +426,40 @@ void GrDrawVerticesOp::onPrepareDraws(Target* target) {
*(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
}
}
if (hasBoneAttribute) {
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];
}
}
verts = (void*)((intptr_t)verts + vertexStride);
}
}
vertexOffset += vertexCount;
}
}
void GrDrawVerticesOp::drawVertices(Target* target,
GrGeometryProcessor* gp,
const GrBuffer* vertexBuffer,
int firstVertex,
const GrBuffer* indexBuffer,
int firstIndex) {
GrMesh mesh(this->primitiveType());
if (!indices) {
mesh.setNonIndexedNonInstanced(fVertexCount);
} else {
mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertexCount - 1,
if (this->isIndexed()) {
mesh.setIndexed(indexBuffer, fIndexCount,
firstIndex, 0, fVertexCount - 1,
GrPrimitiveRestart::kNo);
} else {
mesh.setNonIndexedNonInstanced(fVertexCount);
}
mesh.setVertexData(vertexBuffer, firstVertex);
auto pipe = fHelper.makePipeline(target);
target->draw(gp.get(), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
target->draw(gp, pipe.fPipeline, pipe.fFixedDynamicState, mesh);
}
bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
@ -256,6 +469,20 @@ bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
return false;
}
// Meshes with bones cannot be combined because different meshes use different bones, so to
// combine them, the matrices would have to be combined, and the bone indices on each vertex
// would change, thus making the vertices uncacheable.
if (this->hasBones() || that->hasBones()) {
return false;
}
// Non-volatile meshes cannot batch, because if a non-volatile mesh batches with another mesh,
// then on the next frame, if that non-volatile mesh is drawn, it will draw the other mesh
// that was saved in its vertex buffer, which is not necessarily there anymore.
if (!this->fMeshes[0].fVertices->isVolatile() || !that->fMeshes[0].fVertices->isVolatile()) {
return false;
}
if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
return false;
}
@ -410,8 +637,8 @@ GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp) {
if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
aaType = GrAAType::kMSAA;
}
return GrDrawVerticesOp::Make(context, std::move(paint), std::move(vertices), viewMatrix,
aaType, std::move(colorSpaceXform), &type);
return GrDrawVerticesOp::Make(context, std::move(paint), std::move(vertices), nullptr, 0,
viewMatrix, aaType, std::move(colorSpaceXform), &type);
}
#endif

View File

@ -38,13 +38,15 @@ public:
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
GrPaint&&,
sk_sp<SkVertices>,
const SkMatrix bones[],
int boneCount,
const SkMatrix& viewMatrix,
GrAAType,
sk_sp<GrColorSpaceXform>,
GrPrimitiveType* overridePrimType = nullptr);
GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor, sk_sp<SkVertices>,
GrPrimitiveType, GrAAType, sk_sp<GrColorSpaceXform>,
GrDrawVerticesOp(const Helper::MakeArgs&, GrColor, sk_sp<SkVertices>, const SkMatrix bones[],
int boneCount, GrPrimitiveType, GrAAType, sk_sp<GrColorSpaceXform>,
const SkMatrix& viewMatrix);
const char* name() const override { return "DrawVerticesOp"; }
@ -68,7 +70,26 @@ private:
void onPrepareDraws(Target*) override;
sk_sp<GrGeometryProcessor> makeGP(bool* hasColorAttribute, bool* hasLocalCoordAttribute) const;
void drawVolatile(Target*);
void drawNonVolatile(Target*);
void fillBuffers(bool hasColorAttribute,
bool hasLocalCoordsAttribute,
bool hasBoneAttribute,
size_t vertexStride,
void* verts,
uint16_t* indices) const;
void drawVertices(Target*,
GrGeometryProcessor*,
const GrBuffer* vertexBuffer,
int firstVertex,
const GrBuffer* indexBuffer,
int firstIndex);
sk_sp<GrGeometryProcessor> makeGP(bool* hasColorAttribute,
bool* hasLocalCoordAttribute,
bool* hasBoneAttribute) const;
GrPrimitiveType primitiveType() const { return fPrimitiveType; }
bool combinablePrimitive() const {
@ -82,9 +103,11 @@ private:
struct Mesh {
GrColor fColor; // Used if this->hasPerVertexColors() is false.
sk_sp<SkVertices> fVertices;
std::vector<SkMatrix> fBones;
SkMatrix fViewMatrix;
bool fIgnoreTexCoords;
bool fIgnoreColors;
bool fIgnoreBones;
bool hasExplicitLocalCoords() const {
return fVertices->hasTexCoords() && !fIgnoreTexCoords;
@ -93,6 +116,10 @@ private:
bool hasPerVertexColors() const {
return fVertices->hasColors() && !fIgnoreColors;
}
bool hasBones() const {
return fVertices->hasBones() && fBones.size() && !fIgnoreBones;
}
};
bool isIndexed() const {
@ -105,18 +132,22 @@ private:
}
bool anyMeshHasExplicitLocalCoords() const {
return SkToBool(kAnyMeshHasExplicitLocalCoords & fFlags);
return SkToBool(kAnyMeshHasExplicitLocalCoords_Flag & fFlags);
}
bool hasMultipleViewMatrices() const {
return SkToBool(kHasMultipleViewMatrices_Flag & fFlags);
}
enum Flags {
kRequiresPerVertexColors_Flag = 0x1,
kAnyMeshHasExplicitLocalCoords = 0x2,
kHasMultipleViewMatrices_Flag = 0x4
bool hasBones() const {
return SkToBool(kHasBones_Flag & fFlags);
}
enum Flags {
kRequiresPerVertexColors_Flag = 0x1,
kAnyMeshHasExplicitLocalCoords_Flag = 0x2,
kHasMultipleViewMatrices_Flag = 0x4,
kHasBones_Flag = 0x8,
};
Helper fHelper;

View File

@ -62,7 +62,7 @@ public:
this->updateBones();
// Update the vertices object.
this->updateVerticesObject(false);
this->updateVerticesObject(false, false);
}
void renderBackend(SkCanvas* canvas) {
@ -70,16 +70,13 @@ public:
if (fRenderMode != kBackend_RenderMode) {
fRenderMode = kBackend_RenderMode;
this->updateVertices();
this->updateVerticesObject(false);
this->updateVerticesObject(false, false);
}
canvas->save();
// Update the vertex data.
if (fActorImage->doesAnimationVertexDeform() && fActorImage->isVertexDeformDirty()) {
if (fActorImage->doesAnimationVertexDeform()) {
this->updateVertices();
this->updateVerticesObject(false);
fActorImage->isVertexDeformDirty(false);
this->updateVerticesObject(false, true);
}
// Update the bones.
@ -87,8 +84,6 @@ public:
// Draw the vertices object.
this->drawVerticesObject(canvas, true);
canvas->restore();
}
void renderImmediate(SkCanvas* canvas) {
@ -96,7 +91,7 @@ public:
if (fRenderMode != kImmediate_RenderMode) {
fRenderMode = kImmediate_RenderMode;
this->updateVertices();
this->updateVerticesObject(true);
this->updateVerticesObject(true, true);
}
// Update the vertex data.
@ -106,7 +101,7 @@ public:
}
// Update the vertices object.
this->updateVerticesObject(true);
this->updateVerticesObject(true, true);
// Draw the vertices object.
this->drawVerticesObject(canvas, false);
@ -198,7 +193,7 @@ private:
nima_to_skmatrix(fActorImage->worldTransform().values(), fBones[0]);
}
void updateVerticesObject(bool applyDeforms) {
void updateVerticesObject(bool applyDeforms, bool isVolatile) {
std::vector<SkPoint>* positions = &fPositions;
// Apply deforms if requested.
@ -229,7 +224,8 @@ private:
fBoneIdx.data(),
fBoneWgt.data(),
fIndices.size(),
fIndices.data());
fIndices.data(),
isVolatile);
}
void drawVerticesObject(SkCanvas* canvas, bool useBones) const {
@ -421,11 +417,19 @@ NIMASlide::~NIMASlide() {}
void NIMASlide::draw(SkCanvas* canvas) {
canvas->save();
canvas->translate(500, 500);
canvas->scale(1, -1);
for (int i = 0; i < 10; i ++) {
for (int j = 0; j < 10; j ++) {
canvas->save();
// Render the actor.
fActor->render(canvas, fRenderMode);
canvas->translate(1250 - 250 * i, 1250 - 250 * j);
canvas->scale(0.5, -0.5);
// Render the actor.
fActor->render(canvas, fRenderMode);
canvas->restore();
}
}
canvas->restore();