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