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