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