Flutter Engine
The Flutter Engine
SoftwarePathRenderer.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2012 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
9
27#include "src/gpu/ganesh/SkGr.h"
32
33namespace {
34
35/**
36 * Payload class for use with GrTDeferredProxyUploader. The software path renderer only draws
37 * a single path into the mask texture. This stores all of the information needed by the worker
38 * thread's call to drawShape (see below, in onDrawPath).
39 */
40class SoftwarePathData {
41public:
42 SoftwarePathData(const SkIRect& maskBounds, const SkMatrix& viewMatrix,
43 const GrStyledShape& shape, GrAA aa)
44 : fMaskBounds(maskBounds)
45 , fViewMatrix(viewMatrix)
46 , fShape(shape)
47 , fAA(aa) {}
48
49 const SkIRect& getMaskBounds() const { return fMaskBounds; }
50 const SkMatrix* getViewMatrix() const { return &fViewMatrix; }
51 const GrStyledShape& getShape() const { return fShape; }
52 GrAA getAA() const { return fAA; }
53
54private:
55 SkIRect fMaskBounds;
58 GrAA fAA;
59};
60
62 SkIRect* devBounds) {
63 SkRect shapeBounds = shape.styledBounds();
64 if (shapeBounds.isEmpty()) {
65 return false;
66 }
67 SkRect shapeDevBounds;
68 matrix.mapRect(&shapeDevBounds, shapeBounds);
69 // Even though these are "unclipped" bounds we still clip to the int32_t range.
70 // This is the largest int32_t that is representable exactly as a float. The next 63 larger ints
71 // would round down to this value when cast to a float, but who really cares.
72 // INT32_MIN is exactly representable.
73 static constexpr int32_t kMaxInt = 2147483520;
74 if (!shapeDevBounds.intersect(SkRect::MakeLTRB(INT32_MIN, INT32_MIN, kMaxInt, kMaxInt))) {
75 return false;
76 }
77 // Make sure that the resulting SkIRect can have representable width and height
78 if (SkScalarRoundToInt(shapeDevBounds.width()) > kMaxInt ||
79 SkScalarRoundToInt(shapeDevBounds.height()) > kMaxInt) {
80 return false;
81 }
82 shapeDevBounds.roundOut(devBounds);
83 return true;
84}
85
86GrSurfaceProxyView make_deferred_mask_texture_view(GrRecordingContext* rContext,
87 SkBackingFit fit,
88 SkISize dimensions) {
89 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
90 const GrCaps* caps = rContext->priv().caps();
91
94
96
97 auto proxy = proxyProvider->createProxy(format,
98 dimensions,
100 1,
102 fit,
105 /*label=*/"MakeDeferredMaskTextureView");
106 return {std::move(proxy), kTopLeft_GrSurfaceOrigin, swizzle};
107}
108
109
110} // anonymous namespace
111
112namespace skgpu::ganesh {
113
114////////////////////////////////////////////////////////////////////////////////
115PathRenderer::CanDrawPath SoftwarePathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
116 // Pass on any style that applies. The caller will apply the style if a suitable renderer is
117 // not found and try again with the new GrStyledShape.
118 if (!args.fShape->style().applies() && SkToBool(fProxyProvider) &&
119 (args.fAAType == GrAAType::kCoverage || args.fAAType == GrAAType::kNone)) {
120 // This is the fallback renderer for when a path is too complicated for the GPU ones.
122 }
123 return CanDrawPath::kNo;
124}
125
126////////////////////////////////////////////////////////////////////////////////
127
128// Gets the shape bounds, the clip bounds, and the intersection (if any). Returns false if there
129// is no intersection.
131 const GrClip* clip,
132 const GrStyledShape& shape,
133 const SkMatrix& matrix,
134 SkIRect* unclippedDevShapeBounds,
135 SkIRect* clippedDevShapeBounds,
136 SkIRect* devClipBounds) {
137 // compute bounds as intersection of rt size, clip, and path
138 *devClipBounds = clip ? clip->getConservativeBounds()
139 : SkIRect::MakeWH(sdc->width(), sdc->height());
140
141 if (!get_unclipped_shape_dev_bounds(shape, matrix, unclippedDevShapeBounds)) {
142 *unclippedDevShapeBounds = SkIRect::MakeEmpty();
143 *clippedDevShapeBounds = SkIRect::MakeEmpty();
144 return false;
145 }
146 if (!clippedDevShapeBounds->intersect(*devClipBounds, *unclippedDevShapeBounds)) {
147 *clippedDevShapeBounds = SkIRect::MakeEmpty();
148 return false;
149 }
150 return true;
151}
152
153////////////////////////////////////////////////////////////////////////////////
154
155void SoftwarePathRenderer::DrawNonAARect(SurfaceDrawContext* sdc,
156 GrPaint&& paint,
157 const GrUserStencilSettings& userStencilSettings,
158 const GrClip* clip,
159 const SkMatrix& viewMatrix,
160 const SkRect& rect,
161 const SkMatrix& localMatrix) {
162 sdc->stencilRect(clip, &userStencilSettings, std::move(paint), GrAA::kNo,
163 viewMatrix, rect, &localMatrix);
164}
165
166void SoftwarePathRenderer::DrawAroundInvPath(SurfaceDrawContext* sdc,
167 GrPaint&& paint,
168 const GrUserStencilSettings& userStencilSettings,
169 const GrClip* clip,
170 const SkMatrix& viewMatrix,
171 const SkIRect& devClipBounds,
172 const SkIRect& devPathBounds) {
174 if (!viewMatrix.invert(&invert)) {
175 return;
176 }
177
178 SkRect rect;
179 if (devClipBounds.fTop < devPathBounds.fTop) {
180 rect.setLTRB(SkIntToScalar(devClipBounds.fLeft), SkIntToScalar(devClipBounds.fTop),
181 SkIntToScalar(devClipBounds.fRight), SkIntToScalar(devPathBounds.fTop));
182 DrawNonAARect(sdc, GrPaint::Clone(paint), userStencilSettings, clip,
184 }
185 if (devClipBounds.fLeft < devPathBounds.fLeft) {
186 rect.setLTRB(SkIntToScalar(devClipBounds.fLeft), SkIntToScalar(devPathBounds.fTop),
187 SkIntToScalar(devPathBounds.fLeft), SkIntToScalar(devPathBounds.fBottom));
188 DrawNonAARect(sdc, GrPaint::Clone(paint), userStencilSettings, clip,
190 }
191 if (devClipBounds.fRight > devPathBounds.fRight) {
192 rect.setLTRB(SkIntToScalar(devPathBounds.fRight), SkIntToScalar(devPathBounds.fTop),
193 SkIntToScalar(devClipBounds.fRight), SkIntToScalar(devPathBounds.fBottom));
194 DrawNonAARect(sdc, GrPaint::Clone(paint), userStencilSettings, clip,
196 }
197 if (devClipBounds.fBottom > devPathBounds.fBottom) {
198 rect.setLTRB(SkIntToScalar(devClipBounds.fLeft), SkIntToScalar(devPathBounds.fBottom),
199 SkIntToScalar(devClipBounds.fRight), SkIntToScalar(devClipBounds.fBottom));
200 DrawNonAARect(sdc, std::move(paint), userStencilSettings, clip,
202 }
203}
204
205void SoftwarePathRenderer::DrawToTargetWithShapeMask(
208 GrPaint&& paint,
209 const GrUserStencilSettings& userStencilSettings,
210 const GrClip* clip,
211 const SkMatrix& viewMatrix,
212 const SkIPoint& textureOriginInDeviceSpace,
213 const SkIRect& deviceSpaceRectToDraw) {
215 if (!viewMatrix.invert(&invert)) {
216 return;
217 }
218
219 view.concatSwizzle(skgpu::Swizzle("aaaa"));
220
221 SkRect dstRect = SkRect::Make(deviceSpaceRectToDraw);
222
223 // We use device coords to compute the texture coordinates. We take the device coords and apply
224 // a translation so that the top-left of the device bounds maps to 0,0, and then a scaling
225 // matrix to normalized coords.
226 SkMatrix maskMatrix = SkMatrix::Translate(SkIntToScalar(-textureOriginInDeviceSpace.fX),
227 SkIntToScalar(-textureOriginInDeviceSpace.fY));
228 maskMatrix.preConcat(viewMatrix);
229
230 paint.setCoverageFragmentProcessor(GrTextureEffect::Make(
231 std::move(view), kPremul_SkAlphaType, maskMatrix, GrSamplerState::Filter::kNearest));
232 DrawNonAARect(sdc, std::move(paint), userStencilSettings, clip, SkMatrix::I(),
233 dstRect, invert);
234}
235
236////////////////////////////////////////////////////////////////////////////////
237// return true on success; false on failure
238bool SoftwarePathRenderer::onDrawPath(const DrawPathArgs& args) {
239 GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
240 "SoftwarePathRenderer::onDrawPath");
241
242 if (!fProxyProvider) {
243 return false;
244 }
245
246 SkASSERT(!args.fShape->style().applies());
247 // We really need to know if the shape will be inverse filled or not
248 // If the path is hairline, ignore inverse fill.
249 bool inverseFilled = args.fShape->inverseFilled() &&
250 !GrIsStrokeHairlineOrEquivalent(args.fShape->style(),
251 *args.fViewMatrix, nullptr);
252
253 SkIRect unclippedDevShapeBounds, clippedDevShapeBounds, devClipBounds;
254 // To prevent overloading the cache with entries during animations we limit the cache of masks
255 // to cases where the matrix preserves axis alignment.
256 bool useCache = fAllowCaching && !inverseFilled && args.fViewMatrix->preservesAxisAlignment() &&
257 args.fShape->hasUnstyledKey() && (GrAAType::kCoverage == args.fAAType);
258
259 if (!GetShapeAndClipBounds(args.fSurfaceDrawContext,
260 args.fClip, *args.fShape,
261 *args.fViewMatrix, &unclippedDevShapeBounds,
262 &clippedDevShapeBounds,
263 &devClipBounds)) {
264 if (inverseFilled) {
265 DrawAroundInvPath(args.fSurfaceDrawContext, std::move(args.fPaint),
266 *args.fUserStencilSettings, args.fClip, *args.fViewMatrix,
267 devClipBounds, unclippedDevShapeBounds);
268 }
269 return true;
270 }
271
272 const SkIRect* boundsForMask = &clippedDevShapeBounds;
273 if (useCache) {
274 // Use the cache only if >50% of the path is visible.
275 int unclippedWidth = unclippedDevShapeBounds.width();
276 int unclippedHeight = unclippedDevShapeBounds.height();
277 int64_t unclippedArea = sk_64_mul(unclippedWidth, unclippedHeight);
278 int64_t clippedArea = sk_64_mul(clippedDevShapeBounds.width(),
279 clippedDevShapeBounds.height());
280 int maxTextureSize = args.fSurfaceDrawContext->caps()->maxTextureSize();
281 if (unclippedArea > 2 * clippedArea || unclippedWidth > maxTextureSize ||
282 unclippedHeight > maxTextureSize) {
283 useCache = false;
284 } else {
285 boundsForMask = &unclippedDevShapeBounds;
286 }
287 }
288
289 skgpu::UniqueKey maskKey;
290 if (useCache) {
291 // We require the upper left 2x2 of the matrix to match exactly for a cache hit.
292 SkScalar sx = args.fViewMatrix->get(SkMatrix::kMScaleX);
293 SkScalar sy = args.fViewMatrix->get(SkMatrix::kMScaleY);
294 SkScalar kx = args.fViewMatrix->get(SkMatrix::kMSkewX);
295 SkScalar ky = args.fViewMatrix->get(SkMatrix::kMSkewY);
297 skgpu::UniqueKey::Builder builder(&maskKey, kDomain, 7 + args.fShape->unstyledKeySize(),
298 "SW Path Mask");
299 builder[0] = boundsForMask->width();
300 builder[1] = boundsForMask->height();
301
302#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
303 // Fractional translate does not affect caching on Android. This is done for better cache
304 // hit ratio and speed, but it is matching HWUI behavior, which doesn't consider the matrix
305 // at all when caching paths.
306 SkFixed fracX = 0;
307 SkFixed fracY = 0;
308#else
309 SkScalar tx = args.fViewMatrix->get(SkMatrix::kMTransX);
310 SkScalar ty = args.fViewMatrix->get(SkMatrix::kMTransY);
311 // Allow 8 bits each in x and y of subpixel positioning.
312 SkFixed fracX = SkScalarToFixed(SkScalarFraction(tx)) & 0x0000FF00;
313 SkFixed fracY = SkScalarToFixed(SkScalarFraction(ty)) & 0x0000FF00;
314#endif
315 builder[2] = SkFloat2Bits(sx);
316 builder[3] = SkFloat2Bits(sy);
317 builder[4] = SkFloat2Bits(kx);
318 builder[5] = SkFloat2Bits(ky);
319 // Distinguish between hairline and filled paths. For hairlines, we also need to include
320 // the cap. (SW grows hairlines by 0.5 pixel with round and square caps). Note that
321 // stroke-and-fill of hairlines is turned into pure fill by SkStrokeRec, so this covers
322 // all cases we might see.
323 uint32_t styleBits = args.fShape->style().isSimpleHairline() ?
324 ((args.fShape->style().strokeRec().getCap() << 1) | 1) : 0;
325 builder[6] = fracX | (fracY >> 8) | (styleBits << 16);
326 args.fShape->writeUnstyledKey(&builder[7]);
327 }
328
330 if (useCache) {
331 sk_sp<GrTextureProxy> proxy = fProxyProvider->findOrCreateProxyByUniqueKey(maskKey);
332 if (proxy) {
333 skgpu::Swizzle swizzle = args.fSurfaceDrawContext->caps()->getReadSwizzle(
335 view = {std::move(proxy), kTopLeft_GrSurfaceOrigin, swizzle};
336 args.fContext->priv().stats()->incNumPathMasksCacheHits();
337 }
338 }
339 if (!view) {
341 GrAA aa = GrAA(GrAAType::kCoverage == args.fAAType);
342
343 SkTaskGroup* taskGroup = nullptr;
344 if (auto direct = args.fContext->asDirectContext()) {
345 taskGroup = direct->priv().getTaskGroup();
346 }
347
348 if (taskGroup) {
349 view = make_deferred_mask_texture_view(args.fContext, fit, boundsForMask->size());
350 if (!view) {
351 return false;
352 }
353
354 auto uploader = std::make_unique<GrTDeferredProxyUploader<SoftwarePathData>>(
355 *boundsForMask, *args.fViewMatrix, *args.fShape, aa);
356 GrTDeferredProxyUploader<SoftwarePathData>* uploaderRaw = uploader.get();
357
358 auto drawAndUploadMask = [uploaderRaw] {
359 TRACE_EVENT0("skia.gpu", "Threaded SW Mask Render");
360 GrSWMaskHelper helper(uploaderRaw->getPixels());
361 if (helper.init(uploaderRaw->data().getMaskBounds())) {
362 helper.drawShape(uploaderRaw->data().getShape(),
363 *uploaderRaw->data().getViewMatrix(),
364 uploaderRaw->data().getAA(), 0xFF);
365 } else {
366 SkDEBUGFAIL("Unable to allocate SW mask.");
367 }
368 uploaderRaw->signalAndFreeData();
369 };
370 taskGroup->add(std::move(drawAndUploadMask));
371 view.asTextureProxy()->texPriv().setDeferredUploader(std::move(uploader));
372 } else {
373 GrSWMaskHelper helper;
374 if (!helper.init(*boundsForMask)) {
375 return false;
376 }
377 helper.drawShape(*args.fShape, *args.fViewMatrix, aa, 0xFF);
378 view = helper.toTextureView(args.fContext, fit);
379 }
380
381 if (!view) {
382 return false;
383 }
384 if (useCache) {
386
387 // We will add an invalidator to the path so that if the path goes away we will
388 // delete or recycle the mask texture.
389 auto listener = GrMakeUniqueKeyInvalidationListener(&maskKey,
390 args.fContext->priv().contextID());
391 fProxyProvider->assignUniqueKeyToProxy(maskKey, view.asTextureProxy());
392 args.fShape->addGenIDChangeListener(std::move(listener));
393 }
394
395 args.fContext->priv().stats()->incNumPathMasksGenerated();
396 }
397 SkASSERT(view);
398 if (inverseFilled) {
399 DrawAroundInvPath(args.fSurfaceDrawContext, GrPaint::Clone(args.fPaint),
400 *args.fUserStencilSettings, args.fClip, *args.fViewMatrix, devClipBounds,
401 unclippedDevShapeBounds);
402 }
403 DrawToTargetWithShapeMask(std::move(view), args.fSurfaceDrawContext, std::move(args.fPaint),
404 *args.fUserStencilSettings, args.fClip, *args.fViewMatrix,
405 SkIPoint{boundsForMask->fLeft, boundsForMask->fTop}, *boundsForMask);
406
407 return true;
408}
409
410} // namespace skgpu::ganesh
SkMatrix fViewMatrix
#define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename)
Definition: GrAuditTrail.h:167
GrAA
Definition: GrTypesPriv.h:173
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
bool GrIsStrokeHairlineOrEquivalent(const GrStyle &style, const SkMatrix &matrix, SkScalar *outCoverage)
Definition: GrUtil.cpp:65
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkDEBUGFAIL(message)
Definition: SkAssert.h:118
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBackingFit
Definition: SkBackingFit.h:16
int32_t SkFixed
Definition: SkFixed.h:25
#define SkScalarToFixed(x)
Definition: SkFixed.h:125
static uint32_t SkFloat2Bits(float value)
Definition: SkFloatBits.h:41
sk_sp< SkIDChangeListener > GrMakeUniqueKeyInvalidationListener(skgpu::UniqueKey *key, uint32_t contextID)
Definition: SkGr.cpp:75
static int64_t sk_64_mul(int64_t a, int64_t b)
Definition: SkMath.h:33
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
#define SkIntToScalar(x)
Definition: SkScalar.h:57
static SkScalar SkScalarFraction(SkScalar x)
Definition: SkScalar.h:67
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
GrStyledShape fShape
const GrCaps * caps() const
Definition: GrCaps.h:57
GrBackendFormat getDefaultBackendFormat(GrColorType, GrRenderable) const
Definition: GrCaps.cpp:400
skgpu::Swizzle getReadSwizzle(const GrBackendFormat &format, GrColorType colorType) const
Definition: GrCaps.cpp:443
Definition: GrClip.h:29
static GrPaint Clone(const GrPaint &src)
Definition: GrPaint.h:45
sk_sp< GrTextureProxy > createProxy(const GrBackendFormat &, SkISize dimensions, GrRenderable, int renderTargetSampleCnt, skgpu::Mipmapped, SkBackingFit, skgpu::Budgeted, GrProtected, std::string_view label, GrInternalSurfaceFlags=GrInternalSurfaceFlags::kNone, UseAllocator useAllocator=UseAllocator::kYes)
bool assignUniqueKeyToProxy(const skgpu::UniqueKey &, GrTextureProxy *)
sk_sp< GrTextureProxy > findOrCreateProxyByUniqueKey(const skgpu::UniqueKey &, UseAllocator=UseAllocator::kYes)
GrProxyProvider * proxyProvider()
GrRecordingContextPriv priv()
void drawShape(const GrStyledShape &, const SkMatrix &matrix, GrAA, uint8_t alpha)
bool init(const SkIRect &resultBounds)
GrSurfaceProxyView toTextureView(GrRecordingContext *, SkBackingFit fit)
SkRect styledBounds() const
void concatSwizzle(skgpu::Swizzle swizzle)
GrTextureProxy * asTextureProxy() const
GrSurfaceOrigin origin() const
const GrBackendFormat & backendFormat() const
static std::unique_ptr< GrFragmentProcessor > Make(GrSurfaceProxyView, SkAlphaType, const SkMatrix &=SkMatrix::I(), GrSamplerState::Filter=GrSamplerState::Filter::kNearest, GrSamplerState::MipmapMode mipmapMode=GrSamplerState::MipmapMode::kNone)
void setDeferredUploader(std::unique_ptr< GrDeferredProxyUploader >)
GrTextureProxyPriv texPriv()
static constexpr int kMScaleX
horizontal scale factor
Definition: SkMatrix.h:353
static constexpr int kMTransY
vertical translation
Definition: SkMatrix.h:358
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
SkMatrix & preConcat(const SkMatrix &other)
Definition: SkMatrix.cpp:674
static constexpr int kMTransX
horizontal translation
Definition: SkMatrix.h:355
static constexpr int kMSkewY
vertical skew factor
Definition: SkMatrix.h:356
static constexpr int kMScaleY
vertical scale factor
Definition: SkMatrix.h:357
static constexpr int kMSkewX
horizontal skew factor
Definition: SkMatrix.h:354
void add(std::function< void(void)> fn)
Definition: SkTaskGroup.cpp:16
static Domain GenerateDomain()
Definition: ResourceKey.cpp:27
static bool GetShapeAndClipBounds(SurfaceDrawContext *, const GrClip *, const GrStyledShape &, const SkMatrix &viewMatrix, SkIRect *unclippedDevShapeBounds, SkIRect *clippedDevShapeBounds, SkIRect *devClipBounds)
void stencilRect(const GrClip *clip, const GrUserStencilSettings *ss, GrPaint &&paint, GrAA doStencilMSAA, const SkMatrix &viewMatrix, const SkRect &rect, const SkMatrix *localMatrix=nullptr)
const Paint & paint
Definition: color_source.cc:38
float SkScalar
Definition: extension.cpp:12
gboolean invert
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t uint32_t * format
static bool get_unclipped_shape_dev_bounds(const GrStyledShape &shape, const SkMatrix &matrix, SkIRect *devBounds)
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
constexpr int kMaxInt
Definition: globals.h:490
int32_t fX
x-axis value
Definition: SkPoint_impl.h:29
int32_t fY
y-axis value
Definition: SkPoint_impl.h:30
Definition: SkRect.h:32
bool intersect(const SkIRect &r)
Definition: SkRect.h:513
int32_t fBottom
larger y-axis bounds
Definition: SkRect.h:36
constexpr SkISize size() const
Definition: SkRect.h:172
constexpr int32_t height() const
Definition: SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition: SkRect.h:34
static constexpr SkIRect MakeEmpty()
Definition: SkRect.h:45
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
int32_t fRight
larger x-axis bounds
Definition: SkRect.h:35
Definition: SkSize.h:16
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
bool isEmpty() const
Definition: SkRect.h:693
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
#define TRACE_EVENT0(category_group, name)
Definition: trace_event.h:131