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