Flutter Engine
The Flutter Engine
SmallPathRenderer.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2014 Google Inc.
3 * Copyright 2017 ARM Ltd.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
10
14#include "src/core/SkDraw.h"
33
34using namespace skia_private;
35
36#if !defined(SK_ENABLE_OPTIMIZE_SIZE)
37
39
40namespace skgpu::ganesh {
41
42namespace {
43
44// mip levels
45static constexpr SkScalar kIdealMinMIP = 12;
46static constexpr SkScalar kMaxMIP = 162;
47
48static constexpr SkScalar kMaxDim = 73;
49static constexpr SkScalar kMinSize = SK_ScalarHalf;
50static constexpr SkScalar kMaxSize = 2*kMaxMIP;
51
52////////////////////////////////////////////////////////////////////////////////
53
54// padding around path bounds to allow for antialiased pixels
55static const int kAntiAliasPad = 1;
56
57class SmallPathOp final : public GrMeshDrawOp {
58private:
60
61public:
63
64 static GrOp::Owner Make(GrRecordingContext* context,
65 GrPaint&& paint,
66 const GrStyledShape& shape,
67 const SkMatrix& viewMatrix,
68 bool gammaCorrect,
69 const GrUserStencilSettings* stencilSettings) {
70 return Helper::FactoryHelper<SmallPathOp>(context, std::move(paint), shape, viewMatrix,
71 gammaCorrect, stencilSettings);
72 }
73
74 SmallPathOp(GrProcessorSet* processorSet, const SkPMColor4f& color, const GrStyledShape& shape,
75 const SkMatrix& viewMatrix, bool gammaCorrect,
76 const GrUserStencilSettings* stencilSettings)
77 : INHERITED(ClassID())
78 , fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
79 SkASSERT(shape.hasUnstyledKey());
80 // Compute bounds
81 this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsHairline::kNo);
82
83#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
84 fUsesDistanceField = true;
85#else
86 // only use distance fields on desktop and Android framework to save space in the atlas
87 fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
88#endif
89 // always use distance fields if in perspective
90 fUsesDistanceField = fUsesDistanceField || viewMatrix.hasPerspective();
91
92 fShapes.emplace_back(Entry{color, shape, viewMatrix});
93
94 fGammaCorrect = gammaCorrect;
95 }
96
97 const char* name() const override { return "SmallPathOp"; }
98
99 void visitProxies(const GrVisitProxyFunc& func) const override {
100 fHelper.visitProxies(func);
101 }
102
103 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
104
105 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
106 GrClampType clampType) override {
107 return fHelper.finalizeProcessors(caps, clip, clampType,
109 &fShapes.front().fColor, &fWideColor);
110 }
111
112private:
113 struct FlushInfo {
120 };
121
122 GrProgramInfo* programInfo() override {
123 // TODO [PI]: implement
124 return nullptr;
125 }
126
127 void onCreateProgramInfo(const GrCaps*,
129 const GrSurfaceProxyView& writeView,
130 bool usesMSAASurface,
132 const GrDstProxyView&,
133 GrXferBarrierFlags renderPassXferBarriers,
134 GrLoadOp colorLoadOp) override {
135 // We cannot surface the SmallPathOp's programInfo at record time. As currently
136 // implemented, the GP is modified at flush time based on the number of pages in the
137 // atlas.
138 }
139
140 void onPrePrepareDraws(GrRecordingContext*,
141 const GrSurfaceProxyView& writeView,
143 const GrDstProxyView&,
144 GrXferBarrierFlags renderPassXferBarriers,
145 GrLoadOp colorLoadOp) override {
146 // TODO [PI]: implement
147 }
148
149 void onPrepareDraws(GrMeshDrawTarget* target) override {
150 int instanceCount = fShapes.size();
151
152 auto atlasMgr = target->smallPathAtlasManager();
153 if (!atlasMgr) {
154 return;
155 }
156
157 static constexpr int kMaxTextures = GrDistanceFieldPathGeoProc::kMaxTextures;
158 static_assert(GrBitmapTextGeoProc::kMaxTextures == kMaxTextures);
159
160 FlushInfo flushInfo;
161 flushInfo.fPrimProcProxies = target->allocPrimProcProxyPtrs(kMaxTextures);
162
163 int numActiveProxies;
164 const GrSurfaceProxyView* views = atlasMgr->getViews(&numActiveProxies);
165 for (int i = 0; i < numActiveProxies; ++i) {
166 // This op does not know its atlas proxies when it is added to a OpsTasks, so the
167 // proxies don't get added during the visitProxies call. Thus we add them here.
168 flushInfo.fPrimProcProxies[i] = views[i].proxy();
169 target->sampledProxyArray()->push_back(views[i].proxy());
170 }
171
172 // Setup GrGeometryProcessor
173 const SkMatrix& ctm = fShapes[0].fViewMatrix;
175 if (fHelper.usesLocalCoords()) {
176 if (!ctm.invert(&invert)) {
177 return;
178 }
179 }
180 if (fUsesDistanceField) {
181 uint32_t flags = 0;
182 // Still need to key off of ctm to pick the right shader for the transformed quad
185 flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
186 flags |= fWideColor ? kWideColor_DistanceFieldEffectFlag : 0;
187 // We always use Point3 for position
189
190 flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
191 target->allocator(), *target->caps().shaderCaps(),
192 views, numActiveProxies, GrSamplerState::Filter::kLinear,
193 invert, flags);
194 } else {
195 flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
196 target->allocator(), *target->caps().shaderCaps(), this->color(), fWideColor,
197 /*colorSpaceXform=*/nullptr, views, numActiveProxies,
199 }
200
201 // allocate vertices
202 const size_t kVertexStride = flushInfo.fGeometryProcessor->vertexStride();
203
204 // We need to make sure we don't overflow a 32 bit int when we request space in the
205 // makeVertexSpace call below.
206 if (instanceCount > SK_MaxS32 / GrResourceProvider::NumVertsPerNonAAQuad()) {
207 return;
208 }
209 VertexWriter vertices = target->makeVertexWriter(
210 kVertexStride, GrResourceProvider::NumVertsPerNonAAQuad() * instanceCount,
211 &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset);
212
213 flushInfo.fIndexBuffer = target->resourceProvider()->refNonAAQuadIndexBuffer();
214 if (!vertices || !flushInfo.fIndexBuffer) {
215 SkDebugf("Could not allocate vertices\n");
216 return;
217 }
218
219 flushInfo.fInstancesToFlush = 0;
220 for (int i = 0; i < instanceCount; i++) {
221 const Entry& args = fShapes[i];
222
224 if (fUsesDistanceField) {
225 // get mip level
226 SkScalar maxScale;
227 const SkRect& bounds = args.fShape.bounds();
228 if (args.fViewMatrix.hasPerspective()) {
229 // approximate the scale since we can't get it from the matrix
230 SkRect xformedBounds;
231 args.fViewMatrix.mapRect(&xformedBounds, bounds);
232 maxScale = SkScalarAbs(std::max(xformedBounds.width() / bounds.width(),
233 xformedBounds.height() / bounds.height()));
234 } else {
235 maxScale = SkScalarAbs(args.fViewMatrix.getMaxScale());
236 }
237 SkScalar maxDim = std::max(bounds.width(), bounds.height());
238 // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
239 // In the majority of cases this will yield a crisper rendering.
240 SkScalar mipScale = 1.0f;
241 // Our mipscale is the maxScale clamped to the next highest power of 2
242 if (maxScale <= SK_ScalarHalf) {
244 mipScale = SkScalarPow(2, -log);
245 } else if (maxScale > SK_Scalar1) {
247 mipScale = SkScalarPow(2, log);
248 }
249 // Log2 isn't very precise at values close to a power of 2,
250 // so add a little tolerance here. A little bit of scaling up is fine.
251 SkASSERT(maxScale <= mipScale + SK_ScalarNearlyZero);
252
253 SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
254 // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
255 // so we can preserve as much detail as possible. However, we can't scale down more
256 // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
257 // just bigger than the ideal, and then scale down until we are no more than 4x the
258 // original mipsize.
259 if (mipSize < kIdealMinMIP) {
260 SkScalar newMipSize = mipSize;
261 do {
262 newMipSize *= 2;
263 } while (newMipSize < kIdealMinMIP);
264 while (newMipSize > 4 * mipSize) {
265 newMipSize *= 0.25f;
266 }
267 mipSize = newMipSize;
268 }
269
270 SkScalar desiredDimension = std::min(mipSize, kMaxMIP);
271 int ceilDesiredDimension = SkScalarCeilToInt(desiredDimension);
272
273 // check to see if df path is cached
274 shapeData = atlasMgr->findOrCreate(args.fShape, ceilDesiredDimension);
275 if (!shapeData->fAtlasLocator.plotLocator().isValid()) {
276 SkScalar scale = desiredDimension / maxDim;
277
278 if (!this->addDFPathToAtlas(target,
279 &flushInfo,
280 atlasMgr,
281 shapeData,
282 args.fShape,
283 ceilDesiredDimension,
284 scale)) {
285 atlasMgr->deleteCacheEntry(shapeData);
286 continue;
287 }
288 }
289 } else {
290 // check to see if bitmap path is cached
291 shapeData = atlasMgr->findOrCreate(args.fShape, args.fViewMatrix);
292 if (!shapeData->fAtlasLocator.plotLocator().isValid()) {
293 if (!this->addBMPathToAtlas(target,
294 &flushInfo,
295 atlasMgr,
296 shapeData,
297 args.fShape,
298 args.fViewMatrix)) {
299 atlasMgr->deleteCacheEntry(shapeData);
300 continue;
301 }
302 }
303 }
304
305 auto uploadTarget = target->deferredUploadTarget();
306 atlasMgr->setUseToken(shapeData, uploadTarget->tokenTracker()->nextDrawToken());
307
308 this->writePathVertices(vertices, VertexColor(args.fColor, fWideColor),
309 args.fViewMatrix, shapeData);
310 flushInfo.fInstancesToFlush++;
311 }
312
313 this->flush(target, &flushInfo);
314 }
315
316 bool addToAtlasWithRetry(GrMeshDrawTarget* target,
317 FlushInfo* flushInfo,
319 int width,
320 int height,
321 const void* image,
322 const SkRect& bounds,
323 int srcInset,
324 skgpu::ganesh::SmallPathShapeData* shapeData) const {
325 auto resourceProvider = target->resourceProvider();
326 auto uploadTarget = target->deferredUploadTarget();
327
328 auto code = atlasMgr->addToAtlas(resourceProvider, uploadTarget, width, height,
329 image, &shapeData->fAtlasLocator);
331 return false;
332 }
333
335 this->flush(target, flushInfo);
336
337 code = atlasMgr->addToAtlas(resourceProvider, uploadTarget, width, height,
338 image, &shapeData->fAtlasLocator);
339 }
340
341 shapeData->fAtlasLocator.insetSrc(srcInset);
342 shapeData->fBounds = bounds;
343
345 }
346
347 bool addDFPathToAtlas(GrMeshDrawTarget* target,
348 FlushInfo* flushInfo,
351 const GrStyledShape& shape,
352 uint32_t dimension,
353 SkScalar scale) const {
354 const SkRect& bounds = shape.bounds();
355
356 // generate bounding rect for bitmap draw
357 SkRect scaledBounds = bounds;
358 // scale to mip level size
359 scaledBounds.fLeft *= scale;
360 scaledBounds.fTop *= scale;
361 scaledBounds.fRight *= scale;
362 scaledBounds.fBottom *= scale;
363 // subtract out integer portion of origin
364 // (SDF created will be placed with fractional offset burnt in)
365 SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
366 SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
367 scaledBounds.offset(-dx, -dy);
368 // get integer boundary
369 SkIRect devPathBounds;
370 scaledBounds.roundOut(&devPathBounds);
371 // place devBounds at origin with padding to allow room for antialiasing
372 int width = devPathBounds.width() + 2 * kAntiAliasPad;
373 int height = devPathBounds.height() + 2 * kAntiAliasPad;
374 devPathBounds = SkIRect::MakeWH(width, height);
375 SkScalar translateX = kAntiAliasPad - dx;
376 SkScalar translateY = kAntiAliasPad - dy;
377
378 // draw path to bitmap
379 SkMatrix drawMatrix;
380 drawMatrix.setScale(scale, scale);
381 drawMatrix.postTranslate(translateX, translateY);
382
383 SkASSERT(devPathBounds.fLeft == 0);
384 SkASSERT(devPathBounds.fTop == 0);
385 SkASSERT(devPathBounds.width() > 0);
386 SkASSERT(devPathBounds.height() > 0);
387
388 // setup signed distance field storage
390 width = dfBounds.width();
391 height = dfBounds.height();
392 // TODO We should really generate this directly into the plot somehow
393 SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
394
395 SkPath path;
396 shape.asPath(&path);
397 // Generate signed distance field directly from SkPath
398 bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
399 path, drawMatrix, width, height,
400 width * sizeof(unsigned char));
401 if (!succeed) {
402 // setup bitmap backing
404 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) {
405 return false;
406 }
407 sk_bzero(dst.writable_addr(), dst.computeByteSize());
408
409 // rasterize path
411 paint.setStyle(SkPaint::kFill_Style);
412 paint.setAntiAlias(true);
413
414 SkDraw draw;
415
416 SkRasterClip rasterClip;
417 rasterClip.setRect(devPathBounds);
418 draw.fRC = &rasterClip;
419 draw.fCTM = &drawMatrix;
420 draw.fDst = dst;
421
422 draw.drawPathCoverage(path, paint);
423
424 // Generate signed distance field
425 SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
426 (const unsigned char*)dst.addr(),
427 dst.width(), dst.height(), dst.rowBytes());
428 }
429
430 SkRect drawBounds = SkRect::Make(devPathBounds).makeOffset(-translateX, -translateY);
431 drawBounds.fLeft /= scale;
432 drawBounds.fTop /= scale;
433 drawBounds.fRight /= scale;
434 drawBounds.fBottom /= scale;
435
436 return this->addToAtlasWithRetry(target, flushInfo, atlasMgr,
437 width, height, dfStorage.get(),
438 drawBounds, SK_DistanceFieldPad, shapeData);
439 }
440
441 bool addBMPathToAtlas(GrMeshDrawTarget* target,
442 FlushInfo* flushInfo,
445 const GrStyledShape& shape,
446 const SkMatrix& ctm) const {
447 const SkRect& bounds = shape.bounds();
448 if (bounds.isEmpty()) {
449 return false;
450 }
451 SkMatrix drawMatrix(ctm);
452 SkScalar tx = ctm.getTranslateX();
453 SkScalar ty = ctm.getTranslateY();
454 tx -= SkScalarFloorToScalar(tx);
455 ty -= SkScalarFloorToScalar(ty);
456 drawMatrix.set(SkMatrix::kMTransX, tx);
457 drawMatrix.set(SkMatrix::kMTransY, ty);
458 SkRect shapeDevBounds;
459 drawMatrix.mapRect(&shapeDevBounds, bounds);
460 SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
461 SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
462
463 // get integer boundary
464 SkIRect devPathBounds;
465 shapeDevBounds.roundOut(&devPathBounds);
466 // place devBounds at origin with padding to allow room for antialiasing
467 int width = devPathBounds.width() + 2 * kAntiAliasPad;
468 int height = devPathBounds.height() + 2 * kAntiAliasPad;
469 devPathBounds = SkIRect::MakeWH(width, height);
470 SkScalar translateX = kAntiAliasPad - dx;
471 SkScalar translateY = kAntiAliasPad - dy;
472
473 SkASSERT(devPathBounds.fLeft == 0);
474 SkASSERT(devPathBounds.fTop == 0);
475 SkASSERT(devPathBounds.width() > 0);
476 SkASSERT(devPathBounds.height() > 0);
477
478 SkPath path;
479 shape.asPath(&path);
480 // setup bitmap backing
482 if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) {
483 return false;
484 }
485 sk_bzero(dst.writable_addr(), dst.computeByteSize());
486
487 // rasterize path
489 paint.setStyle(SkPaint::kFill_Style);
490 paint.setAntiAlias(true);
491
492 SkDraw draw;
493
494 SkRasterClip rasterClip;
495 rasterClip.setRect(devPathBounds);
496 drawMatrix.postTranslate(translateX, translateY);
497 draw.fRC = &rasterClip;
498 draw.fCTM = &drawMatrix;
499 draw.fDst = dst;
500
501 draw.drawPathCoverage(path, paint);
502
503 SkRect drawBounds = SkRect::Make(devPathBounds).makeOffset(-translateX, -translateY);
504
505 return this->addToAtlasWithRetry(target, flushInfo, atlasMgr,
506 dst.width(), dst.height(), dst.addr(),
507 drawBounds, 0, shapeData);
508 }
509
510 void writePathVertices(VertexWriter& vertices,
511 const VertexColor& color,
512 const SkMatrix& ctm,
513 const skgpu::ganesh::SmallPathShapeData* shapeData) const {
514 SkRect translatedBounds(shapeData->fBounds);
515 if (!fUsesDistanceField) {
516 translatedBounds.offset(SkScalarFloorToScalar(ctm.get(SkMatrix::kMTransX)),
518 }
519
520 // set up texture coordinates
521 auto texCoords = VertexWriter::TriStripFromUVs(shapeData->fAtlasLocator.getUVs());
522
523 if (fUsesDistanceField) {
524 SkPoint pts[4];
525 SkPoint3 out[4];
526 translatedBounds.toQuad(pts);
527 ctm.mapHomogeneousPoints(out, pts, 4);
528
529 vertices << out[0] << color << texCoords.l << texCoords.t;
530 vertices << out[3] << color << texCoords.l << texCoords.b;
531 vertices << out[1] << color << texCoords.r << texCoords.t;
532 vertices << out[2] << color << texCoords.r << texCoords.b;
533 } else {
534 vertices.writeQuad(VertexWriter::TriStripFromRect(translatedBounds),
535 color,
536 texCoords);
537 }
538 }
539
540 void flush(GrMeshDrawTarget* target, FlushInfo* flushInfo) const {
541 auto atlasMgr = target->smallPathAtlasManager();
542 if (!atlasMgr) {
543 return;
544 }
545
546 int numActiveProxies;
547 const GrSurfaceProxyView* views = atlasMgr->getViews(&numActiveProxies);
548
549 GrGeometryProcessor* gp = flushInfo->fGeometryProcessor;
550 if (gp->numTextureSamplers() != numActiveProxies) {
551 for (int i = gp->numTextureSamplers(); i < numActiveProxies; ++i) {
552 flushInfo->fPrimProcProxies[i] = views[i].proxy();
553 // This op does not know its atlas proxies when it is added to a OpsTasks, so the
554 // proxies don't get added during the visitProxies call. Thus we add them here.
555 target->sampledProxyArray()->push_back(views[i].proxy());
556 }
557 // During preparation the number of atlas pages has increased.
558 // Update the proxies used in the GP to match.
559 if (fUsesDistanceField) {
560 reinterpret_cast<GrDistanceFieldPathGeoProc*>(gp)->addNewViews(
561 views, numActiveProxies, GrSamplerState::Filter::kLinear);
562 } else {
563 reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(
564 views, numActiveProxies, GrSamplerState::Filter::kNearest);
565 }
566 }
567
568 if (flushInfo->fInstancesToFlush) {
569 GrSimpleMesh* mesh = target->allocMesh();
570 mesh->setIndexedPatterned(flushInfo->fIndexBuffer,
572 flushInfo->fInstancesToFlush,
574 flushInfo->fVertexBuffer,
576 flushInfo->fVertexOffset);
577 target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fPrimProcProxies,
579 flushInfo->fVertexOffset += GrResourceProvider::NumVertsPerNonAAQuad() *
580 flushInfo->fInstancesToFlush;
581 flushInfo->fInstancesToFlush = 0;
582 }
583 }
584
585 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
586 auto pipeline = fHelper.createPipeline(flushState);
587
588 flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline,
589 fHelper.stencilSettings());
590 }
591
592 const SkPMColor4f& color() const { return fShapes[0].fColor; }
593 bool usesDistanceField() const { return fUsesDistanceField; }
594
595 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
596 SmallPathOp* that = t->cast<SmallPathOp>();
597 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
598 return CombineResult::kCannotCombine;
599 }
600
601 if (this->usesDistanceField() != that->usesDistanceField()) {
602 return CombineResult::kCannotCombine;
603 }
604
605 const SkMatrix& thisCtm = this->fShapes[0].fViewMatrix;
606 const SkMatrix& thatCtm = that->fShapes[0].fViewMatrix;
607
608 if (this->usesDistanceField()) {
609 // Need to make sure local matrices are identical
610 if (fHelper.usesLocalCoords() && !SkMatrixPriv::CheapEqual(thisCtm, thatCtm)) {
611 return CombineResult::kCannotCombine;
612 }
613
614 // Depending on the ctm we may have a different shader for SDF paths
615 if (thisCtm.isScaleTranslate() != thatCtm.isScaleTranslate() ||
616 thisCtm.isSimilarity() != thatCtm.isSimilarity()) {
617 return CombineResult::kCannotCombine;
618 }
619 } else {
620 if (thisCtm.hasPerspective() != thatCtm.hasPerspective()) {
621 return CombineResult::kCannotCombine;
622 }
623
624 // We can position on the cpu unless we're in perspective,
625 // but also need to make sure local matrices are identical
626 if ((thisCtm.hasPerspective() || fHelper.usesLocalCoords()) &&
627 !SkMatrixPriv::CheapEqual(thisCtm, thatCtm)) {
628 return CombineResult::kCannotCombine;
629 }
630 }
631
632 fShapes.push_back_n(that->fShapes.size(), that->fShapes.begin());
633 fWideColor |= that->fWideColor;
634 return CombineResult::kMerged;
635 }
636
637#if defined(GR_TEST_UTILS)
638 SkString onDumpInfo() const override {
639 SkString string;
640 for (const auto& geo : fShapes) {
641 string.appendf("Color: 0x%08x\n", geo.fColor.toBytes_RGBA());
642 }
643 string += fHelper.dumpInfo();
644 return string;
645 }
646#endif
647
648 bool fUsesDistanceField;
649
650 struct Entry {
654 };
655
656 STArray<1, Entry> fShapes;
657 Helper fHelper;
658 bool fGammaCorrect;
659 bool fWideColor;
660
661 using INHERITED = GrMeshDrawOp;
662};
663
664} // anonymous namespace
665
666///////////////////////////////////////////////////////////////////////////////////////////////////
667
668PathRenderer::CanDrawPath SmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
669 if (!args.fCaps->shaderCaps()->fShaderDerivativeSupport) {
670 return CanDrawPath::kNo;
671 }
672 // If the shape has no key then we won't get any reuse.
673 if (!args.fShape->hasUnstyledKey()) {
674 return CanDrawPath::kNo;
675 }
676 // This only supports filled paths, however, the caller may apply the style to make a filled
677 // path and try again.
678 if (!args.fShape->style().isSimpleFill()) {
679 return CanDrawPath::kNo;
680 }
681 // This does non-inverse coverage-based antialiased fills.
682 if (GrAAType::kCoverage != args.fAAType) {
683 return CanDrawPath::kNo;
684 }
685 // TODO: Support inverse fill
686 if (args.fShape->inverseFilled()) {
687 return CanDrawPath::kNo;
688 }
689
690 SkScalar scaleFactors[2] = { 1, 1 };
691 // TODO: handle perspective distortion
692 if (!args.fViewMatrix->hasPerspective() && !args.fViewMatrix->getMinMaxScales(scaleFactors)) {
693 return CanDrawPath::kNo;
694 }
695 // For affine transformations, too much shear can produce artifacts.
696 if (!scaleFactors[0] || scaleFactors[1]/scaleFactors[0] > 4) {
697 return CanDrawPath::kNo;
698 }
699 // Only support paths with bounds within kMaxDim by kMaxDim,
700 // scaled to have bounds within kMaxSize by kMaxSize.
701 // The goal is to accelerate rendering of lots of small paths that may be scaling.
702 SkRect bounds = args.fShape->styledBounds();
703 SkScalar minDim = std::min(bounds.width(), bounds.height());
704 SkScalar maxDim = std::max(bounds.width(), bounds.height());
705 SkScalar minSize = minDim * SkScalarAbs(scaleFactors[0]);
706 SkScalar maxSize = maxDim * SkScalarAbs(scaleFactors[1]);
707 if (maxDim > kMaxDim || kMinSize > minSize || maxSize > kMaxSize) {
708 return CanDrawPath::kNo;
709 }
710
711 return CanDrawPath::kYes;
712}
713
714bool SmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
715 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
716 "SmallPathRenderer::onDrawPath");
717
718 // we've already bailed on inverse filled paths, so this is safe
719 SkASSERT(!args.fShape->isEmpty());
720 SkASSERT(args.fShape->hasUnstyledKey());
721
723 args.fContext, std::move(args.fPaint), *args.fShape, *args.fViewMatrix,
724 args.fGammaCorrect, args.fUserStencilSettings);
725 args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
726
727 return true;
728}
729
730} // namespace skgpu::ganesh
731
732#if defined(GR_TEST_UTILS)
733
734GR_DRAW_OP_TEST_DEFINE(SmallPathOp) {
735 SkMatrix viewMatrix = GrTest::TestMatrix(random);
736 bool gammaCorrect = random->nextBool();
737
738 // This path renderer only allows fill styles.
739 GrStyledShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
741 std::move(paint),
742 shape,
743 viewMatrix,
744 gammaCorrect,
745 GrGetRandomStencil(random, context));
746}
747
748#endif // defined(GR_TEST_UTILS)
749
750#endif // SK_ENABLE_OPTIMIZE_SIZE
sk_bzero(glyphs, sizeof(glyphs))
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename)
Definition: GrAuditTrail.h:167
bool GrGenerateDistanceFieldFromPath(unsigned char *distanceField, const SkPath &path, const SkMatrix &drawMatrix, int width, int height, size_t rowBytes)
@ kGammaCorrect_DistanceFieldEffectFlag
@ kWideColor_DistanceFieldEffectFlag
@ kPerspective_DistanceFieldEffectFlag
@ kSimilarity_DistanceFieldEffectFlag
@ kScaleOnly_DistanceFieldEffectFlag
#define DEFINE_OP_CLASS_ID
Definition: GrOp.h:64
GrClampType
Definition: GrTypesPriv.h:228
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
GrLoadOp
Definition: GrTypesPriv.h:155
GrXferBarrierFlags
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
bool SkGenerateDistanceFieldFromA8Image(unsigned char *distanceField, const unsigned char *image, int width, int height, size_t rowBytes)
#define SK_DistanceFieldPad
static constexpr int32_t SK_MaxS32
Definition: SkMath.h:21
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
#define SkScalarInvert(x)
Definition: SkScalar.h:73
#define SkScalarFloorToScalar(x)
Definition: SkScalar.h:30
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarHalf
Definition: SkScalar.h:19
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
#define SkScalarPow(b, e)
Definition: SkScalar.h:43
#define SkScalarCeilToScalar(x)
Definition: SkScalar.h:31
#define SkScalarAbs(x)
Definition: SkScalar.h:39
#define SkScalarLog2(x)
Definition: SkScalar.h:53
GrGeometryProcessor * fGeometryProcessor
sk_sp< const GrBuffer > fIndexBuffer
int fInstancesToFlush
SkMatrix fViewMatrix
SkPMColor4f fColor
int fVertexOffset
sk_sp< const GrBuffer > fVertexBuffer
const GrSurfaceProxy ** fPrimProcProxies
GrStyledShape fShape
static void draw(SkCanvas *canvas, SkRect &target, int x, int y)
Definition: aaclip.cpp:27
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrShaderCaps &caps, const SkPMColor4f &color, bool wideColor, sk_sp< GrColorSpaceXform > colorSpaceXform, const GrSurfaceProxyView *views, int numActiveViews, GrSamplerState p, skgpu::MaskFormat format, const SkMatrix &localMatrix, bool usesW)
static constexpr int kMaxTextures
Definition: GrCaps.h:57
static GrGeometryProcessor * Make(SkArenaAlloc *arena, const GrShaderCaps &caps, const GrSurfaceProxyView *views, int numActiveViews, GrSamplerState params, const SkMatrix &localMatrix, uint32_t flags)
void executeDrawsAndUploadsForMeshDrawOp(const GrOp *op, const SkRect &chainBounds, const GrPipeline *, const GrUserStencilSettings *)
Definition: GrOp.h:70
std::unique_ptr< GrOp > Owner
Definition: GrOp.h:72
const T & cast() const
Definition: GrOp.h:148
static int NumIndicesPerNonAAQuad()
static int NumVertsPerNonAAQuad()
GrProcessorSet::Analysis finalizeProcessors(const GrCaps &caps, const GrAppliedClip *clip, GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage, GrProcessorAnalysisColor *geometryColor)
void visitProxies(const GrVisitProxyFunc &func) const
GrDrawOp::FixedFunctionFlags fixedFunctionFlags() const
bool isCompatible(const GrSimpleMeshDrawOpHelperWithStencil &that, const GrCaps &, const SkRect &thisBounds, const SkRect &thatBounds, bool ignoreAAType=false) const
const GrUserStencilSettings * stencilSettings() const
const GrPipeline * createPipeline(GrOpFlushState *flushState)
static const GrStyle & SimpleFill()
Definition: GrStyle.h:30
void asPath(SkPath *out) const
bool hasUnstyledKey() const
SkRect bounds() const
GrSurfaceProxy * proxy() const
Definition: SkDraw.h:38
static bool CheapEqual(const SkMatrix &a, const SkMatrix &b)
Definition: SkMatrixPriv.h:181
SkMatrix & postTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:281
static constexpr int kMTransY
vertical translation
Definition: SkMatrix.h:358
void mapHomogeneousPoints(SkPoint3 dst[], const SkPoint3 src[], int count) const
Definition: SkMatrix.cpp:1066
SkScalar getTranslateY() const
Definition: SkMatrix.h:452
SkMatrix & set(int index, SkScalar value)
Definition: SkMatrix.h:489
SkMatrix & setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:296
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
bool isScaleTranslate() const
Definition: SkMatrix.h:236
static constexpr int kMTransX
horizontal translation
Definition: SkMatrix.h:355
bool hasPerspective() const
Definition: SkMatrix.h:312
SkScalar get(int index) const
Definition: SkMatrix.h:392
bool isSimilarity(SkScalar tol=SK_ScalarNearlyZero) const
Definition: SkMatrix.cpp:180
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
SkScalar getTranslateX() const
Definition: SkMatrix.h:445
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
Definition: SkPath.h:59
bool setRect(const SkIRect &)
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:550
std::array< uint16_t, 4 > getUVs() const
Definition: AtlasTypes.h:294
void insetSrc(int padding)
Definition: AtlasTypes.h:327
PlotLocator plotLocator() const
Definition: AtlasTypes.h:301
bool isValid() const
Definition: AtlasTypes.h:262
GrDrawOpAtlas::ErrorCode addToAtlas(GrResourceProvider *, GrDeferredUploadTarget *, int width, int height, const void *image, skgpu::AtlasLocator *)
const GrSurfaceProxyView * getViews(int *numActiveProxies)
const Paint & paint
Definition: color_source.cc:38
DlColor color
float SkScalar
Definition: extension.cpp:12
FlutterSemanticsFlag flags
gboolean invert
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t * target
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SkMesh mesh
Definition: SkRecords.h:345
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition: SkRecords.h:208
void Helper(uword arg)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
dst
Definition: cp.py:12
MaskFormat
Definition: AtlasTypes.h:98
@ kA8
1-byte per pixel
int32_t height
int32_t width
const Scalar scale
Definition: SkRect.h:32
SkIRect makeOutset(int32_t dx, int32_t dy) const
Definition: SkRect.h:350
constexpr int32_t height() const
Definition: SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
constexpr int32_t width() const
Definition: SkRect.h:158
static constexpr SkIRect MakeWH(int32_t w, int32_t h)
Definition: SkRect.h:56
int32_t fLeft
smaller x-axis bounds
Definition: SkRect.h:33
static SkImageInfo MakeA8(int width, int height)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
constexpr SkRect makeOffset(float dx, float dy) const
Definition: SkRect.h:965
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
void offset(float dx, float dy)
Definition: SkRect.h:1016
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15
static TriStrip< uint16_t > TriStripFromUVs(const std::array< uint16_t, 4 > &rect)
Definition: BufferWriter.h:221
static TriStrip< float > TriStripFromRect(const SkRect &r)
Definition: BufferWriter.h:217