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
26namespace skgpu::v1::DrawVerticesOp {
27
28namespace {
29
30enum class ColorArrayType {
31    kUnused,
32    kPremulGrColor,
33    kSkColor,
34};
35
36enum class LocalCoordsType {
37    kUnused,
38    kUsePosition,
39    kExplicit,
40};
41
42class VerticesGP : public GrGeometryProcessor {
43public:
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
66    const Attribute& positionAttr() const { return fAttributes[kPositionIndex]; }
67    const Attribute& colorAttr() const { return fAttributes[kColorIndex]; }
68    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
82private:
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
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
203class DrawVerticesOpImpl final : public GrMeshDrawOp {
204private:
205    using Helper = GrSimpleMeshDrawOpHelper;
206
207public:
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
232private:
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
252    GrPrimitiveType primitiveType() const { return fPrimitiveType; }
253    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
267        bool hasPerVertexColors() const {
268            return fVertices->priv().hasColors() && !fIgnoreColors;
269        }
270    };
271
272    bool isIndexed() const {
273        // Consistency enforced in onCombineIfPossible.
274        return fMeshes[0].fVertices->priv().hasIndices();
275    }
276
277    bool requiresPerVertexColors() const {
278        return fColorArrayType != ColorArrayType::kUnused;
279    }
280
281    bool requiresPerVertexLocalCoords() const {
282        return fLocalCoordsType == LocalCoordsType::kExplicit;
283    }
284
285    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
309DrawVerticesOpImpl::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
352SkString 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
359GrDrawOp::FixedFunctionFlags DrawVerticesOpImpl::fixedFunctionFlags() const {
360    return fHelper.fixedFunctionFlags();
361}
362
363GrProcessorSet::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
384GrGeometryProcessor* 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
396void 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
411void 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
490void 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
504GrOp::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
582static 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
596GrOp::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
624static 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
642static 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
660static 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
667static 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
687GR_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