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