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 positions->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
76
77 if (viewMatrix) {
78 SkMatrixPriv::MapPointsWithStride(*viewMatrix, positions, vertexStride, kVertsPerRect);
79 }
80
81 // Setup local coords
82 // TODO we should only do this if local coords are being read
83 if (localQuad) {
84 static const int kLocalOffset = sizeof(SkPoint) + sizeof(GrColor);
85 for (int i = 0; i < kVertsPerRect; i++) {
86 SkPoint* coords =
87 reinterpret_cast<SkPoint*>(vertices + kLocalOffset + i * vertexStride);
88 *coords = localQuad->point(i);
89 }
90 }
91
92 static const int kColorOffset = sizeof(SkPoint);
93 GrColor* vertColor = reinterpret_cast<GrColor*>(vertices + kColorOffset);
94 for (int j = 0; j < 4; ++j) {
95 *vertColor = color;
96 vertColor = (GrColor*)((intptr_t)vertColor + vertexStride);
97 }
98 }
99
100 namespace {
101
102 class NonAAFillRectOp final : public GrMeshDrawOp {
103 private:
104 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
105
106 public:
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)107 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
108 const SkRect& rect, const SkRect* localRect,
109 const SkMatrix* localMatrix, GrAAType aaType,
110 const GrUserStencilSettings* stencilSettings) {
111 SkASSERT(GrAAType::kCoverage != aaType);
112 return Helper::FactoryHelper<NonAAFillRectOp>(std::move(paint), viewMatrix, rect, localRect,
113 localMatrix, aaType, stencilSettings);
114 }
115
116 NonAAFillRectOp() = delete;
117
NonAAFillRectOp(const Helper::MakeArgs & args,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)118 NonAAFillRectOp(const Helper::MakeArgs& args, GrColor color, const SkMatrix& viewMatrix,
119 const SkRect& rect, const SkRect* localRect, const SkMatrix* localMatrix,
120 GrAAType aaType, const GrUserStencilSettings* stencilSettings)
121 : INHERITED(ClassID()), fHelper(args, aaType, stencilSettings) {
122
123 SkASSERT(!viewMatrix.hasPerspective() && (!localMatrix || !localMatrix->hasPerspective()));
124 RectInfo& info = fRects.push_back();
125 info.fColor = color;
126 info.fViewMatrix = viewMatrix;
127 info.fRect = rect;
128 if (localRect && localMatrix) {
129 info.fLocalQuad.setFromMappedRect(*localRect, *localMatrix);
130 } else if (localRect) {
131 info.fLocalQuad.set(*localRect);
132 } else if (localMatrix) {
133 info.fLocalQuad.setFromMappedRect(rect, *localMatrix);
134 } else {
135 info.fLocalQuad.set(rect);
136 }
137 this->setTransformedBounds(fRects[0].fRect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
138 }
139
name() const140 const char* name() const override { return "NonAAFillRectOp"; }
141
dumpInfo() const142 SkString dumpInfo() const override {
143 SkString str;
144 str.append(GrMeshDrawOp::dumpInfo());
145 str.appendf("# combined: %d\n", fRects.count());
146 for (int i = 0; i < fRects.count(); ++i) {
147 const RectInfo& info = fRects[i];
148 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
149 info.fColor, info.fRect.fLeft, info.fRect.fTop, info.fRect.fRight,
150 info.fRect.fBottom);
151 }
152 str += fHelper.dumpInfo();
153 str += INHERITED::dumpInfo();
154 return str;
155 }
156
finalize(const GrCaps & caps,const GrAppliedClip * clip)157 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
158 GrColor* color = &fRects.front().fColor;
159 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color);
160 }
161
fixedFunctionFlags() const162 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
163
164 DEFINE_OP_CLASS_ID
165
166 private:
onPrepareDraws(Target * target) const167 void onPrepareDraws(Target* target) const override {
168 sk_sp<GrGeometryProcessor> gp = make_gp();
169 if (!gp) {
170 SkDebugf("Couldn't create GrGeometryProcessor\n");
171 return;
172 }
173 SkASSERT(gp->getVertexStride() ==
174 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr));
175
176 size_t vertexStride = gp->getVertexStride();
177 int rectCount = fRects.count();
178
179 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
180 PatternHelper helper(GrPrimitiveType::kTriangles);
181 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
182 kIndicesPerRect, rectCount);
183 if (!vertices || !indexBuffer) {
184 SkDebugf("Could not allocate vertices\n");
185 return;
186 }
187
188 for (int i = 0; i < rectCount; i++) {
189 intptr_t verts =
190 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
191 tesselate(verts, vertexStride, fRects[i].fColor, &fRects[i].fViewMatrix,
192 fRects[i].fRect, &fRects[i].fLocalQuad);
193 }
194 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
195 }
196
onCombineIfPossible(GrOp * t,const GrCaps & caps)197 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
198 NonAAFillRectOp* that = t->cast<NonAAFillRectOp>();
199 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
200 return false;
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 GrColor fColor;
209 SkMatrix fViewMatrix;
210 SkRect fRect;
211 GrQuad fLocalQuad;
212 };
213
214 Helper fHelper;
215 SkSTArray<1, RectInfo, true> fRects;
216 typedef GrMeshDrawOp INHERITED;
217 };
218
219 // We handle perspective in the local matrix or viewmatrix with special ops.
220 class NonAAFillRectPerspectiveOp final : public GrMeshDrawOp {
221 private:
222 using Helper = GrSimpleMeshDrawOpHelperWithStencil;
223
224 public:
Make(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)225 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix,
226 const SkRect& rect, const SkRect* localRect,
227 const SkMatrix* localMatrix, GrAAType aaType,
228 const GrUserStencilSettings* stencilSettings) {
229 SkASSERT(GrAAType::kCoverage != aaType);
230 return Helper::FactoryHelper<NonAAFillRectPerspectiveOp>(std::move(paint), viewMatrix, rect,
231 localRect, localMatrix, aaType,
232 stencilSettings);
233 }
234
235 NonAAFillRectPerspectiveOp() = delete;
236
NonAAFillRectPerspectiveOp(const Helper::MakeArgs & args,GrColor color,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect * localRect,const SkMatrix * localMatrix,GrAAType aaType,const GrUserStencilSettings * stencilSettings)237 NonAAFillRectPerspectiveOp(const Helper::MakeArgs& args, GrColor color,
238 const SkMatrix& viewMatrix, const SkRect& rect,
239 const SkRect* localRect, const SkMatrix* localMatrix,
240 GrAAType aaType, const GrUserStencilSettings* stencilSettings)
241 : INHERITED(ClassID())
242 , fHelper(args, aaType, stencilSettings)
243 , fViewMatrix(viewMatrix) {
244 SkASSERT(viewMatrix.hasPerspective() || (localMatrix && localMatrix->hasPerspective()));
245 RectInfo& info = fRects.push_back();
246 info.fColor = color;
247 info.fRect = rect;
248 fHasLocalRect = SkToBool(localRect);
249 fHasLocalMatrix = SkToBool(localMatrix);
250 if (fHasLocalMatrix) {
251 fLocalMatrix = *localMatrix;
252 }
253 if (fHasLocalRect) {
254 info.fLocalRect = *localRect;
255 }
256 this->setTransformedBounds(rect, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
257 }
258
name() const259 const char* name() const override { return "NonAAFillRectPerspectiveOp"; }
260
dumpInfo() const261 SkString dumpInfo() const override {
262 SkString str;
263 str.appendf("# combined: %d\n", fRects.count());
264 for (int i = 0; i < fRects.count(); ++i) {
265 const RectInfo& geo = fRects[0];
266 str.appendf("%d: Color: 0x%08x, Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
267 geo.fColor, geo.fRect.fLeft, geo.fRect.fTop, geo.fRect.fRight,
268 geo.fRect.fBottom);
269 }
270 str += fHelper.dumpInfo();
271 str += INHERITED::dumpInfo();
272 return str;
273 }
274
finalize(const GrCaps & caps,const GrAppliedClip * clip)275 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
276 GrColor* color = &fRects.front().fColor;
277 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kNone, color);
278 }
279
fixedFunctionFlags() const280 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
281
282 DEFINE_OP_CLASS_ID
283
284 private:
onPrepareDraws(Target * target) const285 void onPrepareDraws(Target* target) const override {
286 sk_sp<GrGeometryProcessor> gp = make_perspective_gp(
287 fViewMatrix, fHasLocalRect, fHasLocalMatrix ? &fLocalMatrix : nullptr);
288 if (!gp) {
289 SkDebugf("Couldn't create GrGeometryProcessor\n");
290 return;
291 }
292 SkASSERT(fHasLocalRect
293 ? gp->getVertexStride() ==
294 sizeof(GrDefaultGeoProcFactory::PositionColorLocalCoordAttr)
295 : gp->getVertexStride() ==
296 sizeof(GrDefaultGeoProcFactory::PositionColorAttr));
297
298 size_t vertexStride = gp->getVertexStride();
299 int rectCount = fRects.count();
300
301 sk_sp<const GrBuffer> indexBuffer(target->resourceProvider()->refQuadIndexBuffer());
302 PatternHelper helper(GrPrimitiveType::kTriangles);
303 void* vertices = helper.init(target, vertexStride, indexBuffer.get(), kVertsPerRect,
304 kIndicesPerRect, rectCount);
305 if (!vertices || !indexBuffer) {
306 SkDebugf("Could not allocate vertices\n");
307 return;
308 }
309
310 for (int i = 0; i < rectCount; i++) {
311 const RectInfo& info = fRects[i];
312 intptr_t verts =
313 reinterpret_cast<intptr_t>(vertices) + i * kVertsPerRect * vertexStride;
314 if (fHasLocalRect) {
315 GrQuad quad(info.fLocalRect);
316 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, &quad);
317 } else {
318 tesselate(verts, vertexStride, info.fColor, nullptr, info.fRect, nullptr);
319 }
320 }
321 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target));
322 }
323
onCombineIfPossible(GrOp * t,const GrCaps & caps)324 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
325 NonAAFillRectPerspectiveOp* that = t->cast<NonAAFillRectPerspectiveOp>();
326 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
327 return false;
328 }
329
330 // We could combine across perspective vm changes if we really wanted to.
331 if (!fViewMatrix.cheapEqualTo(that->fViewMatrix)) {
332 return false;
333 }
334 if (fHasLocalRect != that->fHasLocalRect) {
335 return false;
336 }
337 if (fHasLocalMatrix && !fLocalMatrix.cheapEqualTo(that->fLocalMatrix)) {
338 return false;
339 }
340
341 fRects.push_back_n(that->fRects.count(), that->fRects.begin());
342 this->joinBounds(*that);
343 return true;
344 }
345
346 struct RectInfo {
347 SkRect fRect;
348 GrColor fColor;
349 SkRect fLocalRect;
350 };
351
352 SkSTArray<1, RectInfo, true> fRects;
353 Helper fHelper;
354 bool fHasLocalMatrix;
355 bool fHasLocalRect;
356 SkMatrix fLocalMatrix;
357 SkMatrix fViewMatrix;
358
359 typedef GrMeshDrawOp INHERITED;
360 };
361
362 } // anonymous namespace
363
364 namespace GrRectOpFactory {
365
MakeNonAAFill(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,GrAAType aaType,const GrUserStencilSettings * stencilSettings)366 std::unique_ptr<GrDrawOp> MakeNonAAFill(GrPaint&& paint, const SkMatrix& viewMatrix,
367 const SkRect& rect, GrAAType aaType,
368 const GrUserStencilSettings* stencilSettings) {
369 if (viewMatrix.hasPerspective()) {
370 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
371 nullptr, aaType, stencilSettings);
372 } else {
373 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, nullptr, aaType,
374 stencilSettings);
375 }
376 }
377
MakeNonAAFillWithLocalMatrix(GrPaint && paint,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect,GrAAType aaType,const GrUserStencilSettings * stencilSettings)378 std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalMatrix(
379 GrPaint&& paint, const SkMatrix& viewMatrix, const SkMatrix& localMatrix,
380 const SkRect& rect, GrAAType aaType, const GrUserStencilSettings* stencilSettings) {
381 if (viewMatrix.hasPerspective() || localMatrix.hasPerspective()) {
382 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, nullptr,
383 &localMatrix, aaType, stencilSettings);
384 } else {
385 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, nullptr, &localMatrix,
386 aaType, stencilSettings);
387 }
388 }
389
MakeNonAAFillWithLocalRect(GrPaint && paint,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & localRect,GrAAType aaType)390 std::unique_ptr<GrDrawOp> MakeNonAAFillWithLocalRect(GrPaint&& paint, const SkMatrix& viewMatrix,
391 const SkRect& rect, const SkRect& localRect,
392 GrAAType aaType) {
393 if (viewMatrix.hasPerspective()) {
394 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, &localRect,
395 nullptr, aaType, nullptr);
396 } else {
397 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, &localRect, nullptr,
398 aaType, nullptr);
399 }
400 }
401
402 } // namespace GrRectOpFactory
403
404 ///////////////////////////////////////////////////////////////////////////////////////////////////
405
406 #if GR_TEST_UTILS
407
GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp)408 GR_DRAW_OP_TEST_DEFINE(NonAAFillRectOp) {
409 SkRect rect = GrTest::TestRect(random);
410 SkRect localRect = GrTest::TestRect(random);
411 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
412 SkMatrix localMatrix = GrTest::TestMatrix(random);
413 const GrUserStencilSettings* stencil = GrGetRandomStencil(random, context);
414 GrAAType aaType = GrAAType::kNone;
415 if (fsaaType == GrFSAAType::kUnifiedMSAA) {
416 aaType = random->nextBool() ? GrAAType::kMSAA : GrAAType::kNone;
417 }
418 const SkRect* lr = random->nextBool() ? &localRect : nullptr;
419 const SkMatrix* lm = random->nextBool() ? &localMatrix : nullptr;
420 if (viewMatrix.hasPerspective() || (lm && lm->hasPerspective())) {
421 return NonAAFillRectPerspectiveOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType,
422 stencil);
423 } else {
424 return NonAAFillRectOp::Make(std::move(paint), viewMatrix, rect, lr, lm, aaType, stencil);
425 }
426 }
427
428 #endif
429