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