1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrDrawVerticesOp.h"
9 #include "GrCaps.h"
10 #include "GrDefaultGeoProcFactory.h"
11 #include "GrOpFlushState.h"
12 #include "SkGr.h"
13 #include "SkRectPriv.h"
14 
Make(GrContext * context,GrPaint && paint,sk_sp<SkVertices> vertices,const SkVertices::Bone bones[],int boneCount,const SkMatrix & viewMatrix,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform,GrPrimitiveType * overridePrimType)15 std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrContext* context,
16                                                  GrPaint&& paint,
17                                                  sk_sp<SkVertices> vertices,
18                                                  const SkVertices::Bone bones[],
19                                                  int boneCount,
20                                                  const SkMatrix& viewMatrix,
21                                                  GrAAType aaType,
22                                                  sk_sp<GrColorSpaceXform> colorSpaceXform,
23                                                  GrPrimitiveType* overridePrimType) {
24     SkASSERT(vertices);
25     GrPrimitiveType primType = overridePrimType ? *overridePrimType
26                                                 : SkVertexModeToGrPrimitiveType(vertices->mode());
27     return Helper::FactoryHelper<GrDrawVerticesOp>(context, std::move(paint), std::move(vertices),
28                                                    bones, boneCount, primType, aaType,
29                                                    std::move(colorSpaceXform), viewMatrix);
30 }
31 
GrDrawVerticesOp(const Helper::MakeArgs & helperArgs,const SkPMColor4f & color,sk_sp<SkVertices> vertices,const SkVertices::Bone bones[],int boneCount,GrPrimitiveType primitiveType,GrAAType aaType,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix)32 GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
33                                    sk_sp<SkVertices> vertices, const SkVertices::Bone bones[],
34                                    int boneCount, GrPrimitiveType primitiveType, GrAAType aaType,
35                                    sk_sp<GrColorSpaceXform> colorSpaceXform,
36                                    const SkMatrix& viewMatrix)
37         : INHERITED(ClassID())
38         , fHelper(helperArgs, aaType)
39         , fPrimitiveType(primitiveType)
40         , fColorSpaceXform(std::move(colorSpaceXform)) {
41     SkASSERT(vertices);
42 
43     fVertexCount = vertices->vertexCount();
44     fIndexCount = vertices->indexCount();
45     fColorArrayType = vertices->hasColors() ? ColorArrayType::kSkColor
46                                             : ColorArrayType::kPremulGrColor;
47 
48     Mesh& mesh = fMeshes.push_back();
49     mesh.fColor = color;
50     mesh.fViewMatrix = viewMatrix;
51     mesh.fVertices = std::move(vertices);
52     mesh.fIgnoreTexCoords = false;
53     mesh.fIgnoreColors = false;
54     mesh.fIgnoreBones = false;
55 
56     if (mesh.fVertices->hasBones() && bones) {
57         // Perform the transformations on the CPU instead of the GPU.
58         mesh.fVertices = mesh.fVertices->applyBones(bones, boneCount);
59     } else {
60         if (bones && boneCount > 1) {
61             // NOTE: This should never be used. All bone transforms are being done on the CPU
62             // instead of the GPU.
63 
64             // Copy the bone data.
65             fBones.assign(bones, bones + boneCount);
66         }
67     }
68 
69     fFlags = 0;
70     if (mesh.hasPerVertexColors()) {
71         fFlags |= kRequiresPerVertexColors_Flag;
72     }
73     if (mesh.hasExplicitLocalCoords()) {
74         fFlags |= kAnyMeshHasExplicitLocalCoords_Flag;
75     }
76     if (mesh.hasBones()) {
77         fFlags |= kHasBones_Flag;
78     }
79 
80     // Special case for meshes with a world transform but no bone weights.
81     // These will be considered normal vertices draws without bones.
82     if (!mesh.fVertices->hasBones() && boneCount == 1) {
83         SkMatrix worldTransform;
84         worldTransform.setAffine(bones[0].values);
85         mesh.fViewMatrix.preConcat(worldTransform);
86     }
87 
88     IsZeroArea zeroArea;
89     if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
90         zeroArea = IsZeroArea::kYes;
91     } else {
92         zeroArea = IsZeroArea::kNo;
93     }
94 
95     if (this->hasBones()) {
96         // We don't know the bounds if there are deformations involved, so attempt to calculate
97         // the maximum possible.
98         SkRect bounds = SkRect::MakeEmpty();
99         const SkRect originalBounds = bones[0].mapRect(mesh.fVertices->bounds());
100         for (int i = 1; i < boneCount; i++) {
101             const SkVertices::Bone& matrix = bones[i];
102             bounds.join(matrix.mapRect(originalBounds));
103         }
104 
105         this->setTransformedBounds(bounds,
106                                    mesh.fViewMatrix,
107                                    HasAABloat::kNo,
108                                    zeroArea);
109     } else {
110         this->setTransformedBounds(mesh.fVertices->bounds(),
111                                    mesh.fViewMatrix,
112                                    HasAABloat::kNo,
113                                    zeroArea);
114     }
115 }
116 
117 #ifdef SK_DEBUG
dumpInfo() const118 SkString GrDrawVerticesOp::dumpInfo() const {
119     SkString string;
120     string.appendf("PrimType: %d, MeshCount %d, VCount: %d, ICount: %d\n", (int)fPrimitiveType,
121                    fMeshes.count(), fVertexCount, fIndexCount);
122     string += fHelper.dumpInfo();
123     string += INHERITED::dumpInfo();
124     return string;
125 }
126 #endif
127 
fixedFunctionFlags() const128 GrDrawOp::FixedFunctionFlags GrDrawVerticesOp::fixedFunctionFlags() const {
129     return fHelper.fixedFunctionFlags();
130 }
131 
finalize(const GrCaps & caps,const GrAppliedClip * clip)132 GrProcessorSet::Analysis GrDrawVerticesOp::finalize(const GrCaps& caps, const GrAppliedClip* clip) {
133     GrProcessorAnalysisColor gpColor;
134     if (this->requiresPerVertexColors()) {
135         gpColor.setToUnknown();
136     } else {
137         gpColor.setToConstant(fMeshes.front().fColor);
138     }
139     auto result = fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kNone,
140                                              &gpColor);
141     if (gpColor.isConstant(&fMeshes.front().fColor)) {
142         fMeshes.front().fIgnoreColors = true;
143         fFlags &= ~kRequiresPerVertexColors_Flag;
144         fColorArrayType = ColorArrayType::kPremulGrColor;
145     }
146     if (!fHelper.usesLocalCoords()) {
147         fMeshes[0].fIgnoreTexCoords = true;
148         fFlags &= ~kAnyMeshHasExplicitLocalCoords_Flag;
149     }
150     return result;
151 }
152 
makeGP(const GrShaderCaps * shaderCaps,bool * hasColorAttribute,bool * hasLocalCoordAttribute,bool * hasBoneAttribute) const153 sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(const GrShaderCaps* shaderCaps,
154                                                     bool* hasColorAttribute,
155                                                     bool* hasLocalCoordAttribute,
156                                                     bool* hasBoneAttribute) const {
157     using namespace GrDefaultGeoProcFactory;
158     LocalCoords::Type localCoordsType;
159     if (fHelper.usesLocalCoords()) {
160         // If we have multiple view matrices we will transform the positions into device space. We
161         // must then also provide untransformed positions as local coords.
162         if (this->anyMeshHasExplicitLocalCoords() || this->hasMultipleViewMatrices()) {
163             *hasLocalCoordAttribute = true;
164             localCoordsType = LocalCoords::kHasExplicit_Type;
165         } else {
166             *hasLocalCoordAttribute = false;
167             localCoordsType = LocalCoords::kUsePosition_Type;
168         }
169     } else {
170         localCoordsType = LocalCoords::kUnused_Type;
171         *hasLocalCoordAttribute = false;
172     }
173 
174     Color color(fMeshes[0].fColor);
175     if (this->requiresPerVertexColors()) {
176         if (fColorArrayType == ColorArrayType::kPremulGrColor) {
177             color.fType = Color::kPremulGrColorAttribute_Type;
178         } else {
179             color.fType = Color::kUnpremulSkColorAttribute_Type;
180             color.fColorSpaceXform = fColorSpaceXform;
181         }
182         *hasColorAttribute = true;
183     } else {
184         *hasColorAttribute = false;
185     }
186 
187     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
188 
189     // The bones are packed as 6 floats in column major order, so we can directly upload them to
190     // the GPU as groups of 3 vec2s.
191     Bones bones(reinterpret_cast<const float*>(fBones.data()), fBones.size());
192     *hasBoneAttribute = this->hasBones();
193 
194     if (this->hasBones()) {
195         return GrDefaultGeoProcFactory::MakeWithBones(shaderCaps,
196                                                       color,
197                                                       Coverage::kSolid_Type,
198                                                       localCoordsType,
199                                                       bones,
200                                                       vm);
201     } else {
202         return GrDefaultGeoProcFactory::Make(shaderCaps,
203                                              color,
204                                              Coverage::kSolid_Type,
205                                              localCoordsType,
206                                              vm);
207     }
208 }
209 
onPrepareDraws(Target * target)210 void GrDrawVerticesOp::onPrepareDraws(Target* target) {
211     bool hasMapBufferSupport = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
212     if (fMeshes[0].fVertices->isVolatile() || !hasMapBufferSupport) {
213         this->drawVolatile(target);
214     } else {
215         this->drawNonVolatile(target);
216     }
217 }
218 
drawVolatile(Target * target)219 void GrDrawVerticesOp::drawVolatile(Target* target) {
220     bool hasColorAttribute;
221     bool hasLocalCoordsAttribute;
222     bool hasBoneAttribute;
223     sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
224                                                  &hasColorAttribute,
225                                                  &hasLocalCoordsAttribute,
226                                                  &hasBoneAttribute);
227 
228     // Allocate buffers.
229     size_t vertexStride = gp->vertexStride();
230     sk_sp<const GrBuffer> vertexBuffer = nullptr;
231     int firstVertex = 0;
232     void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
233     if (!verts) {
234         SkDebugf("Could not allocate vertices\n");
235         return;
236     }
237 
238     sk_sp<const GrBuffer> indexBuffer = nullptr;
239     int firstIndex = 0;
240     uint16_t* indices = nullptr;
241     if (this->isIndexed()) {
242         indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
243         if (!indices) {
244             SkDebugf("Could not allocate indices\n");
245             return;
246         }
247     }
248 
249     // Fill the buffers.
250     this->fillBuffers(hasColorAttribute,
251                       hasLocalCoordsAttribute,
252                       hasBoneAttribute,
253                       vertexStride,
254                       verts,
255                       indices);
256 
257     // Draw the vertices.
258     this->drawVertices(target, std::move(gp), std::move(vertexBuffer), firstVertex, indexBuffer,
259                        firstIndex);
260 }
261 
drawNonVolatile(Target * target)262 void GrDrawVerticesOp::drawNonVolatile(Target* target) {
263     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
264 
265     bool hasColorAttribute;
266     bool hasLocalCoordsAttribute;
267     bool hasBoneAttribute;
268     sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
269                                                  &hasColorAttribute,
270                                                  &hasLocalCoordsAttribute,
271                                                  &hasBoneAttribute);
272 
273     SkASSERT(fMeshes.count() == 1); // Non-volatile meshes should never combine.
274 
275     // Get the resource provider.
276     GrResourceProvider* rp = target->resourceProvider();
277 
278     // Generate keys for the buffers.
279     GrUniqueKey vertexKey, indexKey;
280     GrUniqueKey::Builder vertexKeyBuilder(&vertexKey, kDomain, 2);
281     GrUniqueKey::Builder indexKeyBuilder(&indexKey, kDomain, 2);
282     vertexKeyBuilder[0] = indexKeyBuilder[0] = fMeshes[0].fVertices->uniqueID();
283     vertexKeyBuilder[1] = 0;
284     indexKeyBuilder[1] = 1;
285     vertexKeyBuilder.finish();
286     indexKeyBuilder.finish();
287 
288     // Try to grab data from the cache.
289     sk_sp<GrBuffer> vertexBuffer = rp->findByUniqueKey<GrBuffer>(vertexKey);
290     sk_sp<GrBuffer> indexBuffer = this->isIndexed() ?
291             rp->findByUniqueKey<GrBuffer>(indexKey) :
292             nullptr;
293 
294     // Draw using the cached buffers if possible.
295     if (vertexBuffer && (!this->isIndexed() || indexBuffer)) {
296         this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0,
297                            std::move(indexBuffer), 0);
298         return;
299     }
300 
301     // Allocate vertex buffer.
302     size_t vertexStride = gp->vertexStride();
303     vertexBuffer = rp->createBuffer(fVertexCount * vertexStride,
304                                     kVertex_GrBufferType,
305                                     kStatic_GrAccessPattern,
306                                     GrResourceProvider::Flags::kNone);
307     void* verts = vertexBuffer ? vertexBuffer->map() : nullptr;
308     if (!verts) {
309         SkDebugf("Could not allocate vertices\n");
310         return;
311     }
312 
313     // Allocate index buffer.
314     uint16_t* indices = nullptr;
315     if (this->isIndexed()) {
316         indexBuffer = rp->createBuffer(fIndexCount * sizeof(uint16_t),
317                                        kIndex_GrBufferType,
318                                        kStatic_GrAccessPattern,
319                                        GrResourceProvider::Flags::kNone);
320         indices = indexBuffer ? static_cast<uint16_t*>(indexBuffer->map()) : nullptr;
321         if (!indices) {
322             SkDebugf("Could not allocate indices\n");
323             return;
324         }
325     }
326 
327     // Fill the buffers.
328     this->fillBuffers(hasColorAttribute,
329                       hasLocalCoordsAttribute,
330                       hasBoneAttribute,
331                       vertexStride,
332                       verts,
333                       indices);
334 
335     // Unmap the buffers.
336     vertexBuffer->unmap();
337     if (indexBuffer) {
338         indexBuffer->unmap();
339     }
340 
341     // Cache the buffers.
342     rp->assignUniqueKeyToResource(vertexKey, vertexBuffer.get());
343     rp->assignUniqueKeyToResource(indexKey, indexBuffer.get());
344 
345     // Draw the vertices.
346     this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0, std::move(indexBuffer),
347                        0);
348 }
349 
fillBuffers(bool hasColorAttribute,bool hasLocalCoordsAttribute,bool hasBoneAttribute,size_t vertexStride,void * verts,uint16_t * indices) const350 void GrDrawVerticesOp::fillBuffers(bool hasColorAttribute,
351                                    bool hasLocalCoordsAttribute,
352                                    bool hasBoneAttribute,
353                                    size_t vertexStride,
354                                    void* verts,
355                                    uint16_t* indices) const {
356     int instanceCount = fMeshes.count();
357 
358     // Copy data into the buffers.
359     int vertexOffset = 0;
360     // We have a fast case below for uploading the vertex data when the matrix is translate
361     // only and there are colors but not local coords. Fast case does not apply when there are bone
362     // transformations.
363     bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute && !hasBoneAttribute;
364     for (int i = 0; i < instanceCount; i++) {
365         // Get each mesh.
366         const Mesh& mesh = fMeshes[i];
367 
368         // Copy data into the index buffer.
369         if (indices) {
370             int indexCount = mesh.fVertices->indexCount();
371             for (int j = 0; j < indexCount; ++j) {
372                 *indices++ = mesh.fVertices->indices()[j] + vertexOffset;
373             }
374         }
375 
376         // Copy data into the vertex buffer.
377         int vertexCount = mesh.fVertices->vertexCount();
378         const SkPoint* positions = mesh.fVertices->positions();
379         const SkColor* colors = mesh.fVertices->colors();
380         const SkPoint* localCoords = mesh.fVertices->texCoords();
381         const SkVertices::BoneIndices* boneIndices = mesh.fVertices->boneIndices();
382         const SkVertices::BoneWeights* boneWeights = mesh.fVertices->boneWeights();
383         bool fastMesh = (!this->hasMultipleViewMatrices() ||
384                          mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
385                         mesh.hasPerVertexColors();
386         if (fastAttrs && fastMesh) {
387             // Fast case.
388             struct V {
389                 SkPoint fPos;
390                 uint32_t fColor;
391             };
392             SkASSERT(sizeof(V) == vertexStride);
393             V* v = (V*)verts;
394             Sk2f t(0, 0);
395             if (this->hasMultipleViewMatrices()) {
396                 t = Sk2f(mesh.fViewMatrix.getTranslateX(), mesh.fViewMatrix.getTranslateY());
397             }
398             for (int j = 0; j < vertexCount; ++j) {
399                 Sk2f p = Sk2f::Load(positions++) + t;
400                 p.store(&v[j].fPos);
401                 v[j].fColor = colors[j];
402             }
403             verts = v + vertexCount;
404         } else {
405             // Normal case.
406             static constexpr size_t kColorOffset = sizeof(SkPoint);
407             size_t offset = kColorOffset;
408             if (hasColorAttribute) {
409                 offset += sizeof(uint32_t);
410             }
411             size_t localCoordOffset = offset;
412             if (hasLocalCoordsAttribute) {
413                 offset += sizeof(SkPoint);
414             }
415             size_t boneIndexOffset = offset;
416             if (hasBoneAttribute) {
417                 offset += 4 * sizeof(int8_t);
418             }
419             size_t boneWeightOffset = offset;
420 
421             // TODO4F: Preserve float colors
422             GrColor color = mesh.fColor.toBytes_RGBA();
423 
424             for (int j = 0; j < vertexCount; ++j) {
425                 if (this->hasMultipleViewMatrices()) {
426                     mesh.fViewMatrix.mapPoints(((SkPoint*)verts), &positions[j], 1);
427                 } else {
428                     *((SkPoint*)verts) = positions[j];
429                 }
430                 if (hasColorAttribute) {
431                     if (mesh.hasPerVertexColors()) {
432                         *(uint32_t*)((intptr_t)verts + kColorOffset) = colors[j];
433                     } else {
434                         *(uint32_t*)((intptr_t)verts + kColorOffset) = color;
435                     }
436                 }
437                 if (hasLocalCoordsAttribute) {
438                     if (mesh.hasExplicitLocalCoords()) {
439                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = localCoords[j];
440                     } else {
441                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
442                     }
443                 }
444                 if (hasBoneAttribute) {
445                     const SkVertices::BoneIndices& indices = boneIndices[j];
446                     const SkVertices::BoneWeights& weights = boneWeights[j];
447                     for (int k = 0; k < 4; k++) {
448                         size_t indexOffset = boneIndexOffset + sizeof(int8_t) * k;
449                         size_t weightOffset = boneWeightOffset + sizeof(uint8_t) * k;
450                         *(int8_t*)((intptr_t)verts + indexOffset) = indices.indices[k];
451                         *(uint8_t*)((intptr_t)verts + weightOffset) = weights.weights[k] * 255.0f;
452                     }
453                 }
454                 verts = (void*)((intptr_t)verts + vertexStride);
455             }
456         }
457         vertexOffset += vertexCount;
458     }
459 }
460 
drawVertices(Target * target,sk_sp<const GrGeometryProcessor> gp,sk_sp<const GrBuffer> vertexBuffer,int firstVertex,sk_sp<const GrBuffer> indexBuffer,int firstIndex)461 void GrDrawVerticesOp::drawVertices(Target* target,
462                                     sk_sp<const GrGeometryProcessor> gp,
463                                     sk_sp<const GrBuffer> vertexBuffer,
464                                     int firstVertex,
465                                     sk_sp<const GrBuffer> indexBuffer,
466                                     int firstIndex) {
467     GrMesh* mesh = target->allocMesh(this->primitiveType());
468     if (this->isIndexed()) {
469         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertexCount - 1,
470                          GrPrimitiveRestart::kNo);
471     } else {
472         mesh->setNonIndexedNonInstanced(fVertexCount);
473     }
474     mesh->setVertexData(std::move(vertexBuffer), firstVertex);
475     auto pipe = fHelper.makePipeline(target);
476     target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh);
477 }
478 
onCombineIfPossible(GrOp * t,const GrCaps & caps)479 GrOp::CombineResult GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
480     GrDrawVerticesOp* that = t->cast<GrDrawVerticesOp>();
481 
482     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
483         return CombineResult::kCannotCombine;
484     }
485 
486     // Meshes with bones cannot be combined because different meshes use different bones, so to
487     // combine them, the matrices would have to be combined, and the bone indices on each vertex
488     // would change, thus making the vertices uncacheable.
489     if (this->hasBones() || that->hasBones()) {
490         return CombineResult::kCannotCombine;
491     }
492 
493     // Non-volatile meshes cannot batch, because if a non-volatile mesh batches with another mesh,
494     // then on the next frame, if that non-volatile mesh is drawn, it will draw the other mesh
495     // that was saved in its vertex buffer, which is not necessarily there anymore.
496     if (!this->fMeshes[0].fVertices->isVolatile() || !that->fMeshes[0].fVertices->isVolatile()) {
497         return CombineResult::kCannotCombine;
498     }
499 
500     if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
501         return CombineResult::kCannotCombine;
502     }
503 
504     if (fMeshes[0].fVertices->hasIndices() != that->fMeshes[0].fVertices->hasIndices()) {
505         return CombineResult::kCannotCombine;
506     }
507 
508     if (fColorArrayType != that->fColorArrayType) {
509         return CombineResult::kCannotCombine;
510     }
511 
512     if (fVertexCount + that->fVertexCount > SkTo<int>(UINT16_MAX)) {
513         return CombineResult::kCannotCombine;
514     }
515 
516     // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
517     // gamut is determined by the render target context. A mis-match should be impossible.
518     SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
519 
520     // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
521     // with multiple view matrices.
522     fFlags |= that->fFlags;
523 
524     if (!this->requiresPerVertexColors() && this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
525         fFlags |= kRequiresPerVertexColors_Flag;
526     }
527     // Check whether we are about to acquire a mesh with a different view matrix.
528     if (!this->hasMultipleViewMatrices() &&
529         !this->fMeshes[0].fViewMatrix.cheapEqualTo(that->fMeshes[0].fViewMatrix)) {
530         fFlags |= kHasMultipleViewMatrices_Flag;
531     }
532 
533     fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
534     fVertexCount += that->fVertexCount;
535     fIndexCount += that->fIndexCount;
536 
537     return CombineResult::kMerged;
538 }
539 
540 ///////////////////////////////////////////////////////////////////////////////////////////////////
541 
542 #if GR_TEST_UTILS
543 
544 #include "GrDrawOpTest.h"
545 
seed_vertices(GrPrimitiveType type)546 static uint32_t seed_vertices(GrPrimitiveType type) {
547     switch (type) {
548         case GrPrimitiveType::kTriangles:
549         case GrPrimitiveType::kTriangleStrip:
550             return 3;
551         case GrPrimitiveType::kPoints:
552             return 1;
553         case GrPrimitiveType::kLines:
554         case GrPrimitiveType::kLineStrip:
555             return 2;
556         case GrPrimitiveType::kLinesAdjacency:
557             return 4;
558     }
559     SK_ABORT("Incomplete switch\n");
560     return 0;
561 }
562 
primitive_vertices(GrPrimitiveType type)563 static uint32_t primitive_vertices(GrPrimitiveType type) {
564     switch (type) {
565         case GrPrimitiveType::kTriangles:
566             return 3;
567         case GrPrimitiveType::kLines:
568             return 2;
569         case GrPrimitiveType::kTriangleStrip:
570         case GrPrimitiveType::kPoints:
571         case GrPrimitiveType::kLineStrip:
572             return 1;
573         case GrPrimitiveType::kLinesAdjacency:
574             return 4;
575     }
576     SK_ABORT("Incomplete switch\n");
577     return 0;
578 }
579 
random_point(SkRandom * random,SkScalar min,SkScalar max)580 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
581     SkPoint p;
582     p.fX = random->nextRangeScalar(min, max);
583     p.fY = random->nextRangeScalar(min, max);
584     return p;
585 }
586 
randomize_params(size_t count,size_t maxVertex,SkScalar min,SkScalar max,SkRandom * random,SkTArray<SkPoint> * positions,SkTArray<SkPoint> * texCoords,bool hasTexCoords,SkTArray<uint32_t> * colors,bool hasColors,SkTArray<uint16_t> * indices,bool hasIndices)587 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
588                              SkRandom* random, SkTArray<SkPoint>* positions,
589                              SkTArray<SkPoint>* texCoords, bool hasTexCoords,
590                              SkTArray<uint32_t>* colors, bool hasColors,
591                              SkTArray<uint16_t>* indices, bool hasIndices) {
592     for (uint32_t v = 0; v < count; v++) {
593         positions->push_back(random_point(random, min, max));
594         if (hasTexCoords) {
595             texCoords->push_back(random_point(random, min, max));
596         }
597         if (hasColors) {
598             colors->push_back(GrRandomColor(random));
599         }
600         if (hasIndices) {
601             SkASSERT(maxVertex <= UINT16_MAX);
602             indices->push_back(random->nextULessThan((uint16_t)maxVertex));
603         }
604     }
605 }
606 
GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp)607 GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp) {
608     GrPrimitiveType type;
609     do {
610        type = GrPrimitiveType(random->nextULessThan(kNumGrPrimitiveTypes));
611     } while (GrPrimTypeRequiresGeometryShaderSupport(type) &&
612              !context->contextPriv().caps()->shaderCaps()->geometryShaderSupport());
613 
614     uint32_t primitiveCount = random->nextRangeU(1, 100);
615 
616     // TODO make 'sensible' indexbuffers
617     SkTArray<SkPoint> positions;
618     SkTArray<SkPoint> texCoords;
619     SkTArray<uint32_t> colors;
620     SkTArray<uint16_t> indices;
621 
622     bool hasTexCoords = random->nextBool();
623     bool hasIndices = random->nextBool();
624     bool hasColors = random->nextBool();
625 
626     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
627 
628     static const SkScalar kMinVertExtent = -100.f;
629     static const SkScalar kMaxVertExtent = 100.f;
630     randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
631                      &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
632                      hasIndices);
633 
634     for (uint32_t i = 1; i < primitiveCount; i++) {
635         randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
636                          random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
637                          hasIndices);
638     }
639 
640     SkMatrix viewMatrix = GrTest::TestMatrix(random);
641 
642     sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
643 
644     static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
645     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
646                                                       texCoords.begin(), colors.begin(),
647                                                       hasIndices ? indices.count() : 0,
648                                                       indices.begin());
649     GrAAType aaType = GrAAType::kNone;
650     if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
651         aaType = GrAAType::kMSAA;
652     }
653     return GrDrawVerticesOp::Make(context, std::move(paint), std::move(vertices), nullptr, 0,
654                                   viewMatrix, aaType, std::move(colorSpaceXform), &type);
655 }
656 
657 #endif
658