GrNonAAFillRectOp.cpp revision 9c80b5ff592caf1c18b43e98c85bc8340b3ac531
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 "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
270