Home | History | Annotate | Download | only in ops
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrDrawVerticesOp.h"
      9 #include "GrCaps.h"
     10 #include "GrDefaultGeoProcFactory.h"
     11 #include "GrOpFlushState.h"
     12 #include "GrSimpleMeshDrawOpHelper.h"
     13 #include "SkGr.h"
     14 #include "SkRectPriv.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 
     29     const char* name() const override { return "DrawVerticesOp"; }
     30 
     31     void visitProxies(const VisitProxyFunc& func, VisitorType) 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*, GrFSAAType,
     42                                       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 
     73     GrPrimitiveType primitiveType() const { return fPrimitiveType; }
     74     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 
     89         bool hasExplicitLocalCoords() const {
     90             return fVertices->hasTexCoords() && !fIgnoreTexCoords;
     91         }
     92 
     93         bool hasPerVertexColors() const {
     94             return fVertices->hasColors() && !fIgnoreColors;
     95         }
     96     };
     97 
     98     bool isIndexed() const {
     99         // Consistency enforced in onCombineIfPossible.
    100         return fMeshes[0].fVertices->hasIndices();
    101     }
    102 
    103     bool requiresPerVertexColors() const {
    104         return SkToBool(kRequiresPerVertexColors_Flag & fFlags);
    105     }
    106 
    107     bool anyMeshHasExplicitLocalCoords() const {
    108         return SkToBool(kAnyMeshHasExplicitLocalCoords_Flag & fFlags);
    109     }
    110 
    111     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 
    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
    195 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 
    205 GrDrawOp::FixedFunctionFlags DrawVerticesOp::fixedFunctionFlags() const {
    206     return fHelper.fixedFunctionFlags();
    207 }
    208 
    209 GrProcessorSet::Analysis DrawVerticesOp::finalize(const GrCaps& caps, const GrAppliedClip* clip,
    210                                                   GrFSAAType fsaaType, GrClampType clampType) {
    211     GrProcessorAnalysisColor gpColor;
    212     if (this->requiresPerVertexColors()) {
    213         gpColor.setToUnknown();
    214     } else {
    215         gpColor.setToConstant(fMeshes.front().fColor);
    216     }
    217     auto result = fHelper.finalizeProcessors(
    218             caps, clip, fsaaType, clampType, GrProcessorAnalysisCoverage::kNone, &gpColor);
    219     if (gpColor.isConstant(&fMeshes.front().fColor)) {
    220         fMeshes.front().fIgnoreColors = true;
    221         fFlags &= ~kRequiresPerVertexColors_Flag;
    222         fColorArrayType = ColorArrayType::kPremulGrColor;
    223     }
    224     if (!fHelper.usesLocalCoords()) {
    225         fMeshes[0].fIgnoreTexCoords = true;
    226         fFlags &= ~kAnyMeshHasExplicitLocalCoords_Flag;
    227     }
    228     return result;
    229 }
    230 
    231 sk_sp<GrGeometryProcessor> DrawVerticesOp::makeGP(const GrShaderCaps* shaderCaps,
    232                                                   bool* hasColorAttribute,
    233                                                   bool* hasLocalCoordAttribute) const {
    234     using namespace GrDefaultGeoProcFactory;
    235     LocalCoords::Type localCoordsType;
    236     if (fHelper.usesLocalCoords()) {
    237         // If we have multiple view matrices we will transform the positions into device space. We
    238         // must then also provide untransformed positions as local coords.
    239         if (this->anyMeshHasExplicitLocalCoords() || this->hasMultipleViewMatrices()) {
    240             *hasLocalCoordAttribute = true;
    241             localCoordsType = LocalCoords::kHasExplicit_Type;
    242         } else {
    243             *hasLocalCoordAttribute = false;
    244             localCoordsType = LocalCoords::kUsePosition_Type;
    245         }
    246     } else {
    247         localCoordsType = LocalCoords::kUnused_Type;
    248         *hasLocalCoordAttribute = false;
    249     }
    250 
    251     Color color(fMeshes[0].fColor);
    252     if (this->requiresPerVertexColors()) {
    253         if (fColorArrayType == ColorArrayType::kPremulGrColor) {
    254             color.fType = Color::kPremulGrColorAttribute_Type;
    255         } else {
    256             color.fType = Color::kUnpremulSkColorAttribute_Type;
    257             color.fColorSpaceXform = fColorSpaceXform;
    258         }
    259         *hasColorAttribute = true;
    260     } else {
    261         *hasColorAttribute = false;
    262     }
    263 
    264     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
    265 
    266     return GrDefaultGeoProcFactory::Make(shaderCaps,
    267                                             color,
    268                                             Coverage::kSolid_Type,
    269                                             localCoordsType,
    270                                             vm);
    271 }
    272 
    273 void DrawVerticesOp::onPrepareDraws(Target* target) {
    274     bool hasMapBufferSupport = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags();
    275     if (fMeshes[0].fVertices->isVolatile() || !hasMapBufferSupport) {
    276         this->drawVolatile(target);
    277     } else {
    278         this->drawNonVolatile(target);
    279     }
    280 }
    281 
    282 void DrawVerticesOp::drawVolatile(Target* target) {
    283     bool hasColorAttribute;
    284     bool hasLocalCoordsAttribute;
    285     sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
    286                                                  &hasColorAttribute,
    287                                                  &hasLocalCoordsAttribute);
    288 
    289     // Allocate buffers.
    290     size_t vertexStride = gp->vertexStride();
    291     sk_sp<const GrBuffer> vertexBuffer;
    292     int firstVertex = 0;
    293     void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
    294     if (!verts) {
    295         SkDebugf("Could not allocate vertices\n");
    296         return;
    297     }
    298 
    299     sk_sp<const GrBuffer> indexBuffer;
    300     int firstIndex = 0;
    301     uint16_t* indices = nullptr;
    302     if (this->isIndexed()) {
    303         indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
    304         if (!indices) {
    305             SkDebugf("Could not allocate indices\n");
    306             return;
    307         }
    308     }
    309 
    310     // Fill the buffers.
    311     this->fillBuffers(hasColorAttribute,
    312                       hasLocalCoordsAttribute,
    313                       vertexStride,
    314                       verts,
    315                       indices);
    316 
    317     // Draw the vertices.
    318     this->drawVertices(target, std::move(gp), std::move(vertexBuffer), firstVertex, indexBuffer,
    319                        firstIndex);
    320 }
    321 
    322 void DrawVerticesOp::drawNonVolatile(Target* target) {
    323     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    324 
    325     bool hasColorAttribute;
    326     bool hasLocalCoordsAttribute;
    327     sk_sp<GrGeometryProcessor> gp = this->makeGP(target->caps().shaderCaps(),
    328                                                  &hasColorAttribute,
    329                                                  &hasLocalCoordsAttribute);
    330 
    331     SkASSERT(fMeshes.count() == 1); // Non-volatile meshes should never combine.
    332 
    333     // Get the resource provider.
    334     GrResourceProvider* rp = target->resourceProvider();
    335 
    336     // Generate keys for the buffers.
    337     GrUniqueKey vertexKey, indexKey;
    338     GrUniqueKey::Builder vertexKeyBuilder(&vertexKey, kDomain, 2);
    339     GrUniqueKey::Builder indexKeyBuilder(&indexKey, kDomain, 2);
    340     vertexKeyBuilder[0] = indexKeyBuilder[0] = fMeshes[0].fVertices->uniqueID();
    341     vertexKeyBuilder[1] = 0;
    342     indexKeyBuilder[1] = 1;
    343     vertexKeyBuilder.finish();
    344     indexKeyBuilder.finish();
    345 
    346     // Try to grab data from the cache.
    347     sk_sp<GrGpuBuffer> vertexBuffer = rp->findByUniqueKey<GrGpuBuffer>(vertexKey);
    348     sk_sp<GrGpuBuffer> indexBuffer =
    349             this->isIndexed() ? rp->findByUniqueKey<GrGpuBuffer>(indexKey) : nullptr;
    350 
    351     // Draw using the cached buffers if possible.
    352     if (vertexBuffer && (!this->isIndexed() || indexBuffer)) {
    353         this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0,
    354                            std::move(indexBuffer), 0);
    355         return;
    356     }
    357 
    358     // Allocate vertex buffer.
    359     size_t vertexStride = gp->vertexStride();
    360     vertexBuffer = rp->createBuffer(
    361             fVertexCount * vertexStride, GrGpuBufferType::kVertex, kStatic_GrAccessPattern);
    362     void* verts = vertexBuffer ? vertexBuffer->map() : nullptr;
    363     if (!verts) {
    364         SkDebugf("Could not allocate vertices\n");
    365         return;
    366     }
    367 
    368     // Allocate index buffer.
    369     uint16_t* indices = nullptr;
    370     if (this->isIndexed()) {
    371         indexBuffer = rp->createBuffer(
    372                 fIndexCount * sizeof(uint16_t), GrGpuBufferType::kIndex, kStatic_GrAccessPattern);
    373         indices = indexBuffer ? static_cast<uint16_t*>(indexBuffer->map()) : nullptr;
    374         if (!indices) {
    375             SkDebugf("Could not allocate indices\n");
    376             return;
    377         }
    378     }
    379 
    380     // Fill the buffers.
    381     this->fillBuffers(hasColorAttribute,
    382                       hasLocalCoordsAttribute,
    383                       vertexStride,
    384                       verts,
    385                       indices);
    386 
    387     // Unmap the buffers.
    388     vertexBuffer->unmap();
    389     if (indexBuffer) {
    390         indexBuffer->unmap();
    391     }
    392 
    393     // Cache the buffers.
    394     rp->assignUniqueKeyToResource(vertexKey, vertexBuffer.get());
    395     rp->assignUniqueKeyToResource(indexKey, indexBuffer.get());
    396 
    397     // Draw the vertices.
    398     this->drawVertices(target, std::move(gp), std::move(vertexBuffer), 0, std::move(indexBuffer),
    399                        0);
    400 }
    401 
    402 void DrawVerticesOp::fillBuffers(bool hasColorAttribute,
    403                                  bool hasLocalCoordsAttribute,
    404                                  size_t vertexStride,
    405                                  void* verts,
    406                                  uint16_t* indices) const {
    407     int instanceCount = fMeshes.count();
    408 
    409     // Copy data into the buffers.
    410     int vertexOffset = 0;
    411     // We have a fast case below for uploading the vertex data when the matrix is translate
    412     // only and there are colors but not local coords.
    413     bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute;
    414     for (int i = 0; i < instanceCount; i++) {
    415         // Get each mesh.
    416         const Mesh& mesh = fMeshes[i];
    417 
    418         // Copy data into the index buffer.
    419         if (indices) {
    420             int indexCount = mesh.fVertices->indexCount();
    421             for (int j = 0; j < indexCount; ++j) {
    422                 *indices++ = mesh.fVertices->indices()[j] + vertexOffset;
    423             }
    424         }
    425 
    426         // Copy data into the vertex buffer.
    427         int vertexCount = mesh.fVertices->vertexCount();
    428         const SkPoint* positions = mesh.fVertices->positions();
    429         const SkColor* colors = mesh.fVertices->colors();
    430         const SkPoint* localCoords = mesh.fVertices->texCoords();
    431         bool fastMesh = (!this->hasMultipleViewMatrices() ||
    432                          mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
    433                         mesh.hasPerVertexColors();
    434         if (fastAttrs && fastMesh) {
    435             // Fast case.
    436             struct V {
    437                 SkPoint fPos;
    438                 uint32_t fColor;
    439             };
    440             SkASSERT(sizeof(V) == vertexStride);
    441             V* v = (V*)verts;
    442             Sk2f t(0, 0);
    443             if (this->hasMultipleViewMatrices()) {
    444                 t = Sk2f(mesh.fViewMatrix.getTranslateX(), mesh.fViewMatrix.getTranslateY());
    445             }
    446             for (int j = 0; j < vertexCount; ++j) {
    447                 Sk2f p = Sk2f::Load(positions++) + t;
    448                 p.store(&v[j].fPos);
    449                 v[j].fColor = colors[j];
    450             }
    451             verts = v + vertexCount;
    452         } else {
    453             // Normal case.
    454             static constexpr size_t kColorOffset = sizeof(SkPoint);
    455             size_t offset = kColorOffset;
    456             if (hasColorAttribute) {
    457                 offset += sizeof(uint32_t);
    458             }
    459             size_t localCoordOffset = offset;
    460             if (hasLocalCoordsAttribute) {
    461                 offset += sizeof(SkPoint);
    462             }
    463 
    464             // TODO4F: Preserve float colors
    465             GrColor color = mesh.fColor.toBytes_RGBA();
    466 
    467             for (int j = 0; j < vertexCount; ++j) {
    468                 if (this->hasMultipleViewMatrices()) {
    469                     mesh.fViewMatrix.mapPoints(((SkPoint*)verts), &positions[j], 1);
    470                 } else {
    471                     *((SkPoint*)verts) = positions[j];
    472                 }
    473                 if (hasColorAttribute) {
    474                     if (mesh.hasPerVertexColors()) {
    475                         *(uint32_t*)((intptr_t)verts + kColorOffset) = colors[j];
    476                     } else {
    477                         *(uint32_t*)((intptr_t)verts + kColorOffset) = color;
    478                     }
    479                 }
    480                 if (hasLocalCoordsAttribute) {
    481                     if (mesh.hasExplicitLocalCoords()) {
    482                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = localCoords[j];
    483                     } else {
    484                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
    485                     }
    486                 }
    487                 verts = (void*)((intptr_t)verts + vertexStride);
    488             }
    489         }
    490         vertexOffset += vertexCount;
    491     }
    492 }
    493 
    494 void DrawVerticesOp::drawVertices(Target* target,
    495                                   sk_sp<const GrGeometryProcessor> gp,
    496                                   sk_sp<const GrBuffer> vertexBuffer,
    497                                   int firstVertex,
    498                                   sk_sp<const GrBuffer> indexBuffer,
    499                                   int firstIndex) {
    500     GrMesh* mesh = target->allocMesh(this->primitiveType());
    501     if (this->isIndexed()) {
    502         mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertexCount - 1,
    503                          GrPrimitiveRestart::kNo);
    504     } else {
    505         mesh->setNonIndexedNonInstanced(fVertexCount);
    506     }
    507     mesh->setVertexData(std::move(vertexBuffer), firstVertex);
    508     target->recordDraw(std::move(gp), mesh);
    509 }
    510 
    511 void DrawVerticesOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
    512     fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
    513 }
    514 
    515 GrOp::CombineResult DrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
    516     DrawVerticesOp* that = t->cast<DrawVerticesOp>();
    517 
    518     if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
    519         return CombineResult::kCannotCombine;
    520     }
    521 
    522     // Non-volatile meshes cannot batch, because if a non-volatile mesh batches with another mesh,
    523     // then on the next frame, if that non-volatile mesh is drawn, it will draw the other mesh
    524     // that was saved in its vertex buffer, which is not necessarily there anymore.
    525     if (!this->fMeshes[0].fVertices->isVolatile() || !that->fMeshes[0].fVertices->isVolatile()) {
    526         return CombineResult::kCannotCombine;
    527     }
    528 
    529     if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
    530         return CombineResult::kCannotCombine;
    531     }
    532 
    533     if (fMeshes[0].fVertices->hasIndices() != that->fMeshes[0].fVertices->hasIndices()) {
    534         return CombineResult::kCannotCombine;
    535     }
    536 
    537     if (fColorArrayType != that->fColorArrayType) {
    538         return CombineResult::kCannotCombine;
    539     }
    540 
    541     if (fVertexCount + that->fVertexCount > SkTo<int>(UINT16_MAX)) {
    542         return CombineResult::kCannotCombine;
    543     }
    544 
    545     // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
    546     // gamut is determined by the render target context. A mis-match should be impossible.
    547     SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
    548 
    549     // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
    550     // with multiple view matrices.
    551     fFlags |= that->fFlags;
    552 
    553     if (!this->requiresPerVertexColors() && this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
    554         fFlags |= kRequiresPerVertexColors_Flag;
    555     }
    556     // Check whether we are about to acquire a mesh with a different view matrix.
    557     if (!this->hasMultipleViewMatrices() &&
    558         !this->fMeshes[0].fViewMatrix.cheapEqualTo(that->fMeshes[0].fViewMatrix)) {
    559         fFlags |= kHasMultipleViewMatrices_Flag;
    560     }
    561 
    562     fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
    563     fVertexCount += that->fVertexCount;
    564     fIndexCount += that->fIndexCount;
    565 
    566     return CombineResult::kMerged;
    567 }
    568 
    569 } // anonymous namespace
    570 
    571 std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrRecordingContext* context,
    572                                                  GrPaint&& paint,
    573                                                  sk_sp<SkVertices> vertices,
    574                                                  const SkVertices::Bone bones[],
    575                                                  int boneCount,
    576                                                  const SkMatrix& viewMatrix,
    577                                                  GrAAType aaType,
    578                                                  sk_sp<GrColorSpaceXform> colorSpaceXform,
    579                                                  GrPrimitiveType* overridePrimType) {
    580     SkASSERT(vertices);
    581     GrPrimitiveType primType = overridePrimType ? *overridePrimType
    582                                                 : SkVertexModeToGrPrimitiveType(vertices->mode());
    583     return GrSimpleMeshDrawOpHelper::FactoryHelper<DrawVerticesOp>(context, std::move(paint),
    584                                                                    std::move(vertices),
    585                                                                    bones, boneCount,
    586                                                                    primType, aaType,
    587                                                                    std::move(colorSpaceXform),
    588                                                                    viewMatrix);
    589 }
    590 
    591 ///////////////////////////////////////////////////////////////////////////////////////////////////
    592 
    593 #if GR_TEST_UTILS
    594 
    595 #include "GrDrawOpTest.h"
    596 
    597 static uint32_t seed_vertices(GrPrimitiveType type) {
    598     switch (type) {
    599         case GrPrimitiveType::kTriangles:
    600         case GrPrimitiveType::kTriangleStrip:
    601             return 3;
    602         case GrPrimitiveType::kPoints:
    603             return 1;
    604         case GrPrimitiveType::kLines:
    605         case GrPrimitiveType::kLineStrip:
    606             return 2;
    607         case GrPrimitiveType::kLinesAdjacency:
    608             return 4;
    609     }
    610     SK_ABORT("Incomplete switch\n");
    611     return 0;
    612 }
    613 
    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     return 0;
    629 }
    630 
    631 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
    632     SkPoint p;
    633     p.fX = random->nextRangeScalar(min, max);
    634     p.fY = random->nextRangeScalar(min, max);
    635     return p;
    636 }
    637 
    638 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
    639                              SkRandom* random, SkTArray<SkPoint>* positions,
    640                              SkTArray<SkPoint>* texCoords, bool hasTexCoords,
    641                              SkTArray<uint32_t>* colors, bool hasColors,
    642                              SkTArray<uint16_t>* indices, bool hasIndices) {
    643     for (uint32_t v = 0; v < count; v++) {
    644         positions->push_back(random_point(random, min, max));
    645         if (hasTexCoords) {
    646             texCoords->push_back(random_point(random, min, max));
    647         }
    648         if (hasColors) {
    649             colors->push_back(GrRandomColor(random));
    650         }
    651         if (hasIndices) {
    652             SkASSERT(maxVertex <= UINT16_MAX);
    653             indices->push_back(random->nextULessThan((uint16_t)maxVertex));
    654         }
    655     }
    656 }
    657 
    658 GR_DRAW_OP_TEST_DEFINE(DrawVerticesOp) {
    659     GrPrimitiveType type;
    660     do {
    661        type = GrPrimitiveType(random->nextULessThan(kNumGrPrimitiveTypes));
    662     } while (GrPrimTypeRequiresGeometryShaderSupport(type) &&
    663              !context->priv().caps()->shaderCaps()->geometryShaderSupport());
    664 
    665     uint32_t primitiveCount = random->nextRangeU(1, 100);
    666 
    667     // TODO make 'sensible' indexbuffers
    668     SkTArray<SkPoint> positions;
    669     SkTArray<SkPoint> texCoords;
    670     SkTArray<uint32_t> colors;
    671     SkTArray<uint16_t> indices;
    672 
    673     bool hasTexCoords = random->nextBool();
    674     bool hasIndices = random->nextBool();
    675     bool hasColors = random->nextBool();
    676 
    677     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
    678 
    679     static const SkScalar kMinVertExtent = -100.f;
    680     static const SkScalar kMaxVertExtent = 100.f;
    681     randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
    682                      &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
    683                      hasIndices);
    684 
    685     for (uint32_t i = 1; i < primitiveCount; i++) {
    686         randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
    687                          random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
    688                          hasIndices);
    689     }
    690 
    691     SkMatrix viewMatrix = GrTest::TestMatrix(random);
    692 
    693     sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
    694 
    695     static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
    696     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
    697                                                       texCoords.begin(), colors.begin(),
    698                                                       hasIndices ? indices.count() : 0,
    699                                                       indices.begin());
    700     GrAAType aaType = GrAAType::kNone;
    701     if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
    702         aaType = GrAAType::kMSAA;
    703     }
    704     return GrDrawVerticesOp::Make(context, std::move(paint), std::move(vertices), nullptr, 0,
    705                                   viewMatrix, aaType, std::move(colorSpaceXform), &type);
    706 }
    707 
    708 #endif
    709