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