1/*
2 * Copyright 2017 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 "GrAppliedClip.h"
9#include "GrColor.h"
10#include "GrDefaultGeoProcFactory.h"
11#include "GrDrawOpTest.h"
12#include "GrMeshDrawOp.h"
13#include "GrOpFlushState.h"
14#include "GrPrimitiveProcessor.h"
15#include "GrQuad.h"
16#include "GrRectOpFactory.h"
17#include "GrResourceProvider.h"
18#include "GrSimpleMeshDrawOpHelper.h"
19#include "SkMatrixPriv.h"
20
21static const int kVertsPerRect = 4;
22static const int kIndicesPerRect = 6;
23
24/** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
25    we  have explicit local coords and sometimes not. We *could* always provide explicit local
26    coords and just duplicate the positions when the caller hasn't provided a local coord rect,
27    but we haven't seen a use case which frequently switches between local rect and no local
28    rect draws.
29
30    The vertex attrib order is always pos, color, [local coords].
31 */
32static sk_sp<GrGeometryProcessor> make_gp() {
33    using namespace GrDefaultGeoProcFactory;
34    return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type, Coverage::kSolid_Type,
35                                         LocalCoords::kHasExplicit_Type, SkMatrix::I());
36}
37
38static sk_sp<GrGeometryProcessor> make_perspective_gp(const SkMatrix& viewMatrix,
39                                                      bool hasExplicitLocalCoords,
40                                                      const SkMatrix* localMatrix) {
41    SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
42
43    using namespace GrDefaultGeoProcFactory;
44
45    // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
46    // the local rect on the cpu (in case the localMatrix also has perspective).
47    // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
48    // to generate vertex local coords
49    if (viewMatrix.hasPerspective()) {
50        LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
51                                                       : LocalCoords::kUsePosition_Type,
52                                localMatrix);
53        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
54                                             Coverage::kSolid_Type, localCoords, viewMatrix);
55    } else if (hasExplicitLocalCoords) {
56        LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
57        return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
58                                             Coverage::kSolid_Type, localCoords, SkMatrix::I());
59    } else {
60        LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
61        return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
62                                                           Coverage::kSolid_Type, localCoords,
63                                                           viewMatrix);
64    }
65}
66
67static void tesselate(intptr_t vertices,
68                      size_t vertexStride,
69                      GrColor color,
70                      const SkMatrix* viewMatrix,
71                      const SkRect& rect,
72                      const GrQuad* localQuad) {
73    SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
74
75    SkPointPriv::SetRectTriStrip(positions, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
76            vertexStride);
77
78    if (viewMatrix) {
79        SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerRect);
80    }
81
82    // Setup local coords
83    // TODO we should only do this if local coords are being read
84    if (localQuad) {
85        static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
86        for (int i = 0; i < kVertsPerRect; i++) {
87            SkPoint* coords =
88                    reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
89            *coords = localQuad->point(i);
90        }
91    }
92
93    static const int kColorOffset = sizeof(SkPoint);
94    GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
95    for (int j = 0; j < 4; ++j) {
96        *vertColor = color;
97        vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
98    }
99}
100
101namespace {
102
103class NonAAFillRectOp final : public GrMeshDrawOp {
104private:
105    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
106
107public:
108    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
109                                          const SkRect& rect, const SkRect* localRect,
110                                          const SkMatrix* localMatrix, GrAAType aaType,
111                                          const GrUserStencilSettings* stencilSettings) {
112        SkASSERT(GrAAType::kCoverage != aaType);
113        return Helper::FactoryHelper<NonAAFillRectOp>(std::move(paint), viewMatrix, rect, localRect,
114                                                      localMatrix, aaType, stencilSettings);
115    }
116
117    NonAAFillRectOp() = delete;
118
119    NonAAFillRectOp(const Helper::MakeArgs& args, GrColor color, const SkMatrix& viewMatrix,
120                    const SkRect& rect, const SkRect* localRect, const SkMatrix* localMatrix,
121                    GrAAType aaType, const GrUserStencilSettings* stencilSettings)
122            : INHERITED(ClassID()), fHelper(args, aaType, stencilSettings) {
123
124        SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
125        RectInfo& info = fRects.push_back();
126        info.fColor = color;
127        info.fViewMatrix = viewMatrix;
128        info.fRect = rect;
129        if (localRect && localMatrix) {
130            info.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
131        } else if (localRect) {
132            info.fLocalQuad.set(*localRect);
133        } else if (localMatrix) {
134            info.fLocalQuad.setFromMappedRect(rect, *localMatrix);
135        } else {
136            info.fLocalQuad.set(rect);
137        }
138        this->setTransformedBounds(fRects[0].fRect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
139    }
140
141    const char* name() const override { return "NonAAFillRectOp"; }
142
143    void visitProxies(const VisitProxyFunc& func) const override {
144        fHelper.visitProxies(func);
145    }
146
147    SkString dumpInfo() const override {
148        SkString str;
149        str.append(GrMeshDrawOp::dumpInfo());
150        str.appendf("# combined: %d\n", fRects.count());
151        for (int i = 0; i < fRects.count(); ++i) {
152            const RectInfo& info = fRects[i];
153            str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
154                        info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight,
155                        info.fRect.fBottom);
156        }
157        str += fHelper.dumpInfo();
158        str += INHERITED::dumpInfo();
159        return str;
160    }
161
162    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
163                                GrPixelConfigIsClamped dstIsClamped) override {
164        GrColor* color = &fRects.front().fColor;
165        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
166                                            GrProcessorAnalysisCoverage::kNone, color);
167    }
168
169    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
170
171    DEFINE_OP_CLASS_ID
172
173private:
174    void onPrepareDraws(Target* target) override {
175        sk_sp<GrGeometryProcessor> gp = make_gp();
176        if (!gp) {
177            SkDebugf("Couldn't create GrGeometryProcessor\n");
178            return;
179        }
180        SkASSERT(gp->getVertexStride() ==
181                 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
182
183        size_t vertexStride = gp->getVertexStride();
184        int rectCount = fRects.count();
185
186        sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
187        PatternHelper helper(GrPrimitiveType::kTriangles);
188        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
189                                     kIndicesPerRect, rectCount);
190        if (!vertices || !indexBuffer) {
191            SkDebugf("Could not allocate vertices\n");
192            return;
193        }
194
195        for (int i = 0; i < rectCount; i++) {
196            intptr_t verts =
197                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
198            tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
199                      fRects[i].fRect, &fRects[i].fLocalQuad);
200        }
201        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
202    }
203
204    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
205        NonAAFillRectOp* that = t->cast<NonAAFillRectOp>();
206        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
207            return false;
208        }
209        fRects.push_back_n(that->fRects.count(), that->fRects.begin());
210        this->joinBounds(*that);
211        return true;
212    }
213
214    struct RectInfo {
215        GrColor fColor;
216        SkMatrix fViewMatrix;
217        SkRect fRect;
218        GrQuad fLocalQuad;
219    };
220
221    Helper fHelper;
222    SkSTArray<1, RectInfo, true> fRects;
223    typedef GrMeshDrawOp INHERITED;
224};
225
226// We handle perspective in the local matrix or viewmatrix with special ops.
227class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
228private:
229    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
230
231public:
232    static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
233                                          const SkRect& rect, const SkRect* localRect,
234                                          const SkMatrix* localMatrix, GrAAType aaType,
235                                          const GrUserStencilSettings* stencilSettings) {
236        SkASSERT(GrAAType::kCoverage != aaType);
237        return Helper::FactoryHelper<NonAAFillRectPerspectiveOp>(std::move(paint), viewMatrix, rect,
238                                                                 localRect, localMatrix, aaType,
239                                                                 stencilSettings);
240    }
241
242    NonAAFillRectPerspectiveOp() = delete;
243
244    NonAAFillRectPerspectiveOp(const Helper::MakeArgs& args, GrColor color,
245                               const SkMatrix& viewMatrix, const SkRect& rect,
246                               const SkRect* localRect, const SkMatrix* localMatrix,
247                               GrAAType aaType, const GrUserStencilSettings* stencilSettings)
248            : INHERITED(ClassID())
249            , fHelper(args, aaType, stencilSettings)
250            , fViewMatrix(viewMatrix) {
251        SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
252        RectInfo& info = fRects.push_back();
253        info.fColor = color;
254        info.fRect = rect;
255        fHasLocalRect = SkToBool(localRect);
256        fHasLocalMatrix = SkToBool(localMatrix);
257        if (fHasLocalMatrix) {
258            fLocalMatrix = *localMatrix;
259        }
260        if (fHasLocalRect) {
261            info.fLocalRect = *localRect;
262        }
263        this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
264    }
265
266    const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
267
268    void visitProxies(const VisitProxyFunc& func) const override {
269        fHelper.visitProxies(func);
270    }
271
272    SkString dumpInfo() const override {
273        SkString str;
274        str.appendf("# combined: %d\n", fRects.count());
275        for (int i = 0; i < fRects.count(); ++i) {
276            const RectInfo& geo = fRects[i];
277            str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
278                        geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
279                        geo.fRect.fBottom);
280        }
281        str += fHelper.dumpInfo();
282        str += INHERITED::dumpInfo();
283        return str;
284    }
285
286    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
287                                GrPixelConfigIsClamped dstIsClamped) override {
288        GrColor* color = &fRects.front().fColor;
289        return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped,
290                                            GrProcessorAnalysisCoverage::kNone, color);
291    }
292
293    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
294
295    DEFINE_OP_CLASS_ID
296
297private:
298    void onPrepareDraws(Target* target) override {
299        sk_sp<GrGeometryProcessor> gp = make_perspective_gp(
300                fViewMatrix, fHasLocalRect, fHasLocalMatrix ? &fLocalMatrix : nullptr);
301        if (!gp) {
302            SkDebugf("Couldn't create GrGeometryProcessor\n");
303            return;
304        }
305        SkASSERT(fHasLocalRect
306                         ? gp->getVertexStride() ==
307                                   sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
308                         : gp->getVertexStride() ==
309                                   sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
310
311        size_t vertexStride = gp->getVertexStride();
312        int rectCount = fRects.count();
313
314        sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
315        PatternHelper helper(GrPrimitiveType::kTriangles);
316        void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
317                                     kIndicesPerRect, rectCount);
318        if (!vertices || !indexBuffer) {
319            SkDebugf("Could not allocate vertices\n");
320            return;
321        }
322
323        for (int i = 0; i < rectCount; i++) {
324            const RectInfo& info = fRects[i];
325            intptr_t verts =
326                    reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
327            if (fHasLocalRect) {
328                GrQuad quad(info.fLocalRect);
329                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
330            } else {
331                tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
332            }
333        }
334        helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
335    }
336
337    bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
338        NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
339        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
340            return false;
341        }
342
343        // We could combine across perspective vm changes if we really wanted to.
344        if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
345            return false;
346        }
347        if (fHasLocalRect != that->fHasLocalRect) {
348            return false;
349        }
350        if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
351            return false;
352        }
353
354        fRects.push_back_n(that->fRects.count(), that->fRects.begin());
355        this->joinBounds(*that);
356        return true;
357    }
358
359    struct RectInfo {
360        SkRect fRect;
361        GrColor fColor;
362        SkRect fLocalRect;
363    };
364
365    SkSTArray<1, RectInfo, true> fRects;
366    Helper fHelper;
367    bool fHasLocalMatrix;
368    bool fHasLocalRect;
369    SkMatrix fLocalMatrix;
370    SkMatrix fViewMatrix;
371
372    typedef GrMeshDrawOp INHERITED;
373};
374
375}  // anonymous namespace
376
377namespace GrRectOpFactory {
378
379std::unique_ptr<GrDrawOp> MakeNonAAFill(GrPaint&& paint, const SkMatrix& viewMatrix,
380                                        const SkRect& rect, GrAAType aaType,
381                                        const GrUserStencilSettings* stencilSettings) {
382    if (viewMatrix.hasPerspective()) {
383        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
384                                                nullptr, aaType, stencilSettings);
385    } else {
386        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, nullptr, aaType,
387                                     stencilSettings);
388    }
389}
390
391std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalMatrix(
392        GrPaint&& paint, const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
393        const SkRect& rect, GrAAType aaType, const GrUserStencilSettings* stencilSettings) {
394    if (viewMatrix.hasPerspective() || localMatrix.hasPerspective()) {
395        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
396                                                &localMatrix, aaType, stencilSettings);
397    } else {
398        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix,
399                                     aaType, stencilSettings);
400    }
401}
402
403std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix,
404                                                     const SkRect& rect, const SkRect& localRect,
405                                                     GrAAType aaType) {
406    if (viewMatrix.hasPerspective()) {
407        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, &localRect,
408                                                nullptr, aaType, nullptr);
409    } else {
410        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, &localRect, nullptr,
411                                     aaType, nullptr);
412    }
413}
414
415}  // namespace GrRectOpFactory
416
417///////////////////////////////////////////////////////////////////////////////////////////////////
418
419#if GR_TEST_UTILS
420
421GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp) {
422    SkRect rect = GrTest::TestRect(random);
423    SkRect localRect = GrTest::TestRect(random);
424    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
425    SkMatrix localMatrix = GrTest::TestMatrix(random);
426    const GrUserStencilSettings* stencil = GrGetRandomStencil(random, context);
427    GrAAType aaType = GrAAType::kNone;
428    if (fsaaType == GrFSAAType::kUnifiedMSAA) {
429        aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
430    }
431    const SkRect* lr = random->nextBool() ? &localRect : nullptr;
432    const SkMatrix* lm = random->nextBool() ? &localMatrix : nullptr;
433    if (viewMatrix.hasPerspective() || (lm && lm->hasPerspective())) {
434        return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType,
435                                                stencil);
436    } else {
437        return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType, stencil);
438    }
439}
440
441#endif
442