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