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
13std::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
34std::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
43GrDrawVerticesOp::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
77void 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
87void 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
104sk_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
136void 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
248bool 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
299static 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
315static 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
331static 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
338static 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
358DRAW_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