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/gpu/ops/DrawVerticesOp.h"
9 
10 #include "include/core/SkM44.h"
11 #include "src/core/SkArenaAlloc.h"
12 #include "src/core/SkDevice.h"
13 #include "src/core/SkMatrixPriv.h"
14 #include "src/core/SkVerticesPriv.h"
15 #include "src/gpu/BufferWriter.h"
16 #include "src/gpu/GrGeometryProcessor.h"
17 #include "src/gpu/GrOpFlushState.h"
18 #include "src/gpu/GrProgramInfo.h"
19 #include "src/gpu/SkGr.h"
20 #include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
21 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
22 #include "src/gpu/glsl/GrGLSLVarying.h"
23 #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
24 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
25 
26 namespace skgpu::v1::DrawVerticesOp {
27 
28 namespace {
29 
30 enum class ColorArrayType {
31     kUnused,
32     kPremulGrColor,
33     kSkColor,
34 };
35 
36 enum class LocalCoordsType {
37     kUnused,
38     kUsePosition,
39     kExplicit,
40 };
41 
42 class VerticesGP : public GrGeometryProcessor {
43 public:
Make(SkArenaAlloc* arena, LocalCoordsType localCoordsType, ColorArrayType colorArrayType, const SkPMColor4f& color, sk_sp<GrColorSpaceXform> colorSpaceXform, const SkMatrix& viewMatrix)44     static GrGeometryProcessor* Make(SkArenaAlloc* arena,
45                                      LocalCoordsType localCoordsType,
46                                      ColorArrayType colorArrayType,
47                                      const SkPMColor4f& color,
48                                      sk_sp<GrColorSpaceXform> colorSpaceXform,
49                                      const SkMatrix& viewMatrix) {
50         return arena->make([&](void* ptr) {
51             return new (ptr) VerticesGP(localCoordsType, colorArrayType, color,
52                                         std::move(colorSpaceXform), viewMatrix);
53         });
54     }
55 
56     const char* name() const override { return "VerticesGP"; }
57 
58     SkString getShaderDfxInfo() const override {
59         SkString format;
60         format.printf("ShaderDfx_VerticesGP_%d_%d_%d_%d_%d",
61             fColorArrayType, GrColorSpaceXform::XformKey(fColorSpaceXform.get()),
62             fViewMatrix.isIdentity(), fViewMatrix.isScaleTranslate(), fViewMatrix.hasPerspective());
63         return format;
64     }
65 
positionAttr() const66     const Attribute& positionAttr() const { return fAttributes[kPositionIndex]; }
colorAttr() const67     const Attribute& colorAttr() const { return fAttributes[kColorIndex]; }
localCoordsAttr() const68     const Attribute& localCoordsAttr() const { return fAttributes[kLocalCoordsIndex]; }
69 
70     void addToKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
71         uint32_t key = 0;
72         key |= (fColorArrayType == ColorArrayType::kSkColor) ? 0x1 : 0;
73         key |= ProgramImpl::ComputeMatrixKey(caps, fViewMatrix) << 20;
74         b->add32(key);
75         b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
76     }
77 
78     std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
79         return std::make_unique<Impl>();
80     }
81 
82 private:
83     class Impl : public ProgramImpl {
84     public:
85         void setData(const GrGLSLProgramDataManager& pdman,
86                      const GrShaderCaps& shaderCaps,
87                      const GrGeometryProcessor& geomProc) override {
88             const VerticesGP& vgp = geomProc.cast<VerticesGP>();
89 
90             SetTransform(pdman, shaderCaps, fViewMatrixUniform, vgp.fViewMatrix, &fViewMatrix);
91 
92             if (!vgp.colorAttr().isInitialized() && vgp.fColor != fColor) {
93                 pdman.set4fv(fColorUniform, 1, vgp.fColor.vec());
94                 fColor = vgp.fColor;
95             }
96 
97             fColorSpaceHelper.setData(pdman, vgp.fColorSpaceXform.get());
98         }
99 
100     private:
101         void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
102             const VerticesGP& gp = args.fGeomProc.cast<VerticesGP>();
103             GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
104             GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
105             GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
106             GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
107 
108             // emit attributes
109             varyingHandler->emitAttributes(gp);
110 
111             fColorSpaceHelper.emitCode(uniformHandler, gp.fColorSpaceXform.get(),
112                                        kVertex_GrShaderFlag);
113 
114             // Setup pass through color
115             fragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
116             if (gp.colorAttr().isInitialized()) {
117                 GrGLSLVarying varying(kHalf4_GrSLType);
118                 varyingHandler->addVarying("color", &varying);
119                 vertBuilder->codeAppendf("half4 color = %s;", gp.colorAttr().name());
120 
121                 // For SkColor, do a red/blue swap, possible color space conversion, and premul
122                 if (gp.fColorArrayType == ColorArrayType::kSkColor) {
123                     vertBuilder->codeAppend("color = color.bgra;");
124 
125                     SkString xformedColor;
126                     vertBuilder->appendColorGamutXform(&xformedColor, "color", &fColorSpaceHelper);
127                     vertBuilder->codeAppendf("color = %s;", xformedColor.c_str());
128 
129                     vertBuilder->codeAppend("color = half4(color.rgb * color.a, color.a);");
130                 }
131 
132                 vertBuilder->codeAppendf("%s = color;\n", varying.vsOut());
133                 fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
134             } else {
135                 this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
136                                         &fColorUniform);
137             }
138 
139             // Setup position
140             WriteOutputPosition(vertBuilder,
141                                 uniformHandler,
142                                 *args.fShaderCaps,
143                                 gpArgs,
144                                 gp.positionAttr().name(),
145                                 gp.fViewMatrix,
146                                 &fViewMatrixUniform);
147 
148             // emit transforms using either explicit local coords or positions
149             const auto& coordsAttr = gp.localCoordsAttr().isInitialized() ? gp.localCoordsAttr()
150                                                                           : gp.positionAttr();
151             gpArgs->fLocalCoordVar = coordsAttr.asShaderVar();
152 
153             fragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
154         }
155 
156     private:
157         SkMatrix fViewMatrix = SkMatrix::InvalidMatrix();
158         SkPMColor4f fColor   = SK_PMColor4fILLEGAL;
159 
160         UniformHandle fViewMatrixUniform;
161         UniformHandle fColorUniform;
162 
163         GrGLSLColorSpaceXformHelper fColorSpaceHelper;
164     };
165 
VerticesGP(LocalCoordsType localCoordsType, ColorArrayType colorArrayType, const SkPMColor4f& color, sk_sp<GrColorSpaceXform> colorSpaceXform, const SkMatrix& viewMatrix)166     VerticesGP(LocalCoordsType localCoordsType,
167                ColorArrayType colorArrayType,
168                const SkPMColor4f& color,
169                sk_sp<GrColorSpaceXform> colorSpaceXform,
170                const SkMatrix& viewMatrix)
171             : INHERITED(kVerticesGP_ClassID)
172             , fColorArrayType(colorArrayType)
173             , fColor(color)
174             , fViewMatrix(viewMatrix)
175             , fColorSpaceXform(std::move(colorSpaceXform)) {
176         constexpr Attribute missingAttr;
177         fAttributes.push_back({"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType});
178         fAttributes.push_back(fColorArrayType != ColorArrayType::kUnused
179                                       ? MakeColorAttribute("inColor", false)
180                                       : missingAttr);
181         fAttributes.push_back(localCoordsType == LocalCoordsType::kExplicit
182                         ? Attribute{"inLocalCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType}
183                         : missingAttr);
184 
185         this->setVertexAttributes(fAttributes.data(), fAttributes.size());
186     }
187 
188     enum {
189         kPositionIndex    = 0,
190         kColorIndex       = 1,
191         kLocalCoordsIndex = 2,
192     };
193 
194     std::vector<Attribute> fAttributes;
195     ColorArrayType fColorArrayType;
196     SkPMColor4f fColor;
197     SkMatrix fViewMatrix;
198     sk_sp<GrColorSpaceXform> fColorSpaceXform;
199 
200     using INHERITED = GrGeometryProcessor;
201 };
202 
203 class DrawVerticesOpImpl final : public GrMeshDrawOp {
204 private:
205     using Helper = GrSimpleMeshDrawOpHelper;
206 
207 public:
208     DEFINE_OP_CLASS_ID
209 
210     DrawVerticesOpImpl(GrProcessorSet*,
211                        const SkPMColor4f&,
212                        sk_sp<SkVertices>,
213                        GrPrimitiveType,
214                        GrAAType,
215                        sk_sp<GrColorSpaceXform>,
216                        const SkMatrixProvider&);
217 
218     const char* name() const override { return "DrawVerticesOp"; }
219 
220     void visitProxies(const GrVisitProxyFunc& func) const override {
221         if (fProgramInfo) {
222             fProgramInfo->visitFPProxies(func);
223         } else {
224             fHelper.visitProxies(func);
225         }
226     }
227 
228     FixedFunctionFlags fixedFunctionFlags() const override;
229 
230     GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
231 
232 private:
233     GrProgramInfo* programInfo() override { return fProgramInfo; }
234 
235     void onCreateProgramInfo(const GrCaps*,
236                              SkArenaAlloc*,
237                              const GrSurfaceProxyView& writeView,
238                              bool usesMSAASurface,
239                              GrAppliedClip&&,
240                              const GrDstProxyView&,
241                              GrXferBarrierFlags renderPassXferBarriers,
242                              GrLoadOp colorLoadOp) override;
243 
244     void onPrepareDraws(GrMeshDrawTarget*) override;
245     void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
246 #if GR_TEST_UTILS
247     SkString onDumpInfo() const override;
248 #endif
249 
250     GrGeometryProcessor* makeGP(SkArenaAlloc*);
251 
primitiveType() const252     GrPrimitiveType primitiveType() const { return fPrimitiveType; }
combinablePrimitive() const253     bool combinablePrimitive() const {
254         return GrPrimitiveType::kTriangles == fPrimitiveType ||
255                GrPrimitiveType::kLines == fPrimitiveType ||
256                GrPrimitiveType::kPoints == fPrimitiveType;
257     }
258 
259     CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps&) override;
260 
261     struct Mesh {
262         SkPMColor4f fColor;  // Used if this->hasPerVertexColors() is false.
263         sk_sp<SkVertices> fVertices;
264         SkMatrix fViewMatrix;
265         bool fIgnoreColors;
266 
hasPerVertexColorsskgpu::v1::DrawVerticesOp::final::Mesh267         bool hasPerVertexColors() const {
268             return fVertices->priv().hasColors() && !fIgnoreColors;
269         }
270     };
271 
isIndexed() const272     bool isIndexed() const {
273         // Consistency enforced in onCombineIfPossible.
274         return fMeshes[0].fVertices->priv().hasIndices();
275     }
276 
requiresPerVertexColors() const277     bool requiresPerVertexColors() const {
278         return fColorArrayType != ColorArrayType::kUnused;
279     }
280 
requiresPerVertexLocalCoords() const281     bool requiresPerVertexLocalCoords() const {
282         return fLocalCoordsType == LocalCoordsType::kExplicit;
283     }
284 
vertexStride() const285     size_t vertexStride() const {
286         return sizeof(SkPoint) +
287                (this->requiresPerVertexColors() ? sizeof(uint32_t) : 0) +
288                (this->requiresPerVertexLocalCoords() ? sizeof(SkPoint) : 0);
289     }
290 
291     Helper fHelper;
292     SkSTArray<1, Mesh, true> fMeshes;
293     // GrPrimitiveType is more expressive than fVertices.mode() so it is used instead and we ignore
294     // the SkVertices mode (though fPrimitiveType may have been inferred from it).
295     GrPrimitiveType fPrimitiveType;
296     int fVertexCount;
297     int fIndexCount;
298     bool fMultipleViewMatrices;
299     LocalCoordsType fLocalCoordsType;
300     ColorArrayType fColorArrayType;
301     sk_sp<GrColorSpaceXform> fColorSpaceXform;
302 
303     GrSimpleMesh*  fMesh = nullptr;
304     GrProgramInfo* fProgramInfo = nullptr;
305 
306     using INHERITED = GrMeshDrawOp;
307 };
308 
DrawVerticesOpImpl(GrProcessorSet* processorSet, const SkPMColor4f& color, sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType, GrAAType aaType, sk_sp<GrColorSpaceXform> colorSpaceXform, const SkMatrixProvider& matrixProvider)309 DrawVerticesOpImpl::DrawVerticesOpImpl(GrProcessorSet* processorSet,
310                                        const SkPMColor4f& color,
311                                        sk_sp<SkVertices> vertices,
312                                        GrPrimitiveType primitiveType,
313                                        GrAAType aaType,
314                                        sk_sp<GrColorSpaceXform> colorSpaceXform,
315                                        const SkMatrixProvider& matrixProvider)
316         : INHERITED(ClassID())
317         , fHelper(processorSet, aaType)
318         , fPrimitiveType(primitiveType)
319         , fMultipleViewMatrices(false)
320         , fColorSpaceXform(std::move(colorSpaceXform)) {
321     SkASSERT(vertices);
322 
323     SkVerticesPriv info(vertices->priv());
324 
325     fVertexCount = info.vertexCount();
326     fIndexCount = info.indexCount();
327     fColorArrayType = info.hasColors() ? ColorArrayType::kSkColor
328                                        : ColorArrayType::kUnused;
329     fLocalCoordsType = info.hasTexCoords() ? LocalCoordsType::kExplicit
330                                            : LocalCoordsType::kUsePosition;
331 
332     Mesh& mesh = fMeshes.push_back();
333     mesh.fColor = color;
334     mesh.fViewMatrix = matrixProvider.localToDevice();
335     mesh.fVertices = std::move(vertices);
336     mesh.fIgnoreColors = false;
337 
338     IsHairline zeroArea;
339     if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
340         zeroArea = IsHairline::kYes;
341     } else {
342         zeroArea = IsHairline::kNo;
343     }
344 
345     this->setTransformedBounds(mesh.fVertices->bounds(),
346                                 mesh.fViewMatrix,
347                                 HasAABloat::kNo,
348                                 zeroArea);
349 }
350 
351 #if GR_TEST_UTILS
onDumpInfo() const352 SkString DrawVerticesOpImpl::onDumpInfo() const {
353     return SkStringPrintf("PrimType: %d, MeshCount %d, VCount: %d, ICount: %d\n%s",
354                           (int)fPrimitiveType, fMeshes.count(), fVertexCount, fIndexCount,
355                           fHelper.dumpInfo().c_str());
356 }
357 #endif
358 
fixedFunctionFlags() const359 GrDrawOp::FixedFunctionFlags DrawVerticesOpImpl::fixedFunctionFlags() const {
360     return fHelper.fixedFunctionFlags();
361 }
362 
finalize(const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType)363 GrProcessorSet::Analysis DrawVerticesOpImpl::finalize(const GrCaps& caps,
364                                                       const GrAppliedClip* clip,
365                                                       GrClampType clampType) {
366     GrProcessorAnalysisColor gpColor;
367     if (this->requiresPerVertexColors()) {
368         gpColor.setToUnknown();
369     } else {
370         gpColor.setToConstant(fMeshes.front().fColor);
371     }
372     auto result = fHelper.finalizeProcessors(caps, clip, clampType,
373                                              GrProcessorAnalysisCoverage::kNone, &gpColor);
374     if (gpColor.isConstant(&fMeshes.front().fColor)) {
375         fMeshes.front().fIgnoreColors = true;
376         fColorArrayType = ColorArrayType::kUnused;
377     }
378     if (!fHelper.usesLocalCoords()) {
379         fLocalCoordsType = LocalCoordsType::kUnused;
380     }
381     return result;
382 }
383 
makeGP(SkArenaAlloc* arena)384 GrGeometryProcessor* DrawVerticesOpImpl::makeGP(SkArenaAlloc* arena) {
385     const SkMatrix& vm = fMultipleViewMatrices ? SkMatrix::I() : fMeshes[0].fViewMatrix;
386 
387     sk_sp<GrColorSpaceXform> csxform =
388             (fColorArrayType == ColorArrayType::kSkColor) ? fColorSpaceXform : nullptr;
389 
390     auto gp = VerticesGP::Make(arena, fLocalCoordsType, fColorArrayType, fMeshes[0].fColor,
391                                std::move(csxform), vm);
392     SkASSERT(this->vertexStride() == gp->vertexStride());
393     return gp;
394 }
395 
onCreateProgramInfo(const GrCaps* caps, SkArenaAlloc* arena, const GrSurfaceProxyView& writeView, bool usesMSAASurface, GrAppliedClip&& appliedClip, const GrDstProxyView& dstProxyView, GrXferBarrierFlags renderPassXferBarriers, GrLoadOp colorLoadOp)396 void DrawVerticesOpImpl::onCreateProgramInfo(const GrCaps* caps,
397                                              SkArenaAlloc* arena,
398                                              const GrSurfaceProxyView& writeView,
399                                              bool usesMSAASurface,
400                                              GrAppliedClip&& appliedClip,
401                                              const GrDstProxyView& dstProxyView,
402                                              GrXferBarrierFlags renderPassXferBarriers,
403                                              GrLoadOp colorLoadOp) {
404     GrGeometryProcessor* gp = this->makeGP(arena);
405     fProgramInfo = fHelper.createProgramInfo(caps, arena, writeView, usesMSAASurface,
406                                              std::move(appliedClip), dstProxyView, gp,
407                                              this->primitiveType(), renderPassXferBarriers,
408                                              colorLoadOp);
409 }
410 
onPrepareDraws(GrMeshDrawTarget* target)411 void DrawVerticesOpImpl::onPrepareDraws(GrMeshDrawTarget* target) {
412     // Allocate buffers.
413     size_t vertexStride = this->vertexStride();
414     sk_sp<const GrBuffer> vertexBuffer;
415     int firstVertex = 0;
416     VertexWriter verts{
417             target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex)};
418     if (!verts) {
419         SkDebugf("Could not allocate vertices\n");
420         return;
421     }
422 
423     sk_sp<const GrBuffer> indexBuffer;
424     int firstIndex = 0;
425     uint16_t* indices = nullptr;
426     if (this->isIndexed()) {
427         indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
428         if (!indices) {
429             SkDebugf("Could not allocate indices\n");
430             return;
431         }
432     }
433 
434     // Copy data into the buffers.
435     bool hasColorAttribute = this->requiresPerVertexColors();
436     bool hasLocalCoordsAttribute = this->requiresPerVertexLocalCoords();
437     int vertexOffset = 0;
438 
439     for (const auto& mesh : fMeshes) {
440         SkVerticesPriv info(mesh.fVertices->priv());
441 
442         // Copy data into the index buffer.
443         if (indices) {
444             int indexCount = info.indexCount();
445             for (int i = 0; i < indexCount; ++i) {
446                 *indices++ = info.indices()[i] + vertexOffset;
447             }
448         }
449 
450         // Copy data into the vertex buffer.
451         int vertexCount = info.vertexCount();
452         const SkPoint* positions = info.positions();
453         const SkColor* colors = info.colors();
454         const SkPoint* localCoords = info.texCoords() ? info.texCoords() : positions;
455 
456         // TODO4F: Preserve float colors
457         GrColor meshColor = mesh.fColor.toBytes_RGBA();
458 
459         SkPoint* posBase = (SkPoint*)verts.ptr();
460 
461         for (int i = 0; i < vertexCount; ++i) {
462             verts << positions[i];
463             if (hasColorAttribute) {
464                 verts << (mesh.hasPerVertexColors() ? colors[i] : meshColor);
465             }
466             if (hasLocalCoordsAttribute) {
467                 verts << localCoords[i];
468             }
469         }
470 
471         if (fMultipleViewMatrices) {
472             SkASSERT(!mesh.fViewMatrix.hasPerspective());
473             SkMatrixPriv::MapPointsWithStride(mesh.fViewMatrix, posBase, vertexStride,
474                                               positions, sizeof(SkPoint), vertexCount);
475         }
476 
477         vertexOffset += vertexCount;
478     }
479 
480     SkASSERT(!fMesh);
481     fMesh = target->allocMesh();
482     if (this->isIndexed()) {
483         fMesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertexCount - 1,
484                          GrPrimitiveRestart::kNo, std::move(vertexBuffer), firstVertex);
485     } else {
486         fMesh->set(std::move(vertexBuffer), fVertexCount, firstVertex);
487     }
488 }
489 
onExecute(GrOpFlushState* flushState, const SkRect& chainBounds)490 void DrawVerticesOpImpl::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
491     if (!fProgramInfo) {
492         this->createProgramInfo(flushState);
493     }
494 
495     if (!fProgramInfo || !fMesh) {
496         return;
497     }
498 
499     flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
500     flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
501     flushState->drawMesh(*fMesh);
502 }
503 
onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps)504 GrOp::CombineResult DrawVerticesOpImpl::onCombineIfPossible(GrOp* t,
505                                                             SkArenaAlloc*,
506                                                             const GrCaps& caps) {
507     auto that = t->cast<DrawVerticesOpImpl>();
508 
509     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
510         return CombineResult::kCannotCombine;
511     }
512 
513     if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
514         return CombineResult::kCannotCombine;
515     }
516 
517     if (this->isIndexed() != that->isIndexed()) {
518         return CombineResult::kCannotCombine;
519     }
520 
521     if (fVertexCount + that->fVertexCount > SkTo<int>(UINT16_MAX)) {
522         return CombineResult::kCannotCombine;
523     }
524 
525     // We can't mix draws that use SkColor vertex colors with those that don't. We can mix uniform
526     // color draws with GrColor draws (by expanding the uniform color into vertex color).
527     if ((fColorArrayType == ColorArrayType::kSkColor) !=
528         (that->fColorArrayType == ColorArrayType::kSkColor)) {
529         return CombineResult::kCannotCombine;
530     }
531 
532     // If we're acquiring a mesh with a different view matrix, or an op that needed multiple view
533     // matrices, we need multiple view matrices.
534     bool needMultipleViewMatrices =
535             fMultipleViewMatrices || that->fMultipleViewMatrices ||
536             !SkMatrixPriv::CheapEqual(this->fMeshes[0].fViewMatrix, that->fMeshes[0].fViewMatrix);
537 
538     // ... but we can't enable multiple view matrices if any of them have perspective, or our other
539     // varyings won't be interpolated correctly.
540     if (needMultipleViewMatrices && (this->fMeshes[0].fViewMatrix.hasPerspective() ||
541                                      that->fMeshes[0].fViewMatrix.hasPerspective())) {
542         return CombineResult::kCannotCombine;
543     } else {
544         fMultipleViewMatrices = needMultipleViewMatrices;
545     }
546 
547     // If the other op already required per-vertex colors, the combined mesh does.
548     if (that->fColorArrayType == ColorArrayType::kPremulGrColor) {
549         fColorArrayType = ColorArrayType::kPremulGrColor;
550     }
551 
552     // If we combine meshes with different (uniform) colors, switch to per-vertex colors.
553     if (fColorArrayType == ColorArrayType::kUnused) {
554         SkASSERT(that->fColorArrayType == ColorArrayType::kUnused);
555         if (this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
556             fColorArrayType = ColorArrayType::kPremulGrColor;
557         }
558     }
559 
560     // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
561     // gamut is determined by the render target context. A mis-match should be impossible.
562     SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
563 
564     // If the other op already required explicit local coords the combined mesh does.
565     if (that->fLocalCoordsType == LocalCoordsType::kExplicit) {
566         fLocalCoordsType = LocalCoordsType::kExplicit;
567     }
568 
569     // If we were planning to use positions for local coords but now have multiple view matrices,
570     // switch to explicit local coords.
571     if (fLocalCoordsType == LocalCoordsType::kUsePosition && fMultipleViewMatrices) {
572         fLocalCoordsType = LocalCoordsType::kExplicit;
573     }
574 
575     fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
576     fVertexCount += that->fVertexCount;
577     fIndexCount += that->fIndexCount;
578 
579     return CombineResult::kMerged;
580 }
581 
SkVertexModeToGrPrimitiveType(SkVertices::VertexMode mode)582 static GrPrimitiveType SkVertexModeToGrPrimitiveType(SkVertices::VertexMode mode) {
583     switch (mode) {
584         case SkVertices::kTriangles_VertexMode:
585             return GrPrimitiveType::kTriangles;
586         case SkVertices::kTriangleStrip_VertexMode:
587             return GrPrimitiveType::kTriangleStrip;
588         case SkVertices::kTriangleFan_VertexMode:
589             break;
590     }
591     SK_ABORT("Invalid mode");
592 }
593 
594 } // anonymous namespace
595 
Make(GrRecordingContext* context, GrPaint&& paint, sk_sp<SkVertices> vertices, const SkMatrixProvider& matrixProvider, GrAAType aaType, sk_sp<GrColorSpaceXform> colorSpaceXform, GrPrimitiveType* overridePrimType)596 GrOp::Owner Make(GrRecordingContext* context,
597                  GrPaint&& paint,
598                  sk_sp<SkVertices> vertices,
599                  const SkMatrixProvider& matrixProvider,
600                  GrAAType aaType,
601                  sk_sp<GrColorSpaceXform> colorSpaceXform,
602                  GrPrimitiveType* overridePrimType) {
603     SkASSERT(vertices);
604     GrPrimitiveType primType = overridePrimType
605                                        ? *overridePrimType
606                                        : SkVertexModeToGrPrimitiveType(vertices->priv().mode());
607     return GrSimpleMeshDrawOpHelper::FactoryHelper<DrawVerticesOpImpl>(context,
608                                                                        std::move(paint),
609                                                                        std::move(vertices),
610                                                                        primType,
611                                                                        aaType,
612                                                                        std::move(colorSpaceXform),
613                                                                        matrixProvider);
614 }
615 
616 } // namespace skgpu::v1::DrawVerticesOp
617 
618 ///////////////////////////////////////////////////////////////////////////////////////////////////
619 
620 #if GR_TEST_UTILS
621 
622 #include "src/gpu/GrDrawOpTest.h"
623 
624 static uint32_t seed_vertices(GrPrimitiveType type) {
625     switch (type) {
626         case GrPrimitiveType::kTriangles:
627         case GrPrimitiveType::kTriangleStrip:
628             return 3;
629         case GrPrimitiveType::kPoints:
630             return 1;
631         case GrPrimitiveType::kLines:
632         case GrPrimitiveType::kLineStrip:
633             return 2;
634         case GrPrimitiveType::kPatches:
635         case GrPrimitiveType::kPath:
636             SkASSERT(0);
637             return 0;
638     }
639     SK_ABORT("Incomplete switch\n");
640 }
641 
642 static uint32_t primitive_vertices(GrPrimitiveType type) {
643     switch (type) {
644         case GrPrimitiveType::kTriangles:
645             return 3;
646         case GrPrimitiveType::kLines:
647             return 2;
648         case GrPrimitiveType::kTriangleStrip:
649         case GrPrimitiveType::kPoints:
650         case GrPrimitiveType::kLineStrip:
651             return 1;
652         case GrPrimitiveType::kPatches:
653         case GrPrimitiveType::kPath:
654             SkASSERT(0);
655             return 0;
656     }
657     SK_ABORT("Incomplete switch\n");
658 }
659 
660 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
661     SkPoint p;
662     p.fX = random->nextRangeScalar(min, max);
663     p.fY = random->nextRangeScalar(min, max);
664     return p;
665 }
666 
667 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
668                              SkRandom* random, SkTArray<SkPoint>* positions,
669                              SkTArray<SkPoint>* texCoords, bool hasTexCoords,
670                              SkTArray<uint32_t>* colors, bool hasColors,
671                              SkTArray<uint16_t>* indices, bool hasIndices) {
672     for (uint32_t v = 0; v < count; v++) {
673         positions->push_back(random_point(random, min, max));
674         if (hasTexCoords) {
675             texCoords->push_back(random_point(random, min, max));
676         }
677         if (hasColors) {
678             colors->push_back(GrTest::RandomColor(random));
679         }
680         if (hasIndices) {
681             SkASSERT(maxVertex <= UINT16_MAX);
682             indices->push_back(random->nextULessThan((uint16_t)maxVertex));
683         }
684     }
685 }
686 
687 GR_DRAW_OP_TEST_DEFINE(DrawVerticesOp) {
688     GrPrimitiveType types[] = {
689         GrPrimitiveType::kTriangles,
690         GrPrimitiveType::kTriangleStrip,
691         GrPrimitiveType::kPoints,
692         GrPrimitiveType::kLines,
693         GrPrimitiveType::kLineStrip
694     };
695     auto type = types[random->nextULessThan(SK_ARRAY_COUNT(types))];
696 
697     uint32_t primitiveCount = random->nextRangeU(1, 100);
698 
699     // TODO make 'sensible' indexbuffers
700     SkTArray<SkPoint> positions;
701     SkTArray<SkPoint> texCoords;
702     SkTArray<uint32_t> colors;
703     SkTArray<uint16_t> indices;
704 
705     bool hasTexCoords = random->nextBool();
706     bool hasIndices = random->nextBool();
707     bool hasColors = random->nextBool();
708 
709     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
710 
711     static const SkScalar kMinVertExtent = -100.f;
712     static const SkScalar kMaxVertExtent = 100.f;
713     randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
714                      &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
715                      hasIndices);
716 
717     for (uint32_t i = 1; i < primitiveCount; i++) {
718         randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
719                          random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
720                          hasIndices);
721     }
722 
723     SkSimpleMatrixProvider matrixProvider(GrTest::TestMatrix(random));
724 
725     sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
726 
727     static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
728     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
729                                                       texCoords.begin(), colors.begin(),
730                                                       hasIndices ? indices.count() : 0,
731                                                       indices.begin());
732     GrAAType aaType = GrAAType::kNone;
733     if (numSamples > 1 && random->nextBool()) {
734         aaType = GrAAType::kMSAA;
735     }
736     return skgpu::v1::DrawVerticesOp::Make(context,
737                                            std::move(paint),
738                                            std::move(vertices),
739                                            matrixProvider,
740                                            aaType,
741                                            std::move(colorSpaceXform),
742                                            &type);
743 }
744 
745 #endif
746