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 "GrNonAAFillRectBatch.h"
9
10#include "GrBatchFlushState.h"
11#include "GrColor.h"
12#include "GrDefaultGeoProcFactory.h"
13#include "GrPrimitiveProcessor.h"
14#include "GrResourceProvider.h"
15#include "GrTInstanceBatch.h"
16#include "GrQuad.h"
17#include "GrVertexBatch.h"
18
19// Common functions
20class NonAAFillRectBatchBase {
21public:
22    static const int kVertsPerInstance = 4;
23    static const int kIndicesPerInstance = 6;
24
25    static void InitInvariantOutputCoverage(GrInitInvariantOutput* out) {
26        out->setKnownSingleComponent(0xff);
27    }
28
29    static const GrIndexBuffer* GetIndexBuffer(GrResourceProvider* rp) {
30        return rp->refQuadIndexBuffer();
31    }
32
33    template <typename Geometry>
34    static void SetBounds(const Geometry& geo, SkRect* outBounds) {
35        geo.fViewMatrix.mapRect(outBounds, geo.fRect);
36    }
37
38    template <typename Geometry>
39    static void UpdateBoundsAfterAppend(const Geometry& geo, SkRect* outBounds) {
40        SkRect bounds = geo.fRect;
41        geo.fViewMatrix.mapRect(&bounds);
42        outBounds->join(bounds);
43    }
44};
45
46/** We always use per-vertex colors so that rects can be batched across color changes. Sometimes
47    we  have explicit local coords and sometimes not. We *could* always provide explicit local
48    coords and just duplicate the positions when the caller hasn't provided a local coord rect,
49    but we haven't seen a use case which frequently switches between local rect and no local
50    rect draws.
51
52    The vertex attrib order is always pos, color, [local coords].
53 */
54static const GrGeometryProcessor* create_gp(const SkMatrix& viewMatrix,
55                                            bool readsCoverage,
56                                            bool hasExplicitLocalCoords,
57                                            const SkMatrix* localMatrix) {
58    using namespace GrDefaultGeoProcFactory;
59    Color color(Color::kAttribute_Type);
60    Coverage coverage(readsCoverage ? Coverage::kSolid_Type : Coverage::kNone_Type);
61
62    // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
63    // the local rect on the cpu (in case the localMatrix also has perspective).
64    // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
65    // to generate vertex local coords
66    if (viewMatrix.hasPerspective()) {
67        LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type :
68                                                         LocalCoords::kUsePosition_Type,
69                                localMatrix);
70        return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, viewMatrix);
71    } else if (hasExplicitLocalCoords) {
72        LocalCoords localCoords(LocalCoords::kHasExplicit_Type);
73        return GrDefaultGeoProcFactory::Create(color, coverage, localCoords, SkMatrix::I());
74    } else {
75        LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
76        return GrDefaultGeoProcFactory::CreateForDeviceSpace(color, coverage, localCoords,
77                                                             viewMatrix);
78    }
79}
80
81static void tesselate(intptr_t vertices,
82                      size_t vertexStride,
83                      GrColor color,
84                      const SkMatrix& viewMatrix,
85                      const SkRect& rect,
86                      const GrQuad* localQuad) {
87    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
88
89    positions->setRectFan(rect.fLeft, rect.fTop,
90                          rect.fRight, rect.fBottom, vertexStride);
91
92    if (!viewMatrix.hasPerspective()) {
93        viewMatrix.mapPointsWithStride(positions, vertexStride,
94                                       NonAAFillRectBatchBase::kVertsPerInstance);
95    }
96
97    // Setup local coords
98    // TODO we should only do this if local coords are being read
99    if (localQuad) {
100        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
101        for (int i = 0; i < NonAAFillRectBatchBase::kVertsPerInstance; i++) {
102            SkPoint* coords = reinterpret_cast<SkPoint*>(vertices + kLocalOffset +
103                              i * vertexStride);
104            *coords = localQuad->point(i);
105        }
106    }
107
108    static const int kColorOffset = sizeof(SkPoint);
109    GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
110    for (int j = 0; j < 4; ++j) {
111        *vertColor = color;
112        vertColor = (GrColor*) ((intptr_t) vertColor + vertexStride);
113    }
114}
115
116class NonAAFillRectBatchImp : public NonAAFillRectBatchBase {
117public:
118    struct Geometry {
119        SkMatrix fViewMatrix;
120        SkRect fRect;
121        GrQuad fLocalQuad;
122        GrColor fColor;
123    };
124
125    static const char* Name() { return "NonAAFillRectBatch"; }
126
127    static SkString DumpInfo(const Geometry& geo, int index) {
128        SkString str;
129        str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
130                    index,
131                    geo.fColor,
132                    geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, geo.fRect.fBottom);
133        return str;
134    }
135
136    static bool CanCombine(const Geometry& mine, const Geometry& theirs,
137                           const GrXPOverridesForBatch& overrides) {
138        return true;
139    }
140
141    static const GrGeometryProcessor* CreateGP(const Geometry& geo,
142                                               const GrXPOverridesForBatch& overrides) {
143        const GrGeometryProcessor* gp = create_gp(geo.fViewMatrix, overrides.readsCoverage(), true,
144                                                  nullptr);
145
146        SkASSERT(gp->getVertexStride() ==
147                sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
148        return gp;
149    }
150
151    static void Tesselate(intptr_t vertices, size_t vertexStride, const Geometry& geo,
152                          const GrXPOverridesForBatch& overrides) {
153        tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, &geo.fLocalQuad);
154    }
155};
156
157// We handle perspective in the local matrix or viewmatrix with special batches
158class NonAAFillRectBatchPerspectiveImp : public NonAAFillRectBatchBase {
159public:
160    struct Geometry {
161        SkMatrix fViewMatrix;
162        SkMatrix fLocalMatrix;
163        SkRect fRect;
164        SkRect fLocalRect;
165        GrColor fColor;
166        bool fHasLocalMatrix;
167        bool fHasLocalRect;
168    };
169
170    static const char* Name() { return "NonAAFillRectBatchPerspective"; }
171
172    static SkString DumpInfo(const Geometry& geo, int index) {
173        SkString str;
174        str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n",
175                    index,
176                    geo.fColor,
177                    geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight, geo.fRect.fBottom);
178        return str;
179    }
180
181    static bool CanCombine(const Geometry& mine, const Geometry& theirs,
182                           const GrXPOverridesForBatch& overrides) {
183        // We could batch across perspective vm changes if we really wanted to
184        return mine.fViewMatrix.cheapEqualTo(theirs.fViewMatrix) &&
185               mine.fHasLocalRect == theirs.fHasLocalRect &&
186               (!mine.fHasLocalMatrix || mine.fLocalMatrix.cheapEqualTo(theirs.fLocalMatrix));
187    }
188
189    static const GrGeometryProcessor* CreateGP(const Geometry& geo,
190                                               const GrXPOverridesForBatch& overrides) {
191        const GrGeometryProcessor* gp = create_gp(geo.fViewMatrix, overrides.readsCoverage(),
192                                                  geo.fHasLocalRect,
193                                                  geo.fHasLocalMatrix ? &geo.fLocalMatrix :
194                                                                        nullptr);
195
196        SkASSERT(geo.fHasLocalRect ?
197             gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr) :
198             gp->getVertexStride() == sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
199        return gp;
200    }
201
202    static void Tesselate(intptr_t vertices, size_t vertexStride, const Geometry& geo,
203                          const GrXPOverridesForBatch& overrides) {
204        if (geo.fHasLocalRect) {
205            GrQuad quad(geo.fLocalRect);
206            tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, &quad);
207        } else {
208            tesselate(vertices, vertexStride, geo.fColor, geo.fViewMatrix, geo.fRect, nullptr);
209        }
210    }
211};
212
213typedef GrTInstanceBatch<NonAAFillRectBatchImp> NonAAFillRectBatchSimple;
214typedef GrTInstanceBatch<NonAAFillRectBatchPerspectiveImp> NonAAFillRectBatchPerspective;
215
216inline static void append_to_batch(NonAAFillRectBatchSimple* batch, GrColor color,
217                                   const SkMatrix& viewMatrix, const SkRect& rect,
218                                   const SkRect* localRect, const SkMatrix* localMatrix) {
219    SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
220    NonAAFillRectBatchSimple::Geometry& geo = batch->geoData()->push_back();
221
222    geo.fColor = color;
223    geo.fViewMatrix = viewMatrix;
224    geo.fRect = rect;
225
226    if (localRect && localMatrix) {
227        geo.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
228    } else if (localRect) {
229        geo.fLocalQuad.set(*localRect);
230    } else if (localMatrix) {
231        geo.fLocalQuad.setFromMappedRect(rect, *localMatrix);
232    } else {
233        geo.fLocalQuad.set(rect);
234    }
235}
236
237inline static void append_to_batch(NonAAFillRectBatchPerspective* batch, GrColor color,
238                                   const SkMatrix& viewMatrix, const SkRect& rect,
239                                   const SkRect* localRect, const SkMatrix* localMatrix) {
240    SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
241    NonAAFillRectBatchPerspective::Geometry& geo = batch->geoData()->push_back();
242
243    geo.fColor = color;
244    geo.fViewMatrix = viewMatrix;
245    geo.fRect = rect;
246    geo.fHasLocalRect = SkToBool(localRect);
247    geo.fHasLocalMatrix = SkToBool(localMatrix);
248    if (localMatrix) {
249        geo.fLocalMatrix = *localMatrix;
250    }
251    if (localRect) {
252        geo.fLocalRect = *localRect;
253    }
254
255}
256
257namespace GrNonAAFillRectBatch {
258
259GrDrawBatch* Create(GrColor color,
260                    const SkMatrix& viewMatrix,
261                    const SkRect& rect,
262                    const SkRect* localRect,
263                    const SkMatrix* localMatrix) {
264    NonAAFillRectBatchSimple* batch = NonAAFillRectBatchSimple::Create();
265    append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix);
266    batch->init();
267    return batch;
268}
269
270GrDrawBatch* CreateWithPerspective(GrColor color,
271                                   const SkMatrix& viewMatrix,
272                                   const SkRect& rect,
273                                   const SkRect* localRect,
274                                   const SkMatrix* localMatrix) {
275    NonAAFillRectBatchPerspective* batch = NonAAFillRectBatchPerspective::Create();
276    append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix);
277    batch->init();
278    return batch;
279}
280
281bool Append(GrBatch* origBatch,
282            GrColor color,
283            const SkMatrix& viewMatrix,
284            const SkRect& rect,
285            const SkRect* localRect,
286            const SkMatrix* localMatrix) {
287    bool usePerspective = viewMatrix.hasPerspective() ||
288                          (localMatrix && localMatrix->hasPerspective());
289
290    if (usePerspective && origBatch->classID() != NonAAFillRectBatchPerspective::ClassID()) {
291        return false;
292    }
293
294    if (!usePerspective) {
295        NonAAFillRectBatchSimple* batch = origBatch->cast<NonAAFillRectBatchSimple>();
296        append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix);
297        batch->updateBoundsAfterAppend();
298    } else {
299        NonAAFillRectBatchPerspective* batch = origBatch->cast<NonAAFillRectBatchPerspective>();
300        const NonAAFillRectBatchPerspective::Geometry& geo = batch->geoData()->back();
301
302        if (!geo.fViewMatrix.cheapEqualTo(viewMatrix) ||
303            geo.fHasLocalRect != SkToBool(localRect) ||
304            geo.fHasLocalMatrix != SkToBool(localMatrix) ||
305            (geo.fHasLocalMatrix && !geo.fLocalMatrix.cheapEqualTo(*localMatrix))) {
306            return false;
307        }
308
309        append_to_batch(batch, color, viewMatrix, rect, localRect, localMatrix);
310        batch->updateBoundsAfterAppend();
311    }
312
313    return true;
314}
315
316};
317
318///////////////////////////////////////////////////////////////////////////////////////////////////
319
320#ifdef GR_TEST_UTILS
321
322#include "GrBatchTest.h"
323
324DRAW_BATCH_TEST_DEFINE(RectBatch) {
325    GrColor color = GrRandomColor(random);
326    SkRect rect = GrTest::TestRect(random);
327    SkRect localRect = GrTest::TestRect(random);
328    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
329    SkMatrix localMatrix = GrTest::TestMatrix(random);
330
331    bool hasLocalRect = random->nextBool();
332    bool hasLocalMatrix = random->nextBool();
333    return GrNonAAFillRectBatch::Create(color, viewMatrix, rect,
334                                        hasLocalRect ? &localRect : nullptr,
335                                        hasLocalMatrix ? &localMatrix : nullptr);
336}
337
338#endif
339