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 "GrDefaultGeoProcFactory.h"
10 #include "GrOpFlushState.h"
11 #include "SkGr.h"
12 
Make(GrColor color,GrPrimitiveType primitiveType,const SkMatrix & viewMatrix,const SkPoint * positions,int vertexCount,const uint16_t * indices,int indexCount,const uint32_t * colors,const SkPoint * localCoords,const SkRect & bounds,GrRenderTargetContext::ColorArrayType colorArrayType)13 std::unique_ptr<GrMeshDrawOp> GrDrawVerticesOp::Make(
14         GrColor color, GrPrimitiveType primitiveType, const SkMatrix& viewMatrix,
15         const SkPoint* positions, int vertexCount, const uint16_t* indices, int indexCount,
16         const uint32_t* colors, const SkPoint* localCoords, const SkRect& bounds,
17         GrRenderTargetContext::ColorArrayType colorArrayType) {
18     static constexpr SkCanvas::VertexMode kIgnoredMode = SkCanvas::kTriangles_VertexMode;
19     SkASSERT(positions);
20     if (!colors) {
21         // When we tessellate we will fill a color array with the GrColor value passed above as
22         // 'color'.
23         colorArrayType = GrRenderTargetContext::ColorArrayType::kPremulGrColor;
24     }
25     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions,
26                                                       localCoords, colors, indexCount, indices);
27     if (!vertices) {
28         return nullptr;
29     }
30     return std::unique_ptr<GrMeshDrawOp>(new GrDrawVerticesOp(std::move(vertices), primitiveType,
31                                                               color, colorArrayType, viewMatrix));
32 }
33 
Make(GrColor color,sk_sp<SkVertices> vertices,const SkMatrix & viewMatrix)34 std::unique_ptr<GrMeshDrawOp> GrDrawVerticesOp::Make(GrColor color, sk_sp<SkVertices> vertices,
35                                                      const SkMatrix& viewMatrix) {
36     SkASSERT(vertices);
37     GrPrimitiveType primType = SkVertexModeToGrPrimitiveType(vertices->mode());
38     return std::unique_ptr<GrMeshDrawOp>(new GrDrawVerticesOp(
39             std::move(vertices), primType, color, GrRenderTargetContext::ColorArrayType::kSkColor,
40             viewMatrix));
41 }
42 
GrDrawVerticesOp(sk_sp<SkVertices> vertices,GrPrimitiveType primitiveType,GrColor color,GrRenderTargetContext::ColorArrayType colorArrayType,const SkMatrix & viewMatrix,uint32_t flags)43 GrDrawVerticesOp::GrDrawVerticesOp(sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType,
44                                    GrColor color,
45                                    GrRenderTargetContext::ColorArrayType colorArrayType,
46                                    const SkMatrix& viewMatrix, uint32_t flags)
47         : INHERITED(ClassID()), fColorArrayType(colorArrayType) {
48     SkASSERT(vertices);
49 
50     fVertexCount = vertices->vertexCount();
51     fIndexCount = vertices->indexCount();
52     fPrimitiveType = primitiveType;
53 
54     Mesh& mesh = fMeshes.push_back();
55     mesh.fColor = color;
56     mesh.fViewMatrix = viewMatrix;
57     mesh.fVertices = std::move(vertices);
58     mesh.fFlags = flags;
59 
60     fFlags = 0;
61     if (mesh.hasPerVertexColors()) {
62         fFlags |= kRequiresPerVertexColors_Flag;
63     }
64     if (mesh.hasExplicitLocalCoords()) {
65         fFlags |= kAnyMeshHasExplicitLocalCoords;
66     }
67 
68     IsZeroArea zeroArea;
69     if (GrIsPrimTypeLines(primitiveType) || kPoints_GrPrimitiveType == primitiveType) {
70         zeroArea = IsZeroArea::kYes;
71     } else {
72         zeroArea = IsZeroArea::kNo;
73     }
74     this->setTransformedBounds(mesh.fVertices->bounds(), viewMatrix, HasAABloat::kNo, zeroArea);
75 }
76 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const77 void GrDrawVerticesOp::getFragmentProcessorAnalysisInputs(
78         GrPipelineAnalysisColor* color, GrPipelineAnalysisCoverage* coverage) const {
79     if (this->requiresPerVertexColors()) {
80         color->setToUnknown();
81     } else {
82         color->setToConstant(fMeshes[0].fColor);
83     }
84     *coverage = GrPipelineAnalysisCoverage::kNone;
85 }
86 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)87 void GrDrawVerticesOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
88     SkASSERT(fMeshes.count() == 1);
89     GrColor overrideColor;
90     if (optimizations.getOverrideColorIfSet(&overrideColor)) {
91         fMeshes[0].fColor = overrideColor;
92         fMeshes[0].fFlags |= kIgnoreColors_VerticesFlag;
93         fFlags &= ~kRequiresPerVertexColors_Flag;
94         fColorArrayType = GrRenderTargetContext::ColorArrayType::kPremulGrColor;
95     }
96     if (optimizations.readsLocalCoords()) {
97         fFlags |= kPipelineRequiresLocalCoords_Flag;
98     } else {
99         fFlags |= kIgnoreTexCoords_VerticesFlag;
100         fFlags &= ~kAnyMeshHasExplicitLocalCoords;
101     }
102 }
103 
makeGP(bool * hasColorAttribute,bool * hasLocalCoordAttribute) const104 sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
105                                                     bool* hasLocalCoordAttribute) const {
106     using namespace GrDefaultGeoProcFactory;
107     LocalCoords::Type localCoordsType;
108     if (this->pipelineRequiresLocalCoords()) {
109         // If we have multiple view matrices we will transform the positions into device space. We
110         // must then also provide untransformed positions as local coords.
111         if (this->anyMeshHasExplicitLocalCoords() || this->hasMultipleViewMatrices()) {
112             *hasLocalCoordAttribute = true;
113             localCoordsType = LocalCoords::kHasExplicit_Type;
114         } else {
115             *hasLocalCoordAttribute = false;
116             localCoordsType = LocalCoords::kUsePosition_Type;
117         }
118     } else {
119         localCoordsType = LocalCoords::kUnused_Type;
120         *hasLocalCoordAttribute = false;
121     }
122 
123     Color color(fMeshes[0].fColor);
124     if (this->requiresPerVertexColors()) {
125         color.fType = (fColorArrayType == GrRenderTargetContext::ColorArrayType::kPremulGrColor)
126                               ? Color::kPremulGrColorAttribute_Type
127                               : Color::kUnpremulSkColorAttribute_Type;
128         *hasColorAttribute = true;
129     } else {
130         *hasColorAttribute = false;
131     };
132     const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
133     return GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType, vm);
134 }
135 
onPrepareDraws(Target * target) const136 void GrDrawVerticesOp::onPrepareDraws(Target* target) const {
137     bool hasColorAttribute;
138     bool hasLocalCoordsAttribute;
139     sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute, &hasLocalCoordsAttribute);
140     size_t vertexStride = gp->getVertexStride();
141 
142     SkASSERT(vertexStride == sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
143                                      (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0));
144 
145     int instanceCount = fMeshes.count();
146 
147     const GrBuffer* vertexBuffer;
148     int firstVertex;
149 
150     void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
151 
152     if (!verts) {
153         SkDebugf("Could not allocate vertices\n");
154         return;
155     }
156 
157     const GrBuffer* indexBuffer = nullptr;
158     int firstIndex = 0;
159 
160     uint16_t* indices = nullptr;
161     if (this->isIndexed()) {
162         indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
163 
164         if (!indices) {
165             SkDebugf("Could not allocate indices\n");
166             return;
167         }
168     }
169 
170     int vertexOffset = 0;
171     // We have a fast case below for uploading the vertex data when the matrix is translate
172     // only and there are colors but not local coords.
173     bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute;
174     for (int i = 0; i < instanceCount; i++) {
175         const Mesh& mesh = fMeshes[i];
176         if (indices) {
177             int indexCount = mesh.fVertices->indexCount();
178             for (int j = 0; j < indexCount; ++j) {
179                 *indices++ = mesh.fVertices->indices()[j] + vertexOffset;
180             }
181         }
182         int vertexCount = mesh.fVertices->vertexCount();
183         const SkPoint* positions = mesh.fVertices->positions();
184         const SkColor* colors = mesh.fVertices->colors();
185         const SkPoint* localCoords = mesh.fVertices->texCoords();
186         bool fastMesh = (!this->hasMultipleViewMatrices() ||
187                          mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
188                         mesh.hasPerVertexColors();
189         if (fastAttrs && fastMesh) {
190             struct V {
191                 SkPoint fPos;
192                 uint32_t fColor;
193             };
194             SkASSERT(sizeof(V) == vertexStride);
195             V* v = (V*)verts;
196             Sk2f t(0, 0);
197             if (this->hasMultipleViewMatrices()) {
198                 t = Sk2f(mesh.fViewMatrix.getTranslateX(), mesh.fViewMatrix.getTranslateY());
199             }
200             for (int j = 0; j < vertexCount; ++j) {
201                 Sk2f p = Sk2f::Load(positions++) + t;
202                 p.store(&v[j].fPos);
203                 v[j].fColor = colors[j];
204             }
205             verts = v + vertexCount;
206         } else {
207             static constexpr size_t kColorOffset = sizeof(SkPoint);
208             size_t localCoordOffset =
209                     hasColorAttribute ? kColorOffset + sizeof(uint32_t) : kColorOffset;
210 
211             for (int j = 0; j < vertexCount; ++j) {
212                 if (this->hasMultipleViewMatrices()) {
213                     mesh.fViewMatrix.mapPoints(((SkPoint*)verts), &positions[j], 1);
214                 } else {
215                     *((SkPoint*)verts) = positions[j];
216                 }
217                 if (hasColorAttribute) {
218                     if (mesh.hasPerVertexColors()) {
219                         *(uint32_t*)((intptr_t)verts + kColorOffset) = colors[j];
220                     } else {
221                         *(uint32_t*)((intptr_t)verts + kColorOffset) = mesh.fColor;
222                     }
223                 }
224                 if (hasLocalCoordsAttribute) {
225                     if (mesh.hasExplicitLocalCoords()) {
226                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = localCoords[j];
227                     } else {
228                         *(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
229                     }
230                 }
231                 verts = (void*)((intptr_t)verts + vertexStride);
232             }
233         }
234         vertexOffset += vertexCount;
235     }
236 
237     GrMesh mesh;
238     if (indices) {
239         mesh.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex, firstIndex,
240                          fVertexCount, fIndexCount);
241 
242     } else {
243         mesh.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount);
244     }
245     target->draw(gp.get(), mesh);
246 }
247 
onCombineIfPossible(GrOp * t,const GrCaps & caps)248 bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
249     GrDrawVerticesOp* that = t->cast<GrDrawVerticesOp>();
250 
251     if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
252                                 that->bounds(), caps)) {
253         return false;
254     }
255 
256     if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
257         return false;
258     }
259 
260     if (fMeshes[0].fVertices->hasIndices() != that->fMeshes[0].fVertices->hasIndices()) {
261         return false;
262     }
263 
264     if (fColorArrayType != that->fColorArrayType) {
265         return false;
266     }
267 
268     if (fVertexCount + that->fVertexCount > SK_MaxU16) {
269         return false;
270     }
271 
272     // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
273     // with multiple view matrices.
274     fFlags |= that->fFlags;
275 
276     if (!this->requiresPerVertexColors() && this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
277         fFlags |= kRequiresPerVertexColors_Flag;
278     }
279     // Check whether we are about to acquire a mesh with a different view matrix.
280     if (!this->hasMultipleViewMatrices() &&
281         !this->fMeshes[0].fViewMatrix.cheapEqualTo(that->fMeshes[0].fViewMatrix)) {
282         fFlags |= kHasMultipleViewMatrices_Flag;
283     }
284 
285     fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
286     fVertexCount += that->fVertexCount;
287     fIndexCount += that->fIndexCount;
288 
289     this->joinBounds(*that);
290     return true;
291 }
292 
293 ///////////////////////////////////////////////////////////////////////////////////////////////////
294 
295 #if GR_TEST_UTILS
296 
297 #include "GrDrawOpTest.h"
298 
seed_vertices(GrPrimitiveType type)299 static uint32_t seed_vertices(GrPrimitiveType type) {
300     switch (type) {
301         case kTriangles_GrPrimitiveType:
302         case kTriangleStrip_GrPrimitiveType:
303         case kTriangleFan_GrPrimitiveType:
304             return 3;
305         case kPoints_GrPrimitiveType:
306             return 1;
307         case kLines_GrPrimitiveType:
308         case kLineStrip_GrPrimitiveType:
309             return 2;
310     }
311     SkFAIL("Incomplete switch\n");
312     return 0;
313 }
314 
primitive_vertices(GrPrimitiveType type)315 static uint32_t primitive_vertices(GrPrimitiveType type) {
316     switch (type) {
317         case kTriangles_GrPrimitiveType:
318             return 3;
319         case kLines_GrPrimitiveType:
320             return 2;
321         case kTriangleStrip_GrPrimitiveType:
322         case kTriangleFan_GrPrimitiveType:
323         case kPoints_GrPrimitiveType:
324         case kLineStrip_GrPrimitiveType:
325             return 1;
326     }
327     SkFAIL("Incomplete switch\n");
328     return 0;
329 }
330 
random_point(SkRandom * random,SkScalar min,SkScalar max)331 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
332     SkPoint p;
333     p.fX = random->nextRangeScalar(min, max);
334     p.fY = random->nextRangeScalar(min, max);
335     return p;
336 }
337 
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)338 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
339                              SkRandom* random, SkTArray<SkPoint>* positions,
340                              SkTArray<SkPoint>* texCoords, bool hasTexCoords,
341                              SkTArray<uint32_t>* colors, bool hasColors,
342                              SkTArray<uint16_t>* indices, bool hasIndices) {
343     for (uint32_t v = 0; v < count; v++) {
344         positions->push_back(random_point(random, min, max));
345         if (hasTexCoords) {
346             texCoords->push_back(random_point(random, min, max));
347         }
348         if (hasColors) {
349             colors->push_back(GrRandomColor(random));
350         }
351         if (hasIndices) {
352             SkASSERT(maxVertex <= SK_MaxU16);
353             indices->push_back(random->nextULessThan((uint16_t)maxVertex));
354         }
355     }
356 }
357 
DRAW_OP_TEST_DEFINE(VerticesOp)358 DRAW_OP_TEST_DEFINE(VerticesOp) {
359     GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
360     uint32_t primitiveCount = random->nextRangeU(1, 100);
361 
362     // TODO make 'sensible' indexbuffers
363     SkTArray<SkPoint> positions;
364     SkTArray<SkPoint> texCoords;
365     SkTArray<uint32_t> colors;
366     SkTArray<uint16_t> indices;
367 
368     bool hasTexCoords = random->nextBool();
369     bool hasIndices = random->nextBool();
370     bool hasColors = random->nextBool();
371 
372     uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
373 
374     static const SkScalar kMinVertExtent = -100.f;
375     static const SkScalar kMaxVertExtent = 100.f;
376     randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
377                      &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
378                      hasIndices);
379 
380     for (uint32_t i = 1; i < primitiveCount; i++) {
381         randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
382                          random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
383                          hasIndices);
384     }
385 
386     GrRenderTargetContext::ColorArrayType colorArrayType =
387             random->nextBool() ? GrRenderTargetContext::ColorArrayType::kPremulGrColor
388                                : GrRenderTargetContext::ColorArrayType::kSkColor;
389     SkMatrix viewMatrix = GrTest::TestMatrix(random);
390     SkRect bounds;
391     SkDEBUGCODE(bool result =) bounds.setBoundsCheck(positions.begin(), vertexCount);
392     SkASSERT(result);
393 
394     GrColor color = GrRandomColor(random);
395     return GrDrawVerticesOp::Make(color, type, viewMatrix, positions.begin(), vertexCount,
396                                   indices.begin(), hasIndices ? indices.count() : 0, colors.begin(),
397                                   texCoords.begin(), bounds, colorArrayType);
398 }
399 
400 #endif
401