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 "GrDrawVerticesBatch.h"
9
10#include "GrBatchFlushState.h"
11#include "GrInvariantOutput.h"
12#include "GrDefaultGeoProcFactory.h"
13
14static const GrGeometryProcessor* set_vertex_attributes(bool hasLocalCoords,
15                                                        int* colorOffset,
16                                                        int* texOffset,
17                                                        const SkMatrix& viewMatrix,
18                                                        bool coverageIgnored) {
19    using namespace GrDefaultGeoProcFactory;
20    *texOffset = -1;
21    *colorOffset = -1;
22
23    Coverage coverage(coverageIgnored ? Coverage::kNone_Type : Coverage::kSolid_Type);
24    LocalCoords localCoords(hasLocalCoords ? LocalCoords::kHasExplicit_Type :
25                                             LocalCoords::kUsePosition_Type);
26    *colorOffset = sizeof(SkPoint);
27    if (hasLocalCoords) {
28        *texOffset = sizeof(SkPoint) + sizeof(GrColor);
29    }
30    return GrDefaultGeoProcFactory::Create(Color(Color::kAttribute_Type),
31                                           coverage, localCoords, viewMatrix);
32}
33
34GrDrawVerticesBatch::GrDrawVerticesBatch(const Geometry& geometry, GrPrimitiveType primitiveType,
35                                         const SkMatrix& viewMatrix,
36                                         const SkPoint* positions, int vertexCount,
37                                         const uint16_t* indices, int indexCount,
38                                         const GrColor* colors, const SkPoint* localCoords,
39                                         const SkRect& bounds)
40    : INHERITED(ClassID()) {
41    SkASSERT(positions);
42
43    fViewMatrix = viewMatrix;
44    Geometry& installedGeo = fGeoData.push_back(geometry);
45
46    installedGeo.fPositions.append(vertexCount, positions);
47    if (indices) {
48        installedGeo.fIndices.append(indexCount, indices);
49    }
50
51    if (colors) {
52        fVariableColor = true;
53        installedGeo.fColors.append(vertexCount, colors);
54    } else {
55        fVariableColor = false;
56    }
57
58    if (localCoords) {
59        installedGeo.fLocalCoords.append(vertexCount, localCoords);
60    }
61    fVertexCount = vertexCount;
62    fIndexCount = indexCount;
63    fPrimitiveType = primitiveType;
64
65    this->setBounds(bounds);
66}
67
68void GrDrawVerticesBatch::computePipelineOptimizations(GrInitInvariantOutput* color,
69                                                       GrInitInvariantOutput* coverage,
70                                                       GrBatchToXPOverrides* overrides) const {
71    // When this is called on a batch, there is only one geometry bundle
72    if (fVariableColor) {
73        color->setUnknownFourComponents();
74    } else {
75        color->setKnownFourComponents(fGeoData[0].fColor);
76    }
77    coverage->setKnownSingleComponent(0xff);
78}
79
80void GrDrawVerticesBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
81    SkASSERT(fGeoData.count() == 1);
82    GrColor overrideColor;
83    if (overrides.getOverrideColorIfSet(&overrideColor)) {
84        fGeoData[0].fColor = overrideColor;
85        fGeoData[0].fColors.reset();
86        fVariableColor = false;
87    }
88    fCoverageIgnored = !overrides.readsCoverage();
89    if (!overrides.readsLocalCoords()) {
90        fGeoData[0].fLocalCoords.reset();
91    }
92}
93
94void GrDrawVerticesBatch::onPrepareDraws(Target* target) const {
95    bool hasLocalCoords = !fGeoData[0].fLocalCoords.isEmpty();
96    int colorOffset = -1, texOffset = -1;
97    SkAutoTUnref<const GrGeometryProcessor> gp(
98        set_vertex_attributes(hasLocalCoords, &colorOffset, &texOffset, fViewMatrix,
99                              fCoverageIgnored));
100    target->initDraw(gp, this->pipeline());
101
102    size_t vertexStride = gp->getVertexStride();
103
104    SkASSERT(vertexStride == sizeof(SkPoint) + (hasLocalCoords ? sizeof(SkPoint) : 0)
105                                             + sizeof(GrColor));
106
107    int instanceCount = fGeoData.count();
108
109    const GrVertexBuffer* vertexBuffer;
110    int firstVertex;
111
112    void* verts = target->makeVertexSpace(vertexStride, fVertexCount, &vertexBuffer, &firstVertex);
113
114    if (!verts) {
115        SkDebugf("Could not allocate vertices\n");
116        return;
117    }
118
119    const GrIndexBuffer* indexBuffer = nullptr;
120    int firstIndex = 0;
121
122    uint16_t* indices = nullptr;
123    if (!fGeoData[0].fIndices.isEmpty()) {
124        indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
125
126        if (!indices) {
127            SkDebugf("Could not allocate indices\n");
128            return;
129        }
130    }
131
132    int indexOffset = 0;
133    int vertexOffset = 0;
134    for (int i = 0; i < instanceCount; i++) {
135        const Geometry& args = fGeoData[i];
136
137        // TODO we can actually cache this interleaved and then just memcopy
138        if (indices) {
139            for (int j = 0; j < args.fIndices.count(); ++j, ++indexOffset) {
140                *(indices + indexOffset) = args.fIndices[j] + vertexOffset;
141            }
142        }
143
144        for (int j = 0; j < args.fPositions.count(); ++j) {
145            *((SkPoint*)verts) = args.fPositions[j];
146            if (args.fColors.isEmpty()) {
147                *(GrColor*)((intptr_t)verts + colorOffset) = args.fColor;
148            } else {
149                *(GrColor*)((intptr_t)verts + colorOffset) = args.fColors[j];
150            }
151            if (hasLocalCoords) {
152                *(SkPoint*)((intptr_t)verts + texOffset) = args.fLocalCoords[j];
153            }
154            verts = (void*)((intptr_t)verts + vertexStride);
155            vertexOffset++;
156        }
157    }
158
159    GrVertices vertices;
160    if (indices) {
161        vertices.initIndexed(this->primitiveType(), vertexBuffer, indexBuffer, firstVertex,
162                             firstIndex, fVertexCount, fIndexCount);
163
164    } else {
165        vertices.init(this->primitiveType(), vertexBuffer, firstVertex, fVertexCount);
166    }
167    target->draw(vertices);
168}
169
170bool GrDrawVerticesBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) {
171    GrDrawVerticesBatch* that = t->cast<GrDrawVerticesBatch>();
172
173    if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
174                                that->bounds(), caps)) {
175        return false;
176    }
177
178    if (!this->batchablePrimitiveType() || this->primitiveType() != that->primitiveType()) {
179        return false;
180    }
181
182    // We currently use a uniform viewmatrix for this batch
183    if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
184        return false;
185    }
186
187    if (fGeoData[0].fIndices.isEmpty() != that->fGeoData[0].fIndices.isEmpty()) {
188        return false;
189    }
190
191    if (fGeoData[0].fLocalCoords.isEmpty() != that->fGeoData[0].fLocalCoords.isEmpty()) {
192        return false;
193    }
194
195    if (!fVariableColor) {
196        if (that->fVariableColor || that->fGeoData[0].fColor != fGeoData[0].fColor) {
197            fVariableColor = true;
198        }
199    }
200
201    fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
202    fVertexCount += that->fVertexCount;
203    fIndexCount += that->fIndexCount;
204
205    this->joinBounds(that->bounds());
206    return true;
207}
208
209///////////////////////////////////////////////////////////////////////////////////////////////////
210
211#ifdef GR_TEST_UTILS
212
213#include "GrBatchTest.h"
214
215static uint32_t seed_vertices(GrPrimitiveType type) {
216    switch (type) {
217        case kTriangles_GrPrimitiveType:
218        case kTriangleStrip_GrPrimitiveType:
219        case kTriangleFan_GrPrimitiveType:
220            return 3;
221        case kPoints_GrPrimitiveType:
222            return 1;
223        case kLines_GrPrimitiveType:
224        case kLineStrip_GrPrimitiveType:
225            return 2;
226    }
227    SkFAIL("Incomplete switch\n");
228    return 0;
229}
230
231static uint32_t primitive_vertices(GrPrimitiveType type) {
232    switch (type) {
233        case kTriangles_GrPrimitiveType:
234            return 3;
235        case kLines_GrPrimitiveType:
236            return 2;
237        case kTriangleStrip_GrPrimitiveType:
238        case kTriangleFan_GrPrimitiveType:
239        case kPoints_GrPrimitiveType:
240        case kLineStrip_GrPrimitiveType:
241            return 1;
242    }
243    SkFAIL("Incomplete switch\n");
244    return 0;
245}
246
247static SkPoint random_point(SkRandom* random, SkScalar min, SkScalar max) {
248    SkPoint p;
249    p.fX = random->nextRangeScalar(min, max);
250    p.fY = random->nextRangeScalar(min, max);
251    return p;
252}
253
254static void randomize_params(size_t count, size_t maxVertex, SkScalar min, SkScalar max,
255                             SkRandom* random,
256                             SkTArray<SkPoint>* positions,
257                             SkTArray<SkPoint>* texCoords, bool hasTexCoords,
258                             SkTArray<GrColor>* colors, bool hasColors,
259                             SkTArray<uint16_t>* indices, bool hasIndices) {
260    for (uint32_t v = 0; v < count; v++) {
261        positions->push_back(random_point(random, min, max));
262        if (hasTexCoords) {
263            texCoords->push_back(random_point(random, min, max));
264        }
265        if (hasColors) {
266            colors->push_back(GrRandomColor(random));
267        }
268        if (hasIndices) {
269            SkASSERT(maxVertex <= SK_MaxU16);
270            indices->push_back(random->nextULessThan((uint16_t)maxVertex));
271        }
272    }
273}
274
275DRAW_BATCH_TEST_DEFINE(VerticesBatch) {
276    GrPrimitiveType type = GrPrimitiveType(random->nextULessThan(kLast_GrPrimitiveType + 1));
277    uint32_t primitiveCount = random->nextRangeU(1, 100);
278
279    // TODO make 'sensible' indexbuffers
280    SkTArray<SkPoint> positions;
281    SkTArray<SkPoint> texCoords;
282    SkTArray<GrColor> colors;
283    SkTArray<uint16_t> indices;
284
285    bool hasTexCoords = random->nextBool();
286    bool hasIndices = random->nextBool();
287    bool hasColors = random->nextBool();
288
289    uint32_t vertexCount = seed_vertices(type) + (primitiveCount - 1) * primitive_vertices(type);
290
291    static const SkScalar kMinVertExtent = -100.f;
292    static const SkScalar kMaxVertExtent = 100.f;
293    randomize_params(seed_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
294                     random,
295                     &positions,
296                     &texCoords, hasTexCoords,
297                     &colors, hasColors,
298                     &indices, hasIndices);
299
300    for (uint32_t i = 1; i < primitiveCount; i++) {
301        randomize_params(primitive_vertices(type), vertexCount, kMinVertExtent, kMaxVertExtent,
302                         random,
303                         &positions,
304                         &texCoords, hasTexCoords,
305                         &colors, hasColors,
306                         &indices, hasIndices);
307    }
308
309    SkMatrix viewMatrix = GrTest::TestMatrix(random);
310    SkRect bounds;
311    SkDEBUGCODE(bool result = ) bounds.setBoundsCheck(positions.begin(), vertexCount);
312    SkASSERT(result);
313
314    viewMatrix.mapRect(&bounds);
315
316    GrDrawVerticesBatch::Geometry geometry;
317    geometry.fColor = GrRandomColor(random);
318    return GrDrawVerticesBatch::Create(geometry, type, viewMatrix,
319                                       positions.begin(), vertexCount,
320                                       indices.begin(), hasIndices ? vertexCount : 0,
321                                       colors.begin(),
322                                       texCoords.begin(),
323                                       bounds);
324}
325
326#endif
327