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 
21 static const int kVertsPerRect = 4;
22 static 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  */
make_gp()32 static 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 
make_perspective_gp(const SkMatrix & viewMatrix,bool hasExplicitLocalCoords,const SkMatrix * localMatrix)38 static 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 
tesselate(intptr_t vertices,size_t vertexStride,GrColor color,const SkMatrix * viewMatrix,const SkRect & rect,const GrQuad * localQuad)67 static 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 
101 namespace {
102 
103 class NonAAFillRectOp final : public GrMeshDrawOp {
104 private:
105     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
106 
107 public:
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)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 
NonAAFillRectOp(const Helper::MakeArgs & args,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)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 
name() const141     const char* name() const override { return "NonAAFillRectOp"; }
142 
visitProxies(const VisitProxyFunc & func) const143     void visitProxies(const VisitProxyFunc& func) const override {
144         fHelper.visitProxies(func);
145     }
146 
dumpInfo() const147     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 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)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 
fixedFunctionFlags() const169     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
170 
171     DEFINE_OP_CLASS_ID
172 
173 private:
onPrepareDraws(Target * target)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 
onCombineIfPossible(GrOp * t,const GrCaps & caps)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.
227 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
228 private:
229     using Helper = GrSimpleMeshDrawOpHelperWithStencil;
230 
231 public:
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)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 
NonAAFillRectPerspectiveOp(const Helper::MakeArgs & args,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)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 
name() const266     const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
267 
visitProxies(const VisitProxyFunc & func) const268     void visitProxies(const VisitProxyFunc& func) const override {
269         fHelper.visitProxies(func);
270     }
271 
dumpInfo() const272     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 
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrPixelConfigIsClamped dstIsClamped)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 
fixedFunctionFlags() const293     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
294 
295     DEFINE_OP_CLASS_ID
296 
297 private:
onPrepareDraws(Target * target)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 
onCombineIfPossible(GrOp * t,const GrCaps & caps)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 
377 namespace GrRectOpFactory {
378 
MakeNonAAFill(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,GrAAType aaType,const GrUserStencilSettings * stencilSettings)379 std::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 
MakeNonAAFillWithLocalMatrix(GrPaint && paint,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect,GrAAType aaType,const GrUserStencilSettings * stencilSettings)391 std::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 
MakeNonAAFillWithLocalRect(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & localRect,GrAAType aaType)403 std::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 
GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp)421 GR_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