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 "GrNonAAFillRectOp.h"
9 
10 #include "GrColor.h"
11 #include "GrDefaultGeoProcFactory.h"
12 #include "GrMeshDrawOp.h"
13 #include "GrOpFlushState.h"
14 #include "GrPrimitiveProcessor.h"
15 #include "GrQuad.h"
16 #include "GrResourceProvider.h"
17 
18 static const int kVertsPerInstance = 4;
19 static const int kIndicesPerInstance = 6;
20 
21 /** We always use per-vertex colors so that rects can be combined across color changes. Sometimes
22     we  have explicit local coords and sometimes not. We *could* always provide explicit local
23     coords and just duplicate the positions when the caller hasn't provided a local coord rect,
24     but we haven't seen a use case which frequently switches between local rect and no local
25     rect draws.
26 
27     The vertex attrib order is always pos, color, [local coords].
28  */
make_persp_gp(const SkMatrix & viewMatrix,bool hasExplicitLocalCoords,const SkMatrix * localMatrix)29 static sk_sp<GrGeometryProcessor> make_persp_gp(const SkMatrix& viewMatrix,
30                                                 bool hasExplicitLocalCoords,
31                                                 const SkMatrix* localMatrix) {
32     SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
33 
34     using namespace GrDefaultGeoProcFactory;
35 
36     // If we have perspective on the viewMatrix then we won't map on the CPU, nor will we map
37     // the local rect on the cpu (in case the localMatrix also has perspective).
38     // Otherwise, if we have a local rect, then we apply the localMatrix directly to the localRect
39     // to generate vertex local coords
40     if (viewMatrix.hasPerspective()) {
41         LocalCoords localCoords(hasExplicitLocalCoords ? LocalCoords::kHasExplicit_Type
42                                                        : LocalCoords::kUsePosition_Type,
43                                 localMatrix);
44         return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
45                                              Coverage::kSolid_Type, localCoords, viewMatrix);
46     } else if (hasExplicitLocalCoords) {
47         LocalCoords localCoords(LocalCoords::kHasExplicit_Type, localMatrix);
48         return GrDefaultGeoProcFactory::Make(Color::kPremulGrColorAttribute_Type,
49                                              Coverage::kSolid_Type, localCoords, SkMatrix::I());
50     } else {
51         LocalCoords localCoords(LocalCoords::kUsePosition_Type, localMatrix);
52         return GrDefaultGeoProcFactory::MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type,
53                                                            Coverage::kSolid_Type, localCoords,
54                                                            viewMatrix);
55     }
56 }
57 
tesselate(intptr_t vertices,size_t vertexStride,GrColor color,const SkMatrix * viewMatrix,const SkRect & rect,const GrQuad * localQuad)58 static void tesselate(intptr_t vertices,
59                       size_t vertexStride,
60                       GrColor color,
61                       const SkMatrix* viewMatrix,
62                       const SkRect& rect,
63                       const GrQuad* localQuad) {
64     SkPoint* positions = reinterpret_cast<SkPoint*>(vertices);
65 
66     positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
67 
68     if (viewMatrix) {
69         viewMatrix->mapPointsWithStride(positions, vertexStride, kVertsPerInstance);
70     }
71 
72     // Setup local coords
73     // TODO we should only do this if local coords are being read
74     if (localQuad) {
75         static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
76         for (int i = 0; i < kVertsPerInstance; i++) {
77             SkPoint* coords =
78                     reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
79             *coords = localQuad->point(i);
80         }
81     }
82 
83     static const int kColorOffset = sizeof(SkPoint);
84     GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
85     for (int j = 0; j < 4; ++j) {
86         *vertColor = color;
87         vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
88     }
89 }
90 
91 // We handle perspective in the local matrix or viewmatrix with special ops.
92 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
93 public:
94     DEFINE_OP_CLASS_ID
95 
NonAAFillRectPerspectiveOp(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix)96     NonAAFillRectPerspectiveOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
97                                const SkRect* localRect, const SkMatrix* localMatrix)
98             : INHERITED(ClassID()), fViewMatrix(viewMatrix) {
99         SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
100         RectInfo& info = fRects.push_back();
101         info.fColor = color;
102         info.fRect = rect;
103         fHasLocalRect = SkToBool(localRect);
104         fHasLocalMatrix = SkToBool(localMatrix);
105         if (fHasLocalMatrix) {
106             fLocalMatrix = *localMatrix;
107         }
108         if (fHasLocalRect) {
109             info.fLocalRect = *localRect;
110         }
111         this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
112     }
113 
name() const114     const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
115 
dumpInfo() const116     SkString dumpInfo() const override {
117         SkString str;
118         str.appendf("# combined: %d\n", fRects.count());
119         for (int i = 0; i < fRects.count(); ++i) {
120             const RectInfo& geo = fRects[0];
121             str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
122                         geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
123                         geo.fRect.fBottom);
124         }
125         str.append(DumpPipelineInfo(*this->pipeline()));
126         str.append(INHERITED::dumpInfo());
127         return str;
128     }
129 
130 private:
NonAAFillRectPerspectiveOp()131     NonAAFillRectPerspectiveOp() : INHERITED(ClassID()) {}
132 
getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor * color,GrPipelineAnalysisCoverage * coverage) const133     void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color,
134                                             GrPipelineAnalysisCoverage* coverage) const override {
135         color->setToConstant(fRects[0].fColor);
136         *coverage = GrPipelineAnalysisCoverage::kNone;
137     }
138 
applyPipelineOptimizations(const GrPipelineOptimizations & optimizations)139     void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override {
140         optimizations.getOverrideColorIfSet(&fRects[0].fColor);
141     }
142 
onPrepareDraws(Target * target) const143     void onPrepareDraws(Target* target) const override {
144         sk_sp<GrGeometryProcessor> gp = make_persp_gp(fViewMatrix,
145                                                       fHasLocalRect,
146                                                       fHasLocalMatrix ? &fLocalMatrix : nullptr);
147         if (!gp) {
148             SkDebugf("Couldn't create GrGeometryProcessor\n");
149             return;
150         }
151         SkASSERT(fHasLocalRect
152                          ? gp->getVertexStride() ==
153                                    sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
154                          : gp->getVertexStride() ==
155                                    sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
156 
157         size_t vertexStride = gp->getVertexStride();
158         int instanceCount = fRects.count();
159 
160         sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
161         InstancedHelper helper;
162         void* vertices =
163                 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(),
164                             kVertsPerInstance, kIndicesPerInstance, instanceCount);
165         if (!vertices || !indexBuffer) {
166             SkDebugf("Could not allocate vertices\n");
167             return;
168         }
169 
170         for (int i = 0; i < instanceCount; i++) {
171             const RectInfo& info = fRects[i];
172             intptr_t verts =
173                     reinterpret_cast<intptr_t>(vertices) + i * kVertsPerInstance * vertexStride;
174             if (fHasLocalRect) {
175                 GrQuad quad(info.fLocalRect);
176                 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
177             } else {
178                 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
179             }
180         }
181         helper.recordDraw(target, gp.get());
182     }
183 
onCombineIfPossible(GrOp * t,const GrCaps & caps)184     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
185         NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
186         if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
187                                     that->bounds(), caps)) {
188             return false;
189         }
190 
191         // We could combine across perspective vm changes if we really wanted to.
192         if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
193             return false;
194         }
195         if (fHasLocalRect != that->fHasLocalRect) {
196             return false;
197         }
198         if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
199             return false;
200         }
201 
202         fRects.push_back_n(that->fRects.count(), that->fRects.begin());
203         this->joinBounds(*that);
204         return true;
205     }
206 
207     struct RectInfo {
208         SkRect fRect;
209         GrColor fColor;
210         SkRect fLocalRect;
211     };
212 
213     SkSTArray<1, RectInfo, true> fRects;
214     bool fHasLocalMatrix;
215     bool fHasLocalRect;
216     SkMatrix fLocalMatrix;
217     SkMatrix fViewMatrix;
218 
219     typedef GrMeshDrawOp INHERITED;
220 };
221 
222 namespace GrNonAAFillRectOp {
223 
MakeWithPerspective(GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix)224 std::unique_ptr<GrMeshDrawOp> MakeWithPerspective(GrColor color,
225                                                   const SkMatrix& viewMatrix,
226                                                   const SkRect& rect,
227                                                   const SkRect* localRect,
228                                                   const SkMatrix* localMatrix) {
229     return std::unique_ptr<GrMeshDrawOp>(
230             new NonAAFillRectPerspectiveOp(color, viewMatrix, rect, localRect, localMatrix));
231 }
232 };
233 
234 ///////////////////////////////////////////////////////////////////////////////////////////////////
235 
236 #if GR_TEST_UTILS
237 
238 #include "GrDrawOpTest.h"
239 
DRAW_OP_TEST_DEFINE(NonAAFillRectPerspectiveOp)240 DRAW_OP_TEST_DEFINE(NonAAFillRectPerspectiveOp) {
241     GrColor color = GrRandomColor(random);
242     SkRect rect = GrTest::TestRect(random);
243     SkRect localRect = GrTest::TestRect(random);
244     SkMatrix viewMatrix = GrTest::TestMatrix(random);
245     bool hasLocalMatrix = random->nextBool();
246     SkMatrix localMatrix;
247     if (!viewMatrix.hasPerspective()) {
248         localMatrix = GrTest::TestMatrixPerspective(random);
249         hasLocalMatrix = true;
250     }
251 
252     bool hasLocalRect = random->nextBool();
253     return GrNonAAFillRectOp::MakeWithPerspective(color, viewMatrix, rect,
254                                                   hasLocalRect ? &localRect : nullptr,
255                                                   hasLocalMatrix ? &localMatrix : nullptr);
256 }
257 
258 #endif
259