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