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 "GrRectBatch.h"
9 
10 #include "GrBatch.h"
11 #include "GrBatchTarget.h"
12 #include "GrBatchTest.h"
13 #include "GrDefaultGeoProcFactory.h"
14 #include "GrPrimitiveProcessor.h"
15 #include "GrTemplates.h"
16 
17 /** We always use per-vertex colors so that rects can be batched across color changes. Sometimes we
18     have explicit local coords and sometimes not. We *could* always provide explicit local coords
19     and just duplicate the positions when the caller hasn't provided a local coord rect, but we
20     haven't seen a use case which frequently switches between local rect and no local rect draws.
21 
22     The color param is used to determine whether the opaque hint can be set on the draw state.
23     The caller must populate the vertex colors itself.
24 
25     The vertex attrib order is always pos, color, [local coords].
26  */
create_rect_gp(bool hasExplicitLocalCoords,GrColor color,const SkMatrix * localMatrix)27 static const GrGeometryProcessor* create_rect_gp(bool hasExplicitLocalCoords,
28                                                  GrColor color,
29                                                  const SkMatrix* localMatrix) {
30     uint32_t flags = GrDefaultGeoProcFactory::kPosition_GPType |
31                      GrDefaultGeoProcFactory::kColor_GPType;
32     flags |= hasExplicitLocalCoords ? GrDefaultGeoProcFactory::kLocalCoord_GPType : 0;
33     if (localMatrix) {
34         return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), *localMatrix);
35     } else {
36         return GrDefaultGeoProcFactory::Create(flags, color, SkMatrix::I(), SkMatrix::I());
37     }
38 }
39 
40 class RectBatch : public GrBatch {
41 public:
42     struct Geometry {
43         SkMatrix fViewMatrix;
44         SkRect fRect;
45         SkRect fLocalRect;
46         SkMatrix fLocalMatrix;
47         GrColor fColor;
48         bool fHasLocalRect;
49         bool fHasLocalMatrix;
50     };
51 
Create(const Geometry & geometry)52     static GrBatch* Create(const Geometry& geometry) {
53         return SkNEW_ARGS(RectBatch, (geometry));
54     }
55 
name() const56     const char* name() const override { return "RectBatch"; }
57 
getInvariantOutputColor(GrInitInvariantOutput * out) const58     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
59         // When this is called on a batch, there is only one geometry bundle
60         out->setKnownFourComponents(fGeoData[0].fColor);
61     }
62 
getInvariantOutputCoverage(GrInitInvariantOutput * out) const63     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
64         out->setKnownSingleComponent(0xff);
65     }
66 
initBatchTracker(const GrPipelineInfo & init)67     void initBatchTracker(const GrPipelineInfo& init) override {
68         // Handle any color overrides
69         if (init.fColorIgnored) {
70             fGeoData[0].fColor = GrColor_ILLEGAL;
71         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
72             fGeoData[0].fColor = init.fOverrideColor;
73         }
74 
75         // setup batch properties
76         fBatch.fColorIgnored = init.fColorIgnored;
77         fBatch.fColor = fGeoData[0].fColor;
78         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
79         fBatch.fCoverageIgnored = init.fCoverageIgnored;
80     }
81 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)82     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override {
83         // Go to device coords to allow batching across matrix changes
84         SkMatrix invert = SkMatrix::I();
85 
86         // if we have a local rect, then we apply the localMatrix directly to the localRect to
87         // generate vertex local coords
88         bool hasExplicitLocalCoords = this->hasLocalRect();
89         if (!hasExplicitLocalCoords) {
90             if (!this->viewMatrix().isIdentity() && !this->viewMatrix().invert(&invert)) {
91                 SkDebugf("Could not invert\n");
92                 return;
93             }
94 
95             if (this->hasLocalMatrix()) {
96                 invert.preConcat(this->localMatrix());
97             }
98         }
99 
100         SkAutoTUnref<const GrGeometryProcessor> gp(create_rect_gp(hasExplicitLocalCoords,
101                                                                   this->color(),
102                                                                   &invert));
103 
104         batchTarget->initDraw(gp, pipeline);
105 
106         // TODO this is hacky, but the only way we have to initialize the GP is to use the
107         // GrPipelineInfo struct so we can generate the correct shader.  Once we have GrBatch
108         // everywhere we can remove this nastiness
109         GrPipelineInfo init;
110         init.fColorIgnored = fBatch.fColorIgnored;
111         init.fOverrideColor = GrColor_ILLEGAL;
112         init.fCoverageIgnored = fBatch.fCoverageIgnored;
113         init.fUsesLocalCoords = this->usesLocalCoords();
114         gp->initBatchTracker(batchTarget->currentBatchTracker(), init);
115 
116         int instanceCount = fGeoData.count();
117         size_t vertexStride = gp->getVertexStride();
118         SkASSERT(hasExplicitLocalCoords ?
119                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) :
120                  vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
121         QuadHelper helper;
122         void* vertices = helper.init(batchTarget, vertexStride, instanceCount);
123 
124         if (!vertices) {
125             return;
126         }
127 
128 
129         for (int i = 0; i < instanceCount; i++) {
130             const Geometry& geom = fGeoData[i];
131 
132             intptr_t offset = GrTCast<intptr_t>(vertices) + kVerticesPerQuad * i * vertexStride;
133             SkPoint* positions = GrTCast<SkPoint*>(offset);
134 
135             positions->setRectFan(geom.fRect.fLeft, geom.fRect.fTop,
136                                   geom.fRect.fRight, geom.fRect.fBottom, vertexStride);
137             geom.fViewMatrix.mapPointsWithStride(positions, vertexStride, kVerticesPerQuad);
138 
139             if (geom.fHasLocalRect) {
140                 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
141                 SkPoint* coords = GrTCast<SkPoint*>(offset + kLocalOffset);
142                 coords->setRectFan(geom.fLocalRect.fLeft, geom.fLocalRect.fTop,
143                                    geom.fLocalRect.fRight, geom.fLocalRect.fBottom,
144                                    vertexStride);
145                 if (geom.fHasLocalMatrix) {
146                     geom.fLocalMatrix.mapPointsWithStride(coords, vertexStride, kVerticesPerQuad);
147                 }
148             }
149 
150             static const int kColorOffset = sizeof(SkPoint);
151             GrColor* vertColor = GrTCast<GrColor*>(offset + kColorOffset);
152             for (int j = 0; j < 4; ++j) {
153                 *vertColor = geom.fColor;
154                 vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
155             }
156         }
157 
158         helper.issueDraw(batchTarget);
159     }
160 
geoData()161     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
162 
163 private:
RectBatch(const Geometry & geometry)164     RectBatch(const Geometry& geometry) {
165         this->initClassID<RectBatch>();
166         fGeoData.push_back(geometry);
167 
168         fBounds = geometry.fRect;
169         geometry.fViewMatrix.mapRect(&fBounds);
170     }
171 
color() const172     GrColor color() const { return fBatch.fColor; }
usesLocalCoords() const173     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
colorIgnored() const174     bool colorIgnored() const { return fBatch.fColorIgnored; }
viewMatrix() const175     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
localMatrix() const176     const SkMatrix& localMatrix() const { return fGeoData[0].fLocalMatrix; }
hasLocalRect() const177     bool hasLocalRect() const { return fGeoData[0].fHasLocalRect; }
hasLocalMatrix() const178     bool hasLocalMatrix() const { return fGeoData[0].fHasLocalMatrix; }
179 
onCombineIfPossible(GrBatch * t)180     bool onCombineIfPossible(GrBatch* t) override {
181         RectBatch* that = t->cast<RectBatch>();
182 
183         if (this->hasLocalRect() != that->hasLocalRect()) {
184             return false;
185         }
186 
187         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
188         if (!this->hasLocalRect() && this->usesLocalCoords()) {
189             if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
190                 return false;
191             }
192 
193             if (this->hasLocalMatrix() != that->hasLocalMatrix()) {
194                 return false;
195             }
196 
197             if (this->hasLocalMatrix() && !this->localMatrix().cheapEqualTo(that->localMatrix())) {
198                 return false;
199             }
200         }
201 
202         if (this->color() != that->color()) {
203             fBatch.fColor = GrColor_ILLEGAL;
204         }
205         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
206         this->joinBounds(that->bounds());
207         return true;
208     }
209 
210     struct BatchTracker {
211         GrColor fColor;
212         bool fUsesLocalCoords;
213         bool fColorIgnored;
214         bool fCoverageIgnored;
215     };
216 
217     BatchTracker fBatch;
218     SkSTArray<1, Geometry, true> fGeoData;
219 };
220 
221 namespace GrRectBatch {
222 
Create(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix)223 GrBatch* Create(GrColor color,
224                 const SkMatrix& viewMatrix,
225                 const SkRect& rect,
226                 const SkRect* localRect,
227                 const SkMatrix* localMatrix) {
228     RectBatch::Geometry geometry;
229     geometry.fColor = color;
230     geometry.fViewMatrix = viewMatrix;
231     geometry.fRect = rect;
232 
233     if (localRect) {
234         geometry.fHasLocalRect = true;
235         geometry.fLocalRect = *localRect;
236     } else {
237         geometry.fHasLocalRect = false;
238     }
239 
240     if (localMatrix) {
241         geometry.fHasLocalMatrix = true;
242         geometry.fLocalMatrix = *localMatrix;
243     } else {
244         geometry.fHasLocalMatrix = false;
245     }
246 
247     return RectBatch::Create(geometry);
248 }
249 
250 };
251 
252 ///////////////////////////////////////////////////////////////////////////////////////////////////
253 
254 #ifdef GR_TEST_UTILS
255 
BATCH_TEST_DEFINE(RectBatch)256 BATCH_TEST_DEFINE(RectBatch) {
257     GrColor color = GrRandomColor(random);
258 
259     SkRect rect = GrTest::TestRect(random);
260     SkRect localRect;
261     bool hasLocalRect = random->nextBool();
262     bool hasLocalMatrix = random->nextBool();
263 
264     SkMatrix viewMatrix;
265     SkMatrix localMatrix;
266     if (hasLocalRect) {
267         viewMatrix = GrTest::TestMatrixInvertible(random);
268         localRect = GrTest::TestRect(random);
269     } else {
270         viewMatrix = GrTest::TestMatrix(random);
271     }
272 
273     if (hasLocalMatrix) {
274         localMatrix = GrTest::TestMatrix(random);
275     }
276 
277     return GrRectBatch::Create(color, viewMatrix, rect,
278                                hasLocalRect ? &localRect : NULL,
279                                hasLocalMatrix ? &localMatrix : NULL);
280 }
281 
282 #endif
283