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(GrPaint && paint,sk_sp<SkVertices> vertices,const SkMatrix & viewMatrix,GrAAType aaType,bool gammaCorrect,sk_sp<GrColorSpaceXform> colorSpaceXform,GrPrimitiveType * overridePrimType)13 std::unique_ptr<GrDrawOp> GrDrawVerticesOp::Make(GrPaint&& paint,
14 sk_sp<SkVertices> vertices,
15 const SkMatrix& viewMatrix,
16 GrAAType aaType,
17 bool gammaCorrect,
18 sk_sp<GrColorSpaceXform> colorSpaceXform,
19 GrPrimitiveType* overridePrimType) {
20 SkASSERT(vertices);
21 GrPrimitiveType primType = overridePrimType ? *overridePrimType
22 : SkVertexModeToGrPrimitiveType(vertices->mode());
23 return Helper::FactoryHelper<GrDrawVerticesOp>(std::move(paint), std::move(vertices), primType,
24 aaType, gammaCorrect, std::move(colorSpaceXform),
25 viewMatrix);
26 }
27
GrDrawVerticesOp(const Helper::MakeArgs & helperArgs,GrColor color,sk_sp<SkVertices> vertices,GrPrimitiveType primitiveType,GrAAType aaType,bool gammaCorrect,sk_sp<GrColorSpaceXform> colorSpaceXform,const SkMatrix & viewMatrix)28 GrDrawVerticesOp::GrDrawVerticesOp(const Helper::MakeArgs& helperArgs, GrColor color,
29 sk_sp<SkVertices> vertices, GrPrimitiveType primitiveType,
30 GrAAType aaType, bool gammaCorrect,
31 sk_sp<GrColorSpaceXform> colorSpaceXform,
32 const SkMatrix& viewMatrix)
33 : INHERITED(ClassID())
34 , fHelper(helperArgs, aaType)
35 , fPrimitiveType(primitiveType)
36 , fColorSpaceXform(std::move(colorSpaceXform)) {
37 SkASSERT(vertices);
38
39 fVertexCount = vertices->vertexCount();
40 fIndexCount = vertices->indexCount();
41 fColorArrayType = vertices->hasColors() ? ColorArrayType::kSkColor
42 : ColorArrayType::kPremulGrColor;
43 // GrColor is linearized (and gamut converted) during paint conversion, but SkColors need to be
44 // handled in the shader
45 fLinearizeColors = gammaCorrect && vertices->hasColors();
46
47 Mesh& mesh = fMeshes.push_back();
48 mesh.fColor = color;
49 mesh.fViewMatrix = viewMatrix;
50 mesh.fVertices = std::move(vertices);
51 mesh.fIgnoreTexCoords = false;
52 mesh.fIgnoreColors = false;
53
54 fFlags = 0;
55 if (mesh.hasPerVertexColors()) {
56 fFlags |= kRequiresPerVertexColors_Flag;
57 }
58 if (mesh.hasExplicitLocalCoords()) {
59 fFlags |= kAnyMeshHasExplicitLocalCoords;
60 }
61
62 IsZeroArea zeroArea;
63 if (GrIsPrimTypeLines(primitiveType) || GrPrimitiveType::kPoints == primitiveType) {
64 zeroArea = IsZeroArea::kYes;
65 } else {
66 zeroArea = IsZeroArea::kNo;
67 }
68 this->setTransformedBounds(mesh.fVertices->bounds(), viewMatrix, HasAABloat::kNo, zeroArea);
69 }
70
dumpInfo() const71 SkString GrDrawVerticesOp::dumpInfo() const {
72 SkString string;
73 string.appendf("PrimType: %d, MeshCount %d, VCount: %d, ICount: %d\n", (int)fPrimitiveType,
74 fMeshes.count(), fVertexCount, fIndexCount);
75 string += fHelper.dumpInfo();
76 string += INHERITED::dumpInfo();
77 return string;
78 }
79
fixedFunctionFlags() const80 GrDrawOp::FixedFunctionFlags GrDrawVerticesOp::fixedFunctionFlags() const {
81 return fHelper.fixedFunctionFlags();
82 }
83
finalize(const GrCaps & caps,const GrAppliedClip * clip)84 GrDrawOp::RequiresDstTexture GrDrawVerticesOp::finalize(const GrCaps& caps,
85 const GrAppliedClip* clip) {
86 GrProcessorAnalysisColor gpColor;
87 if (this->requiresPerVertexColors()) {
88 gpColor.setToUnknown();
89 } else {
90 gpColor.setToConstant(fMeshes.front().fColor);
91 }
92 auto result =
93 fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, &gpColor);
94 if (gpColor.isConstant(&fMeshes.front().fColor)) {
95 fMeshes.front().fIgnoreColors = true;
96 fFlags &= ~kRequiresPerVertexColors_Flag;
97 fColorArrayType = ColorArrayType::kPremulGrColor;
98 fLinearizeColors = false;
99 }
100 if (!fHelper.usesLocalCoords()) {
101 fMeshes[0].fIgnoreTexCoords = true;
102 fFlags &= ~kAnyMeshHasExplicitLocalCoords;
103 }
104 return result;
105 }
106
makeGP(bool * hasColorAttribute,bool * hasLocalCoordAttribute) const107 sk_sp<GrGeometryProcessor> GrDrawVerticesOp::makeGP(bool* hasColorAttribute,
108 bool* hasLocalCoordAttribute) const {
109 using namespace GrDefaultGeoProcFactory;
110 LocalCoords::Type localCoordsType;
111 if (fHelper.usesLocalCoords()) {
112 // If we have multiple view matrices we will transform the positions into device space. We
113 // must then also provide untransformed positions as local coords.
114 if (this->anyMeshHasExplicitLocalCoords() || this->hasMultipleViewMatrices()) {
115 *hasLocalCoordAttribute = true;
116 localCoordsType = LocalCoords::kHasExplicit_Type;
117 } else {
118 *hasLocalCoordAttribute = false;
119 localCoordsType = LocalCoords::kUsePosition_Type;
120 }
121 } else {
122 localCoordsType = LocalCoords::kUnused_Type;
123 *hasLocalCoordAttribute = false;
124 }
125
126 Color color(fMeshes[0].fColor);
127 if (this->requiresPerVertexColors()) {
128 color.fType = (fColorArrayType == ColorArrayType::kPremulGrColor)
129 ? Color::kPremulGrColorAttribute_Type
130 : Color::kUnpremulSkColorAttribute_Type;
131 color.fLinearize = fLinearizeColors;
132 color.fColorSpaceXform = fColorSpaceXform;
133 *hasColorAttribute = true;
134 } else {
135 *hasColorAttribute = false;
136 };
137 const SkMatrix& vm = this->hasMultipleViewMatrices() ? SkMatrix::I() : fMeshes[0].fViewMatrix;
138 return GrDefaultGeoProcFactory::Make(color, Coverage::kSolid_Type, localCoordsType, vm);
139 }
140
onPrepareDraws(Target * target) const141 void GrDrawVerticesOp::onPrepareDraws(Target* target) const {
142 bool hasColorAttribute;
143 bool hasLocalCoordsAttribute;
144 sk_sp<GrGeometryProcessor> gp = this->makeGP(&hasColorAttribute, &hasLocalCoordsAttribute);
145 size_t vertexStride = gp->getVertexStride();
146
147 SkASSERT(vertexStride == sizeof(SkPoint) + (hasColorAttribute ? sizeof(uint32_t) : 0) +
148 (hasLocalCoordsAttribute ? sizeof(SkPoint) : 0));
149
150 int instanceCount = fMeshes.count();
151
152 const GrBuffer* vertexBuffer;
153 int firstVertex;
154
155 void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
156
157 if (!verts) {
158 SkDebugf("Could not allocate vertices\n");
159 return;
160 }
161
162 const GrBuffer* indexBuffer = nullptr;
163 int firstIndex = 0;
164
165 uint16_t* indices = nullptr;
166 if (this->isIndexed()) {
167 indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
168
169 if (!indices) {
170 SkDebugf("Could not allocate indices\n");
171 return;
172 }
173 }
174
175 int vertexOffset = 0;
176 // We have a fast case below for uploading the vertex data when the matrix is translate
177 // only and there are colors but not local coords.
178 bool fastAttrs = hasColorAttribute && !hasLocalCoordsAttribute;
179 for (int i = 0; i < instanceCount; i++) {
180 const Mesh& mesh = fMeshes[i];
181 if (indices) {
182 int indexCount = mesh.fVertices->indexCount();
183 for (int j = 0; j < indexCount; ++j) {
184 *indices++ = mesh.fVertices->indices()[j] + vertexOffset;
185 }
186 }
187 int vertexCount = mesh.fVertices->vertexCount();
188 const SkPoint* positions = mesh.fVertices->positions();
189 const SkColor* colors = mesh.fVertices->colors();
190 const SkPoint* localCoords = mesh.fVertices->texCoords();
191 bool fastMesh = (!this->hasMultipleViewMatrices() ||
192 mesh.fViewMatrix.getType() <= SkMatrix::kTranslate_Mask) &&
193 mesh.hasPerVertexColors();
194 if (fastAttrs && fastMesh) {
195 struct V {
196 SkPoint fPos;
197 uint32_t fColor;
198 };
199 SkASSERT(sizeof(V) == vertexStride);
200 V* v = (V*)verts;
201 Sk2f t(0, 0);
202 if (this->hasMultipleViewMatrices()) {
203 t = Sk2f(mesh.fViewMatrix.getTranslateX(), mesh.fViewMatrix.getTranslateY());
204 }
205 for (int j = 0; j < vertexCount; ++j) {
206 Sk2f p = Sk2f::Load(positions++) + t;
207 p.store(&v[j].fPos);
208 v[j].fColor = colors[j];
209 }
210 verts = v + vertexCount;
211 } else {
212 static constexpr size_t kColorOffset = sizeof(SkPoint);
213 size_t localCoordOffset =
214 hasColorAttribute ? kColorOffset + sizeof(uint32_t) : kColorOffset;
215
216 for (int j = 0; j < vertexCount; ++j) {
217 if (this->hasMultipleViewMatrices()) {
218 mesh.fViewMatrix.mapPoints(((SkPoint*)verts), &positions[j], 1);
219 } else {
220 *((SkPoint*)verts) = positions[j];
221 }
222 if (hasColorAttribute) {
223 if (mesh.hasPerVertexColors()) {
224 *(uint32_t*)((intptr_t)verts + kColorOffset) = colors[j];
225 } else {
226 *(uint32_t*)((intptr_t)verts + kColorOffset) = mesh.fColor;
227 }
228 }
229 if (hasLocalCoordsAttribute) {
230 if (mesh.hasExplicitLocalCoords()) {
231 *(SkPoint*)((intptr_t)verts + localCoordOffset) = localCoords[j];
232 } else {
233 *(SkPoint*)((intptr_t)verts + localCoordOffset) = positions[j];
234 }
235 }
236 verts = (void*)((intptr_t)verts + vertexStride);
237 }
238 }
239 vertexOffset += vertexCount;
240 }
241
242 GrMesh mesh(this->primitiveType());
243 if (!indices) {
244 mesh.setNonIndexedNonInstanced(fVertexCount);
245 } else {
246 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertexCount - 1);
247 }
248 mesh.setVertexData(vertexBuffer, firstVertex);
249 target->draw(gp.get(), fHelper.makePipeline(target), mesh);
250 }
251
onCombineIfPossible(GrOp * t,const GrCaps & caps)252 bool GrDrawVerticesOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
253 GrDrawVerticesOp* that = t->cast<GrDrawVerticesOp>();
254
255 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
256 return false;
257 }
258
259 if (!this->combinablePrimitive() || this->primitiveType() != that->primitiveType()) {
260 return false;
261 }
262
263 if (fMeshes[0].fVertices->hasIndices() != that->fMeshes[0].fVertices->hasIndices()) {
264 return false;
265 }
266
267 if (fColorArrayType != that->fColorArrayType) {
268 return false;
269 }
270
271 if (fLinearizeColors != that->fLinearizeColors) {
272 return false;
273 }
274
275 if (fVertexCount + that->fVertexCount > SK_MaxU16) {
276 return false;
277 }
278
279 // NOTE: For SkColor vertex colors, the source color space is always sRGB, and the destination
280 // gamut is determined by the render target context. A mis-match should be impossible.
281 SkASSERT(GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get()));
282
283 // If either op required explicit local coords or per-vertex colors the combined mesh does. Same
284 // with multiple view matrices.
285 fFlags |= that->fFlags;
286
287 if (!this->requiresPerVertexColors() && this->fMeshes[0].fColor != that->fMeshes[0].fColor) {
288 fFlags |= kRequiresPerVertexColors_Flag;
289 }
290 // Check whether we are about to acquire a mesh with a different view matrix.
291 if (!this->hasMultipleViewMatrices() &&
292 !this->fMeshes[0].fViewMatrix.cheapEqualTo(that->fMeshes[0].fViewMatrix)) {
293 fFlags |= kHasMultipleViewMatrices_Flag;
294 }
295
296 fMeshes.push_back_n(that->fMeshes.count(), that->fMeshes.begin());
297 fVertexCount += that->fVertexCount;
298 fIndexCount += that->fIndexCount;
299
300 this->joinBounds(*that);
301 return true;
302 }
303
304 ///////////////////////////////////////////////////////////////////////////////////////////////////
305
306 #if GR_TEST_UTILS
307
308 #include "GrDrawOpTest.h"
309
seed_vertices(GrPrimitiveType type)310 static uint32_t seed_vertices(GrPrimitiveType type) {
311 switch (type) {
312 case GrPrimitiveType::kTriangles:
313 case GrPrimitiveType::kTriangleStrip:
314 case GrPrimitiveType::kTriangleFan:
315 return 3;
316 case GrPrimitiveType::kPoints:
317 return 1;
318 case GrPrimitiveType::kLines:
319 case GrPrimitiveType::kLineStrip:
320 return 2;
321 case GrPrimitiveType::kLinesAdjacency:
322 return 4;
323 }
324 SkFAIL("Incomplete switch\n");
325 return 0;
326 }
327
primitive_vertices(GrPrimitiveType type)328 static uint32_t primitive_vertices(GrPrimitiveType type) {
329 switch (type) {
330 case GrPrimitiveType::kTriangles:
331 return 3;
332 case GrPrimitiveType::kLines:
333 return 2;
334 case GrPrimitiveType::kTriangleStrip:
335 case GrPrimitiveType::kTriangleFan:
336 case GrPrimitiveType::kPoints:
337 case GrPrimitiveType::kLineStrip:
338 return 1;
339 case GrPrimitiveType::kLinesAdjacency:
340 return 4;
341 }
342 SkFAIL("Incomplete switch\n");
343 return 0;
344 }
345
random_point(SkRandom * random,SkScalar min,SkScalar max)346 static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
347 SkPoint p;
348 p.fX = random->nextRangeScalar(min, max);
349 p.fY = random->nextRangeScalar(min, max);
350 return p;
351 }
352
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)353 static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
354 SkRandom* random, SkTArray<SkPoint>* positions,
355 SkTArray<SkPoint>* texCoords, bool hasTexCoords,
356 SkTArray<uint32_t>* colors, bool hasColors,
357 SkTArray<uint16_t>* indices, bool hasIndices) {
358 for (uint32_t v = 0; v < count; v++) {
359 positions->push_back(random_point(random, min, max));
360 if (hasTexCoords) {
361 texCoords->push_back(random_point(random, min, max));
362 }
363 if (hasColors) {
364 colors->push_back(GrRandomColor(random));
365 }
366 if (hasIndices) {
367 SkASSERT(maxVertex <= SK_MaxU16);
368 indices->push_back(random->nextULessThan((uint16_t)maxVertex));
369 }
370 }
371 }
372
GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp)373 GR_DRAW_OP_TEST_DEFINE(GrDrawVerticesOp) {
374 GrPrimitiveType type;
375 do {
376 type = GrPrimitiveType(random->nextULessThan(kNumGrPrimitiveTypes));
377 } while (GrPrimTypeRequiresGeometryShaderSupport(type) &&
378 !context->caps()->shaderCaps()->geometryShaderSupport());
379
380 uint32_t primitiveCount = random->nextRangeU(1, 100);
381
382 // TODO make 'sensible' indexbuffers
383 SkTArray<SkPoint> positions;
384 SkTArray<SkPoint> texCoords;
385 SkTArray<uint32_t> colors;
386 SkTArray<uint16_t> indices;
387
388 bool hasTexCoords = random->nextBool();
389 bool hasIndices = random->nextBool();
390 bool hasColors = random->nextBool();
391 bool linearizeColors = random->nextBool();
392
393 uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
394
395 static const SkScalar kMinVertExtent = -100.f;
396 static const SkScalar kMaxVertExtent = 100.f;
397 randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent, random,
398 &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
399 hasIndices);
400
401 for (uint32_t i = 1; i < primitiveCount; i++) {
402 randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
403 random, &positions, &texCoords, hasTexCoords, &colors, hasColors, &indices,
404 hasIndices);
405 }
406
407 SkMatrix viewMatrix = GrTest::TestMatrix(random);
408
409 sk_sp<GrColorSpaceXform> colorSpaceXform = GrTest::TestColorXform(random);
410
411 static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
412 sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, vertexCount, positions.begin(),
413 texCoords.begin(), colors.begin(),
414 hasIndices ? indices.count() : 0,
415 indices.begin());
416 GrAAType aaType = GrAAType::kNone;
417 if (GrFSAAType::kUnifiedMSAA == fsaaType && random->nextBool()) {
418 aaType = GrAAType::kMSAA;
419 }
420 return GrDrawVerticesOp::Make(std::move(paint), std::move(vertices), viewMatrix, aaType,
421 linearizeColors, std::move(colorSpaceXform), &type);
422 }
423
424 #endif
425