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 
set_vertex_attributes(bool hasLocalCoords,int * colorOffset,int * texOffset,const SkMatrix & viewMatrix,bool coverageIgnored)14 static 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 
GrDrawVerticesBatch(const Geometry & geometry,GrPrimitiveType primitiveType,const SkMatrix & viewMatrix,const SkPoint * positions,int vertexCount,const uint16_t * indices,int indexCount,const GrColor * colors,const SkPoint * localCoords,const SkRect & bounds)34 GrDrawVerticesBatch::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 
computePipelineOptimizations(GrInitInvariantOutput * color,GrInitInvariantOutput * coverage,GrBatchToXPOverrides * overrides) const68 void 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 
initBatchTracker(const GrXPOverridesForBatch & overrides)80 void 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 
onPrepareDraws(Target * target) const94 void 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 
onCombineIfPossible(GrBatch * t,const GrCaps & caps)170 bool 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 
seed_vertices(GrPrimitiveType type)215 static 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 
primitive_vertices(GrPrimitiveType type)231 static 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 
random_point(SkRandom * random,SkScalar min,SkScalar max)247 static 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 
randomize_params(size_t count,size_t maxVertex,SkScalar min,SkScalar max,SkRandom * random,SkTArray<SkPoint> * positions,SkTArray<SkPoint> * texCoords,bool hasTexCoords,SkTArray<GrColor> * colors,bool hasColors,SkTArray<uint16_t> * indices,bool hasIndices)254 static 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 
DRAW_BATCH_TEST_DEFINE(VerticesBatch)275 DRAW_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