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