xref: /aosp_15_r20/external/skia/src/gpu/ganesh/Device.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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/Device.h"
8 
9 #include "include/core/SkAlphaType.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkClipOp.h"
14 #include "include/core/SkColor.h"
15 #include "include/core/SkColorSpace.h"
16 #include "include/core/SkColorType.h"
17 #include "include/core/SkDrawable.h"
18 #include "include/core/SkImage.h"
19 #include "include/core/SkImageInfo.h"
20 #include "include/core/SkM44.h"
21 #include "include/core/SkMatrix.h"
22 #include "include/core/SkMesh.h"
23 #include "include/core/SkPaint.h"
24 #include "include/core/SkPath.h"
25 #include "include/core/SkPathTypes.h"
26 #include "include/core/SkPixmap.h"
27 #include "include/core/SkPoint.h"
28 #include "include/core/SkRRect.h"
29 #include "include/core/SkRSXform.h"
30 #include "include/core/SkRect.h"
31 #include "include/core/SkRefCnt.h"
32 #include "include/core/SkRegion.h"
33 #include "include/core/SkScalar.h"
34 #include "include/core/SkSize.h"
35 #include "include/core/SkSpan.h"
36 #include "include/core/SkStrokeRec.h"
37 #include "include/core/SkSurface.h"
38 #include "include/core/SkSurfaceProps.h"
39 #include "include/core/SkVertices.h"
40 #include "include/effects/SkRuntimeEffect.h"
41 #include "include/gpu/GpuTypes.h"
42 #include "include/gpu/ganesh/GrBackendSurface.h"
43 #include "include/gpu/ganesh/GrContextOptions.h"
44 #include "include/gpu/ganesh/GrDirectContext.h"
45 #include "include/gpu/ganesh/GrRecordingContext.h"
46 #include "include/gpu/ganesh/GrTypes.h"
47 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
48 #include "include/private/SkColorData.h"
49 #include "include/private/base/SingleOwner.h"
50 #include "include/private/base/SkAssert.h"
51 #include "include/private/base/SkDebug.h"
52 #include "include/private/base/SkTArray.h"
53 #include "include/private/base/SkTo.h"
54 #include "include/private/chromium/Slug.h"  // IWYU pragma: keep
55 #include "include/private/gpu/ganesh/GrTypesPriv.h"
56 #include "src/base/SkTLazy.h"
57 #include "src/core/SkDevice.h"
58 #include "src/core/SkDrawBase.h"
59 #include "src/core/SkImageFilterTypes.h"  // IWYU pragma: keep
60 #include "src/core/SkImageInfoPriv.h"
61 #include "src/core/SkLatticeIter.h"
62 #include "src/core/SkMeshPriv.h"
63 #include "src/core/SkRasterClip.h"
64 #include "src/core/SkSpecialImage.h"
65 #include "src/core/SkStrikeCache.h"
66 #include "src/core/SkTraceEvent.h"
67 #include "src/core/SkVerticesPriv.h"
68 #include "src/gpu/SkBackingFit.h"
69 #include "src/gpu/Swizzle.h"
70 #include "src/gpu/TiledTextureUtils.h"
71 #include "src/gpu/ganesh/ClipStack.h"
72 #include "src/gpu/ganesh/GrAuditTrail.h"
73 #include "src/gpu/ganesh/GrBlurUtils.h"
74 #include "src/gpu/ganesh/GrCaps.h"
75 #include "src/gpu/ganesh/GrColorInfo.h"
76 #include "src/gpu/ganesh/GrColorSpaceXform.h"
77 #include "src/gpu/ganesh/GrFPArgs.h"
78 #include "src/gpu/ganesh/GrFragmentProcessor.h"
79 #include "src/gpu/ganesh/GrFragmentProcessors.h"
80 #include "src/gpu/ganesh/GrImageInfo.h"
81 #include "src/gpu/ganesh/GrPaint.h"
82 #include "src/gpu/ganesh/GrProxyProvider.h"
83 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
84 #include "src/gpu/ganesh/GrRenderTargetProxy.h"
85 #include "src/gpu/ganesh/GrStyle.h"
86 #include "src/gpu/ganesh/GrSurfaceProxy.h"
87 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
88 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
89 #include "src/gpu/ganesh/GrTextureProxy.h"
90 #include "src/gpu/ganesh/GrTracing.h"
91 #include "src/gpu/ganesh/GrUserStencilSettings.h"
92 #include "src/gpu/ganesh/GrXferProcessor.h"
93 #include "src/gpu/ganesh/SkGr.h"
94 #include "src/gpu/ganesh/SurfaceContext.h"
95 #include "src/gpu/ganesh/SurfaceDrawContext.h"
96 #include "src/gpu/ganesh/SurfaceFillContext.h"
97 #include "src/gpu/ganesh/effects/GrDisableColorXP.h"
98 #include "src/gpu/ganesh/effects/GrRRectEffect.h"
99 #include "src/gpu/ganesh/geometry/GrShape.h"
100 #include "src/gpu/ganesh/geometry/GrStyledShape.h"
101 #include "src/gpu/ganesh/image/GrImageUtils.h"
102 #include "src/gpu/ganesh/image/SkSpecialImage_Ganesh.h"
103 #include "src/text/GlyphRun.h"
104 #include "src/text/gpu/SlugImpl.h"
105 #include "src/text/gpu/SubRunContainer.h"
106 
107 #include <atomic>
108 #include <cstddef>
109 #include <cstdint>
110 #include <functional>
111 #include <memory>
112 #include <tuple>
113 #include <utility>
114 
115 class GrBackendSemaphore;
116 struct GrShaderCaps;
117 struct SkDrawShadowRec;
118 
119 using namespace skia_private;
120 
121 #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(fContext->priv().singleOwner())
122 
123 #if defined(GPU_TEST_UTILS)
124 // GrContextOptions::fMaxTextureSizeOverride exists but doesn't allow for changing the
125 // maxTextureSize on the fly.
126 int gOverrideMaxTextureSizeGanesh = 0;
127 // Allows tests to check how many tiles were drawn on the most recent call to
128 // Device::drawAsTiledImageRect. This is an atomic because we can write to it from
129 // multiple threads during "normal" operations. However, the tests that actually
130 // read from it are done single-threaded.
131 std::atomic<int> gNumTilesDrawnGanesh{0};
132 #endif
133 
134 ///////////////////////////////////////////////////////////////////////////////
135 
136 namespace {
137 
force_aa_clip(const skgpu::ganesh::SurfaceDrawContext * sdc)138 bool force_aa_clip(const skgpu::ganesh::SurfaceDrawContext* sdc) {
139     return sdc->numSamples() > 1 || sdc->alwaysAntialias();
140 }
141 
point_mode_to_primitive_type(SkCanvas::PointMode mode)142 inline GrPrimitiveType point_mode_to_primitive_type(SkCanvas::PointMode mode) {
143     switch (mode) {
144         case SkCanvas::kPoints_PointMode:
145             return GrPrimitiveType::kPoints;
146         case SkCanvas::kLines_PointMode:
147             return GrPrimitiveType::kLines;
148         case SkCanvas::kPolygon_PointMode:
149             return GrPrimitiveType::kLineStrip;
150     }
151     SK_ABORT("Unexpected mode");
152 }
153 
make_inverse_rrect_fp(const SkMatrix & viewMatrix,const SkRRect & rrect,GrAA aa,const GrShaderCaps & shaderCaps)154 std::unique_ptr<GrFragmentProcessor> make_inverse_rrect_fp(const SkMatrix& viewMatrix,
155                                                            const SkRRect& rrect, GrAA aa,
156                                                            const GrShaderCaps& shaderCaps) {
157     SkTCopyOnFirstWrite<SkRRect> devRRect(rrect);
158     if (viewMatrix.isIdentity() || rrect.transform(viewMatrix, devRRect.writable())) {
159         auto edgeType = (aa == GrAA::kYes) ? GrClipEdgeType::kInverseFillAA
160                                            : GrClipEdgeType::kInverseFillBW;
161         auto [success, fp] = GrRRectEffect::Make(/*inputFP=*/nullptr, edgeType, *devRRect,
162                                                  shaderCaps);
163         return (success) ? std::move(fp) : nullptr;
164     }
165     return nullptr;
166 }
167 
init_vertices_paint(GrRecordingContext * rContext,const GrColorInfo & colorInfo,const SkPaint & skPaint,const SkMatrix & ctm,SkBlender * blender,bool hasColors,const SkSurfaceProps & props,GrPaint * grPaint)168 bool init_vertices_paint(GrRecordingContext* rContext,
169                          const GrColorInfo& colorInfo,
170                          const SkPaint& skPaint,
171                          const SkMatrix& ctm,
172                          SkBlender* blender,
173                          bool hasColors,
174                          const SkSurfaceProps& props,
175                          GrPaint* grPaint) {
176     if (hasColors) {
177         return SkPaintToGrPaintWithBlend(rContext,
178                                          colorInfo,
179                                          skPaint,
180                                          ctm,
181                                          blender,
182                                          props,
183                                          grPaint);
184     } else {
185         return SkPaintToGrPaint(rContext, colorInfo, skPaint, ctm, props, grPaint);
186     }
187 }
188 
init_mesh_child_effects(GrRecordingContext * rContext,const GrColorInfo & colorInfo,const SkSurfaceProps & surfaceProps,const SkMesh & mesh,TArray<std::unique_ptr<GrFragmentProcessor>> * meshChildFPs)189 bool init_mesh_child_effects(GrRecordingContext* rContext,
190                              const GrColorInfo& colorInfo,
191                              const SkSurfaceProps& surfaceProps,
192                              const SkMesh& mesh,
193                              TArray<std::unique_ptr<GrFragmentProcessor>>* meshChildFPs) {
194     // We use `Scope::kRuntimeEffect` here to ensure that mesh shaders get the same "raw" sampling
195     // behavior from alpha-only image shaders as a Runtime Effect would, rather than the unexpected
196     // tinting-by-input-color.
197     GrFPArgs fpArgs(rContext, &colorInfo, surfaceProps, GrFPArgs::Scope::kRuntimeEffect);
198 
199     for (const SkRuntimeEffect::ChildPtr& child : mesh.children()) {
200         auto [success, childFP] = GrFragmentProcessors::MakeChildFP(child, fpArgs);
201         if (!success) {
202             return false;
203         }
204         meshChildFPs->push_back(std::move(childFP));
205     }
206     return true;
207 }
208 
209 } // anonymous namespace
210 
211 namespace skgpu::ganesh {
212 
Make(GrRecordingContext * rContext,GrColorType colorType,sk_sp<GrSurfaceProxy> proxy,sk_sp<SkColorSpace> colorSpace,GrSurfaceOrigin origin,const SkSurfaceProps & surfaceProps,InitContents init)213 sk_sp<Device> Device::Make(GrRecordingContext* rContext,
214                            GrColorType colorType,
215                            sk_sp<GrSurfaceProxy> proxy,
216                            sk_sp<SkColorSpace> colorSpace,
217                            GrSurfaceOrigin origin,
218                            const SkSurfaceProps& surfaceProps,
219                            InitContents init) {
220     auto sdc = SurfaceDrawContext::Make(rContext,
221                                         colorType,
222                                         std::move(proxy),
223                                         std::move(colorSpace),
224                                         origin,
225                                         surfaceProps);
226 
227     return Device::Make(std::move(sdc), kPremul_SkAlphaType, init);
228 }
229 
MakeInfo(SurfaceContext * sc,DeviceFlags flags)230 SkImageInfo Device::MakeInfo(SurfaceContext* sc, DeviceFlags flags) {
231     SkColorType colorType = GrColorTypeToSkColorType(sc->colorInfo().colorType());
232     return SkImageInfo::Make(sc->width(), sc->height(), colorType,
233                              flags & DeviceFlags::kIsOpaque ? kOpaque_SkAlphaType
234                                                             : kPremul_SkAlphaType,
235                              sc->colorInfo().refColorSpace());
236 }
237 
238 
239 /** Checks that the alpha type is legal and gets constructor flags. Returns false if device creation
240     should fail. */
CheckAlphaTypeAndGetFlags(SkAlphaType alphaType,InitContents init,DeviceFlags * flags)241 bool Device::CheckAlphaTypeAndGetFlags(SkAlphaType alphaType,
242                                        InitContents init,
243                                        DeviceFlags* flags) {
244     *flags = DeviceFlags::kNone;
245     switch (alphaType) {
246         case kPremul_SkAlphaType:
247             break;
248         case kOpaque_SkAlphaType:
249             *flags |= DeviceFlags::kIsOpaque;
250             break;
251         default: // If it is unpremul or unknown don't try to render
252             return false;
253     }
254     if (InitContents::kClear == init) {
255         *flags |= DeviceFlags::kNeedClear;
256     }
257     return true;
258 }
259 
Make(std::unique_ptr<SurfaceDrawContext> sdc,SkAlphaType alphaType,InitContents init)260 sk_sp<Device> Device::Make(std::unique_ptr<SurfaceDrawContext> sdc,
261                            SkAlphaType alphaType,
262                            InitContents init) {
263     if (!sdc) {
264         return nullptr;
265     }
266 
267     GrRecordingContext* rContext = sdc->recordingContext();
268     if (rContext->abandoned()) {
269         return nullptr;
270     }
271 
272     SkColorType ct = GrColorTypeToSkColorType(sdc->colorInfo().colorType());
273 
274     DeviceFlags flags;
275     if (!rContext->colorTypeSupportedAsSurface(ct) ||
276         !CheckAlphaTypeAndGetFlags(alphaType, init, &flags)) {
277         return nullptr;
278     }
279     return sk_sp<Device>(new Device(std::move(sdc), flags));
280 }
281 
Make(GrRecordingContext * rContext,skgpu::Budgeted budgeted,const SkImageInfo & ii,SkBackingFit fit,int sampleCount,skgpu::Mipmapped mipmapped,GrProtected isProtected,GrSurfaceOrigin origin,const SkSurfaceProps & props,InitContents init)282 sk_sp<Device> Device::Make(GrRecordingContext* rContext,
283                            skgpu::Budgeted budgeted,
284                            const SkImageInfo& ii,
285                            SkBackingFit fit,
286                            int sampleCount,
287                            skgpu::Mipmapped mipmapped,
288                            GrProtected isProtected,
289                            GrSurfaceOrigin origin,
290                            const SkSurfaceProps& props,
291                            InitContents init) {
292     if (!rContext) {
293         return nullptr;
294     }
295 
296     auto sdc = SurfaceDrawContext::Make(rContext,
297                                         SkColorTypeToGrColorType(ii.colorType()),
298                                         ii.refColorSpace(),
299                                         fit,
300                                         ii.dimensions(),
301                                         props,
302                                         /*label=*/"MakeDevice",
303                                         sampleCount,
304                                         mipmapped,
305                                         isProtected,
306                                         origin,
307                                         budgeted);
308 
309     return Device::Make(std::move(sdc), ii.alphaType(), init);
310 }
311 
Device(std::unique_ptr<SurfaceDrawContext> sdc,DeviceFlags flags)312 Device::Device(std::unique_ptr<SurfaceDrawContext> sdc, DeviceFlags flags)
313         : SkDevice(MakeInfo(sdc.get(), flags), sdc->surfaceProps())
314         , fContext(sk_ref_sp(sdc->recordingContext()))
315         , fSubRunControl(sdc->recordingContext()->priv().getSubRunControl(
316                          sdc->surfaceProps().isUseDeviceIndependentFonts()))
317         , fSurfaceDrawContext(std::move(sdc))
318         , fClip(SkIRect::MakeSize(fSurfaceDrawContext->dimensions()),
319                 &this->localToDevice(),
320                 force_aa_clip(fSurfaceDrawContext.get())) {
321     if (flags & DeviceFlags::kNeedClear) {
322         this->clearAll();
323     }
324 }
325 
326 Device::~Device() = default;
327 
328 ///////////////////////////////////////////////////////////////////////////////
329 
onReadPixels(const SkPixmap & pm,int x,int y)330 bool Device::onReadPixels(const SkPixmap& pm, int x, int y) {
331     ASSERT_SINGLE_OWNER
332 
333     // Context TODO: Elevate direct context requirement to public API
334     auto dContext = fContext->asDirectContext();
335     if (!dContext || !SkImageInfoValidConversion(pm.info(), this->imageInfo())) {
336         return false;
337     }
338 
339     return fSurfaceDrawContext->readPixels(dContext, pm, {x, y});
340 }
341 
onWritePixels(const SkPixmap & pm,int x,int y)342 bool Device::onWritePixels(const SkPixmap& pm, int x, int y) {
343     ASSERT_SINGLE_OWNER
344 
345     // Context TODO: Elevate direct context requirement to public API
346     auto dContext = fContext->asDirectContext();
347     if (!dContext || !SkImageInfoValidConversion(this->imageInfo(), pm.info())) {
348         return false;
349     }
350 
351     return fSurfaceDrawContext->writePixels(dContext, pm, {x, y});
352 }
353 
onAccessPixels(SkPixmap * pmap)354 bool Device::onAccessPixels(SkPixmap* pmap) {
355     ASSERT_SINGLE_OWNER
356     return false;
357 }
358 
surfaceDrawContext()359 SurfaceDrawContext* Device::surfaceDrawContext() {
360     ASSERT_SINGLE_OWNER
361     return fSurfaceDrawContext.get();
362 }
363 
surfaceDrawContext() const364 const SurfaceDrawContext* Device::surfaceDrawContext() const {
365     ASSERT_SINGLE_OWNER
366     return fSurfaceDrawContext.get();
367 }
368 
surfaceFillContext()369 SurfaceFillContext* Device::surfaceFillContext() {
370     ASSERT_SINGLE_OWNER
371     return fSurfaceDrawContext.get();
372 }
373 
clearAll()374 void Device::clearAll() {
375     ASSERT_SINGLE_OWNER
376     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "clearAll", fContext.get());
377 
378     SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
379     fSurfaceDrawContext->clearAtLeast(rect, SK_PMColor4fTRANSPARENT);
380 }
381 
382 ///////////////////////////////////////////////////////////////////////////////
383 
clipPath(const SkPath & path,SkClipOp op,bool aa)384 void Device::clipPath(const SkPath& path, SkClipOp op, bool aa) {
385 #if defined(GPU_TEST_UTILS)
386     if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) {
387         this->clipPath(SkPath(path).setIsVolatile(true), op, aa);
388         return;
389     }
390 #endif
391     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
392     fClip.clipPath(this->localToDevice(), path, GrAA(aa), op);
393 }
394 
clipRegion(const SkRegion & globalRgn,SkClipOp op)395 void Device::clipRegion(const SkRegion& globalRgn, SkClipOp op) {
396     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
397 
398     // Regions don't actually need AA, but in DMSAA mode every clip element is antialiased.
399     GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias());
400 
401     if (globalRgn.isEmpty()) {
402         fClip.clipRect(SkMatrix::I(), SkRect::MakeEmpty(), aa, op);
403     } else if (globalRgn.isRect()) {
404         fClip.clipRect(this->globalToDevice().asM33(), SkRect::Make(globalRgn.getBounds()), aa, op);
405     } else {
406         SkPath path;
407         globalRgn.getBoundaryPath(&path);
408         fClip.clipPath(this->globalToDevice().asM33(), path, aa, op);
409     }
410 }
411 
android_utils_clipAsRgn(SkRegion * region) const412 void Device::android_utils_clipAsRgn(SkRegion* region) const {
413     SkIRect bounds = fClip.getConservativeBounds();
414     // Assume wide open and then perform intersect/difference operations reducing the region
415     region->setRect(bounds);
416     const SkRegion deviceBounds(bounds);
417     for (const ClipStack::Element& e : fClip) {
418         SkRegion tmp;
419         if (e.fShape.isRect() && e.fLocalToDevice.isIdentity()) {
420             tmp.setRect(e.fShape.rect().roundOut());
421         } else {
422             SkPath tmpPath;
423             e.fShape.asPath(&tmpPath);
424             tmpPath.transform(e.fLocalToDevice);
425             tmp.setPath(tmpPath, deviceBounds);
426         }
427 
428         region->op(tmp, (SkRegion::Op) e.fOp);
429     }
430 }
431 
isClipAntiAliased() const432 bool Device::isClipAntiAliased() const {
433     for (const ClipStack::Element& e : fClip) {
434         if (e.fAA == GrAA::kYes) {
435             return true;
436         }
437         SkASSERT(!fSurfaceDrawContext->alwaysAntialias());
438     }
439     return false;
440 }
441 
442 ///////////////////////////////////////////////////////////////////////////////
443 
drawPaint(const SkPaint & paint)444 void Device::drawPaint(const SkPaint& paint) {
445     ASSERT_SINGLE_OWNER
446     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPaint", fContext.get());
447 
448     GrPaint grPaint;
449     if (!SkPaintToGrPaint(this->recordingContext(),
450                           fSurfaceDrawContext->colorInfo(),
451                           paint,
452                           this->localToDevice(),
453                           fSurfaceDrawContext->surfaceProps(),
454                           &grPaint)) {
455         return;
456     }
457 
458     fSurfaceDrawContext->drawPaint(this->clip(), std::move(grPaint), this->localToDevice());
459 }
460 
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)461 void Device::drawPoints(SkCanvas::PointMode mode,
462                         size_t count,
463                         const SkPoint pts[],
464                         const SkPaint& paint) {
465     ASSERT_SINGLE_OWNER
466     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPoints", fContext.get());
467     SkScalar width = paint.getStrokeWidth();
468     if (width < 0) {
469         return;
470     }
471 
472     GrAA aa = fSurfaceDrawContext->chooseAA(paint);
473 
474     if (count == 2 && mode == SkCanvas::kLines_PointMode) {
475         if (paint.getPathEffect()) {
476             // Probably a dashed line. Draw as a path.
477             GrPaint grPaint;
478             if (SkPaintToGrPaint(this->recordingContext(),
479                                  fSurfaceDrawContext->colorInfo(),
480                                  paint,
481                                  this->localToDevice(),
482                                  fSurfaceDrawContext->surfaceProps(),
483                                  &grPaint)) {
484                 SkPath path;
485                 path.setIsVolatile(true);
486                 path.moveTo(pts[0]);
487                 path.lineTo(pts[1]);
488                 fSurfaceDrawContext->drawPath(this->clip(),
489                                               std::move(grPaint),
490                                               aa,
491                                               this->localToDevice(),
492                                               path,
493                                               GrStyle(paint, SkPaint::kStroke_Style));
494             }
495             return;
496         }
497         if (!paint.getMaskFilter() &&
498             paint.getStrokeWidth() > 0 &&  // drawStrokedLine doesn't support hairlines.
499             paint.getStrokeCap() != SkPaint::kRound_Cap) { // drawStrokedLine doesn't do round caps.
500             // Simple stroked line. Bypass path rendering.
501             GrPaint grPaint;
502             if (SkPaintToGrPaint(this->recordingContext(),
503                                  fSurfaceDrawContext->colorInfo(),
504                                  paint,
505                                  this->localToDevice(),
506                                  fSurfaceDrawContext->surfaceProps(),
507                                  &grPaint)) {
508                 fSurfaceDrawContext->drawStrokedLine(this->clip(),
509                                                      std::move(grPaint),
510                                                      aa,
511                                                      this->localToDevice(),
512                                                      pts,
513                                                      SkStrokeRec(paint, SkPaint::kStroke_Style));
514             }
515             return;
516         }
517     }
518 
519     const GrCaps* caps = fContext->priv().caps();
520     SkScalar scales[2];
521     bool isHairline =
522             ((0 == width) ||
523              (1 == width && this->localToDevice().getMinMaxScales(scales) &&
524               SkScalarNearlyEqual(scales[0], 1.f) && SkScalarNearlyEqual(scales[1], 1.f))) &&
525 
526             // Don't do this as a hairline draw, which will emit line primitives, if
527             // lines are not permitted by caps.
528             !((mode == SkCanvas::kLines_PointMode || mode == SkCanvas::kPolygon_PointMode) &&
529               caps->avoidLineDraws());
530 
531     // we only handle non-coverage-aa hairlines and paints without path effects or mask filters,
532     // else we let the SkDraw call our drawPath()
533     if (!isHairline ||
534         paint.getPathEffect() ||
535         paint.getMaskFilter() ||
536         fSurfaceDrawContext->chooseAAType(aa) == GrAAType::kCoverage) {
537         SkRasterClip rc(this->devClipBounds());
538         SkDrawBase draw;
539         // don't need to set fBlitterChoose, as it should never get used
540         draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(this->width(), this->height()), nullptr, 0);
541         draw.fCTM = &this->localToDevice();
542         draw.fRC = &rc;
543         draw.drawDevicePoints(mode, count, pts, paint, this);
544         return;
545     }
546 
547     GrPaint grPaint;
548     if (!SkPaintToGrPaint(this->recordingContext(),
549                           fSurfaceDrawContext->colorInfo(),
550                           paint,
551                           this->localToDevice(),
552                           fSurfaceDrawContext->surfaceProps(),
553                           &grPaint)) {
554         return;
555     }
556 
557     static constexpr SkVertices::VertexMode kIgnoredMode = SkVertices::kTriangles_VertexMode;
558     sk_sp<SkVertices> vertices = SkVertices::MakeCopy(kIgnoredMode, SkToS32(count), pts, nullptr,
559                                                       nullptr);
560 
561     GrPrimitiveType primitiveType = point_mode_to_primitive_type(mode);
562     fSurfaceDrawContext->drawVertices(this->clip(), std::move(grPaint), this->localToDevice(),
563                                       std::move(vertices), &primitiveType);
564 }
565 
566 ///////////////////////////////////////////////////////////////////////////////
567 
drawRect(const SkRect & rect,const SkPaint & paint)568 void Device::drawRect(const SkRect& rect, const SkPaint& paint) {
569     ASSERT_SINGLE_OWNER
570     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawRect", fContext.get());
571 
572     GrStyle style(paint);
573 
574     // A couple reasons we might need to call drawPath.
575     if (paint.getMaskFilter() || paint.getPathEffect()) {
576         GrStyledShape shape(rect, style);
577 
578         GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(),
579                                              this->clip(), paint, this->localToDevice(), shape);
580         return;
581     }
582 
583     GrPaint grPaint;
584     if (!SkPaintToGrPaint(this->recordingContext(),
585                           fSurfaceDrawContext->colorInfo(),
586                           paint,
587                           this->localToDevice(),
588                           fSurfaceDrawContext->surfaceProps(),
589                           &grPaint)) {
590         return;
591     }
592 
593     fSurfaceDrawContext->drawRect(this->clip(), std::move(grPaint),
594                                   fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), rect,
595                                   &style);
596 }
597 
drawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],SkCanvas::QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)598 void Device::drawEdgeAAQuad(const SkRect& rect,
599                             const SkPoint clip[4],
600                             SkCanvas::QuadAAFlags aaFlags,
601                             const SkColor4f& color,
602                             SkBlendMode mode) {
603     ASSERT_SINGLE_OWNER
604     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawEdgeAAQuad", fContext.get());
605 
606     SkPMColor4f dstColor = SkColor4fPrepForDst(color, fSurfaceDrawContext->colorInfo()).premul();
607 
608     GrPaint grPaint;
609     grPaint.setColor4f(dstColor);
610     if (mode != SkBlendMode::kSrcOver) {
611         grPaint.setXPFactory(GrXPFactory::FromBlendMode(mode));
612     }
613 
614     if (clip) {
615         // Use fillQuadWithEdgeAA
616         fSurfaceDrawContext->fillQuadWithEdgeAA(this->clip(),
617                                                 std::move(grPaint),
618                                                 SkToGrQuadAAFlags(aaFlags),
619                                                 this->localToDevice(),
620                                                 clip,
621                                                 nullptr);
622     } else {
623         // Use fillRectWithEdgeAA to preserve mathematical properties of dst being rectangular
624         fSurfaceDrawContext->fillRectWithEdgeAA(this->clip(),
625                                                 std::move(grPaint),
626                                                 SkToGrQuadAAFlags(aaFlags),
627                                                 this->localToDevice(),
628                                                 rect);
629     }
630 }
631 
632 ///////////////////////////////////////////////////////////////////////////////
633 
drawRRect(const SkRRect & rrect,const SkPaint & paint)634 void Device::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
635     ASSERT_SINGLE_OWNER
636     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawRRect", fContext.get());
637 
638     auto mf = paint.getMaskFilter();
639     if (mf) {
640         if (GrFragmentProcessors::IsSupported(mf)) {
641             mf = nullptr; // already handled in SkPaintToGrPaint
642         }
643     }
644 
645     GrStyle style(paint);
646 
647     if (mf || style.pathEffect()) {
648         // A path effect will presumably transform this rrect into something else.
649         GrStyledShape shape(rrect, style);
650 
651         GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(),
652                                              this->clip(), paint, this->localToDevice(), shape);
653         return;
654     }
655 
656     SkASSERT(!style.pathEffect());
657 
658     GrPaint grPaint;
659     if (!SkPaintToGrPaint(this->recordingContext(),
660                           fSurfaceDrawContext->colorInfo(),
661                           paint,
662                           this->localToDevice(),
663                           fSurfaceDrawContext->surfaceProps(),
664                           &grPaint)) {
665         return;
666     }
667 
668     fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint),
669                                    fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
670                                    rrect, style);
671 }
672 
drawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)673 void Device::drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
674     ASSERT_SINGLE_OWNER
675     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawDRRect", fContext.get());
676     if (outer.isEmpty()) {
677        return;
678     }
679 
680     if (inner.isEmpty()) {
681         return this->drawRRect(outer, paint);
682     }
683 
684     SkStrokeRec stroke(paint);
685 
686     if (stroke.isFillStyle() && !paint.getMaskFilter() && !paint.getPathEffect()) {
687         // For axis-aligned filled DRRects, just draw a regular rrect with inner clipped out using a
688         // coverage FP instead of using path rendering.
689         if (auto fp = make_inverse_rrect_fp(this->localToDevice(), inner,
690                                             fSurfaceDrawContext->chooseAA(paint),
691                                             *fSurfaceDrawContext->caps()->shaderCaps())) {
692             GrPaint grPaint;
693             if (!SkPaintToGrPaint(this->recordingContext(),
694                                   fSurfaceDrawContext->colorInfo(),
695                                   paint,
696                                   this->localToDevice(),
697                                   fSurfaceDrawContext->surfaceProps(),
698                                   &grPaint)) {
699                 return;
700             }
701             SkASSERT(!grPaint.hasCoverageFragmentProcessor());
702             grPaint.setCoverageFragmentProcessor(std::move(fp));
703             fSurfaceDrawContext->drawRRect(this->clip(), std::move(grPaint),
704                                            fSurfaceDrawContext->chooseAA(paint),
705                                            this->localToDevice(), outer, GrStyle());
706             return;
707         }
708     }
709 
710     SkPath path;
711     path.setIsVolatile(true);
712     path.addRRect(outer);
713     path.addRRect(inner);
714     path.setFillType(SkPathFillType::kEvenOdd);
715 
716     // TODO: We are losing the possible mutability of the path here but this should probably be
717     // fixed by upgrading GrStyledShape to handle DRRects.
718     GrStyledShape shape(path, paint);
719 
720     GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(),
721                                          paint, this->localToDevice(), shape);
722 }
723 
724 /////////////////////////////////////////////////////////////////////////////
725 
drawRegion(const SkRegion & region,const SkPaint & paint)726 void Device::drawRegion(const SkRegion& region, const SkPaint& paint) {
727     ASSERT_SINGLE_OWNER
728 
729     if (paint.getMaskFilter()) {
730         SkPath path;
731         region.getBoundaryPath(&path);
732         path.setIsVolatile(true);
733         return this->drawPath(path, paint, true);
734     }
735 
736     GrPaint grPaint;
737     if (!SkPaintToGrPaint(this->recordingContext(),
738                           fSurfaceDrawContext->colorInfo(),
739                           paint,
740                           this->localToDevice(),
741                           fSurfaceDrawContext->surfaceProps(),
742                           &grPaint)) {
743         return;
744     }
745 
746     fSurfaceDrawContext->drawRegion(this->clip(), std::move(grPaint),
747                                     fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
748                                     region, GrStyle(paint));
749 }
750 
drawOval(const SkRect & oval,const SkPaint & paint)751 void Device::drawOval(const SkRect& oval, const SkPaint& paint) {
752     ASSERT_SINGLE_OWNER
753     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawOval", fContext.get());
754 
755     if (paint.getMaskFilter()) {
756         // The RRect path can handle special case blurring
757         SkRRect rr = SkRRect::MakeOval(oval);
758         return this->drawRRect(rr, paint);
759     }
760 
761     GrPaint grPaint;
762     if (!SkPaintToGrPaint(this->recordingContext(),
763                           fSurfaceDrawContext->colorInfo(),
764                           paint,
765                           this->localToDevice(),
766                           fSurfaceDrawContext->surfaceProps(),
767                           &grPaint)) {
768         return;
769     }
770 
771     fSurfaceDrawContext->drawOval(this->clip(), std::move(grPaint),
772                                   fSurfaceDrawContext->chooseAA(paint), this->localToDevice(), oval,
773                                   GrStyle(paint));
774 }
775 
drawArc(const SkArc & arc,const SkPaint & paint)776 void Device::drawArc(const SkArc& arc, const SkPaint& paint) {
777     ASSERT_SINGLE_OWNER
778     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawArc", fContext.get());
779     if (paint.getMaskFilter()) {
780         this->SkDevice::drawArc(arc, paint);
781         return;
782     }
783     GrPaint grPaint;
784     if (!SkPaintToGrPaint(this->recordingContext(),
785                           fSurfaceDrawContext->colorInfo(),
786                           paint,
787                           this->localToDevice(),
788                           fSurfaceDrawContext->surfaceProps(),
789                           &grPaint)) {
790         return;
791     }
792 
793     fSurfaceDrawContext->drawArc(this->clip(),
794                                  std::move(grPaint),
795                                  fSurfaceDrawContext->chooseAA(paint),
796                                  this->localToDevice(),
797                                  arc,
798                                  GrStyle(paint));
799 }
800 
801 ///////////////////////////////////////////////////////////////////////////////
802 
drawPath(const SkPath & origSrcPath,const SkPaint & paint,bool pathIsMutable)803 void Device::drawPath(const SkPath& origSrcPath, const SkPaint& paint, bool pathIsMutable) {
804 #if defined(GPU_TEST_UTILS)
805     if (fContext->priv().options().fAllPathsVolatile && !origSrcPath.isVolatile()) {
806         this->drawPath(SkPath(origSrcPath).setIsVolatile(true), paint, true);
807         return;
808     }
809 #endif
810     ASSERT_SINGLE_OWNER
811     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawPath", fContext.get());
812     if (!paint.getMaskFilter()) {
813         GrPaint grPaint;
814         if (!SkPaintToGrPaint(this->recordingContext(),
815                               fSurfaceDrawContext->colorInfo(),
816                               paint,
817                               this->localToDevice(),
818                               fSurfaceDrawContext->surfaceProps(),
819                               &grPaint)) {
820             return;
821         }
822         fSurfaceDrawContext->drawPath(this->clip(), std::move(grPaint),
823                                       fSurfaceDrawContext->chooseAA(paint), this->localToDevice(),
824                                       origSrcPath, GrStyle(paint));
825         return;
826     }
827 
828     // TODO: losing possible mutability of 'origSrcPath' here
829     GrStyledShape shape(origSrcPath, paint);
830 
831     GrBlurUtils::DrawShapeWithMaskFilter(fContext.get(), fSurfaceDrawContext.get(), this->clip(),
832                                          paint, this->localToDevice(), shape);
833 }
834 
createImageFilteringBackend(const SkSurfaceProps & surfaceProps,SkColorType colorType) const835 sk_sp<skif::Backend> Device::createImageFilteringBackend(const SkSurfaceProps& surfaceProps,
836                                                          SkColorType colorType) const {
837     return skif::MakeGaneshBackend(
838             fContext, fSurfaceDrawContext->origin(), surfaceProps, colorType);
839 }
840 
makeSpecial(const SkBitmap & bitmap)841 sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap& bitmap) {
842     ASSERT_SINGLE_OWNER
843 
844     // TODO: this makes a tight copy of 'bitmap' but it doesn't have to be (given SkSpecialImage's
845     // semantics). Since this is cached we would have to bake the fit into the cache key though.
846     auto view = std::get<0>(
847             GrMakeCachedBitmapProxyView(fContext.get(), bitmap, /*label=*/"Device_MakeSpecial"));
848     if (!view) {
849         return nullptr;
850     }
851 
852     const SkIRect rect = SkIRect::MakeSize(view.proxy()->dimensions());
853 
854     // GrMakeCachedBitmapProxyView creates a tight copy of 'bitmap' so we don't have to subset
855     // the special image
856     return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
857                                                 rect,
858                                                 bitmap.getGenerationID(),
859                                                 std::move(view),
860                                                 {SkColorTypeToGrColorType(bitmap.colorType()),
861                                                  kPremul_SkAlphaType,
862                                                  bitmap.refColorSpace()},
863                                                 this->surfaceProps());
864 }
865 
makeSpecial(const SkImage * image)866 sk_sp<SkSpecialImage> Device::makeSpecial(const SkImage* image) {
867     ASSERT_SINGLE_OWNER
868 
869     SkPixmap pm;
870     if (image->isTextureBacked()) {
871         auto [view, ct] =
872                 skgpu::ganesh::AsView(this->recordingContext(), image, skgpu::Mipmapped::kNo);
873         SkASSERT(view);
874 
875         return SkSpecialImages::MakeDeferredFromGpu(
876                 fContext.get(),
877                 SkIRect::MakeWH(image->width(), image->height()),
878                 image->uniqueID(),
879                 std::move(view),
880                 {ct, kPremul_SkAlphaType, image->refColorSpace()},
881                 this->surfaceProps());
882     } else if (image->peekPixels(&pm)) {
883         SkBitmap bm;
884 
885         bm.installPixels(pm);
886         return this->makeSpecial(bm);
887     } else {
888         return nullptr;
889     }
890 }
891 
snapSpecial(const SkIRect & subset,bool forceCopy)892 sk_sp<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) {
893     ASSERT_SINGLE_OWNER
894 
895     auto sdc = fSurfaceDrawContext.get();
896 
897     // If we are wrapping a vulkan secondary command buffer, then we can't snap off a special image
898     // since it would require us to make a copy of the underlying VkImage which we don't have access
899     // to. Additionaly we can't stop and start the render pass that is used with the secondary
900     // command buffer.
901     if (sdc->wrapsVkSecondaryCB()) {
902         return nullptr;
903     }
904 
905     SkASSERT(sdc->asSurfaceProxy());
906 
907     SkIRect finalSubset = subset;
908     GrSurfaceProxyView view = sdc->readSurfaceView();
909     if (forceCopy || !view.asTextureProxy()) {
910         // When the device doesn't have a texture, or a copy is requested, we create a temporary
911         // texture that matches the device contents
912         view = GrSurfaceProxyView::Copy(fContext.get(),
913                                         std::move(view),
914                                         skgpu::Mipmapped::kNo,  // Don't auto generate mips
915                                         subset,
916                                         SkBackingFit::kApprox,
917                                         skgpu::Budgeted::kYes,
918                                         /*label=*/"Device_SnapSpecial");  // Always budgeted
919         if (!view) {
920             return nullptr;
921         }
922         // Since this copied only the requested subset, the special image wrapping the proxy no
923         // longer needs the original subset.
924         finalSubset = SkIRect::MakeSize(view.dimensions());
925     }
926 
927     return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
928                                                 finalSubset,
929                                                 kNeedNewImageUniqueID_SpecialImage,
930                                                 std::move(view),
931                                                 GrColorInfo(this->imageInfo().colorInfo()),
932                                                 this->surfaceProps());
933 }
934 
snapSpecialScaled(const SkIRect & subset,const SkISize & dstDims)935 sk_sp<SkSpecialImage> Device::snapSpecialScaled(const SkIRect& subset, const SkISize& dstDims) {
936      ASSERT_SINGLE_OWNER
937 
938     auto sdc = fSurfaceDrawContext.get();
939 
940     // If we are wrapping a vulkan secondary command buffer, then we can't snap off a special image
941     // since it would require us to make a copy of the underlying VkImage which we don't have access
942     // to. Additionaly we can't stop and start the render pass that is used with the secondary
943     // command buffer.
944     if (sdc->wrapsVkSecondaryCB()) {
945         return nullptr;
946     }
947 
948     SkASSERT(sdc->asSurfaceProxy());
949 
950     auto scaledContext = sdc->rescale(sdc->imageInfo().makeDimensions(dstDims),
951                                       sdc->origin(),
952                                       subset,
953                                       RescaleGamma::kSrc,
954                                       RescaleMode::kLinear);
955     if (!scaledContext) {
956         return nullptr;
957     }
958 
959     return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
960                                                 SkIRect::MakeSize(dstDims),
961                                                 kNeedNewImageUniqueID_SpecialImage,
962                                                 scaledContext->readSurfaceView(),
963                                                 GrColorInfo(this->imageInfo().colorInfo()),
964                                                 this->surfaceProps());
965 }
966 
drawDevice(SkDevice * device,const SkSamplingOptions & sampling,const SkPaint & paint)967 void Device::drawDevice(SkDevice* device,
968                         const SkSamplingOptions& sampling,
969                         const SkPaint& paint) {
970     ASSERT_SINGLE_OWNER
971     // clear of the source device must occur before CHECK_SHOULD_DRAW
972     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawDevice", fContext.get());
973     this->SkDevice::drawDevice(device, sampling, paint);
974 }
975 
drawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)976 void Device::drawImageRect(const SkImage* image,
977                            const SkRect* src,
978                            const SkRect& dst,
979                            const SkSamplingOptions& sampling,
980                            const SkPaint& paint,
981                            SkCanvas::SrcRectConstraint constraint) {
982     ASSERT_SINGLE_OWNER
983 
984     GrAA aa = fSurfaceDrawContext->chooseAA(paint);
985     SkCanvas::QuadAAFlags aaFlags = (aa == GrAA::kYes) ? SkCanvas::kAll_QuadAAFlags
986                                                        : SkCanvas::kNone_QuadAAFlags;
987 
988     this->drawImageQuadDirect(image,
989                               src ? *src
990                                   : SkRect::MakeIWH(image->width(), image->height()),
991                               dst,
992                               /* dstClip= */ nullptr,
993                               aaFlags,
994                               /* preViewMatrix= */ nullptr,
995                               sampling,
996                               paint,
997                               constraint);
998 }
999 
drawAsTiledImageRect(SkCanvas * canvas,const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)1000 bool Device::drawAsTiledImageRect(SkCanvas* canvas,
1001                                   const SkImage* image,
1002                                   const SkRect* src,
1003                                   const SkRect& dst,
1004                                   const SkSamplingOptions& sampling,
1005                                   const SkPaint& paint,
1006                                   SkCanvas::SrcRectConstraint constraint) {
1007     GrRecordingContext* rCtx = canvas->recordingContext();
1008     if (!rCtx) {
1009         return false;
1010     }
1011     ASSERT_SINGLE_OWNER
1012 
1013     GrAA aa = fSurfaceDrawContext->chooseAA(paint);
1014     SkCanvas::QuadAAFlags aaFlags = (aa == GrAA::kYes) ? SkCanvas::kAll_QuadAAFlags
1015                                                        : SkCanvas::kNone_QuadAAFlags;
1016 
1017     // NOTE: if the context is not a direct context, it doesn't have access to the resource
1018     // cache, and theoretically, the resource cache's limits could be being changed on
1019     // another thread, so even having access to just the limit wouldn't be a reliable
1020     // test during recording here.
1021     size_t cacheSize = 0;
1022     if (auto dCtx = GrAsDirectContext(rCtx)) {
1023         cacheSize = dCtx->getResourceCacheLimit();
1024     }
1025     size_t maxTextureSize = rCtx->maxTextureSize();
1026 #if defined(GPU_TEST_UTILS)
1027     if (gOverrideMaxTextureSizeGanesh) {
1028         maxTextureSize = gOverrideMaxTextureSizeGanesh;
1029     }
1030     gNumTilesDrawnGanesh.store(0, std::memory_order_relaxed);
1031 #endif
1032 
1033     [[maybe_unused]] auto [wasTiled, numTiles] = TiledTextureUtils::DrawAsTiledImageRect(
1034             canvas,
1035             image,
1036             src ? *src : SkRect::MakeIWH(image->width(), image->height()),
1037             dst,
1038             aaFlags,
1039             sampling,
1040             &paint,
1041             constraint,
1042             rCtx->priv().options().fSharpenMipmappedTextures,
1043             cacheSize,
1044             maxTextureSize);
1045 #if defined(GPU_TEST_UTILS)
1046     gNumTilesDrawnGanesh.store(numTiles, std::memory_order_relaxed);
1047 #endif
1048     return wasTiled;
1049 }
1050 
drawViewLattice(GrSurfaceProxyView view,const GrColorInfo & info,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst,SkFilterMode filter,const SkPaint & origPaint)1051 void Device::drawViewLattice(GrSurfaceProxyView view,
1052                              const GrColorInfo& info,
1053                              std::unique_ptr<SkLatticeIter> iter,
1054                              const SkRect& dst,
1055                              SkFilterMode filter,
1056                              const SkPaint& origPaint) {
1057     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawViewLattice", fContext.get());
1058     SkASSERT(view);
1059 
1060     SkTCopyOnFirstWrite<SkPaint> paint(&origPaint);
1061 
1062     if (!info.isAlphaOnly() && (paint->getColor() & 0x00FFFFFF) != 0x00FFFFFF) {
1063         paint.writable()->setColor(SkColorSetARGB(origPaint.getAlpha(), 0xFF, 0xFF, 0xFF));
1064     }
1065     GrPaint grPaint;
1066     // Passing null as shaderFP indicates that the GP will provide the shader.
1067     if (!SkPaintToGrPaintReplaceShader(this->recordingContext(),
1068                                        fSurfaceDrawContext->colorInfo(),
1069                                        *paint,
1070                                        this->localToDevice(),
1071                                        /*shaderFP=*/nullptr,
1072                                        fSurfaceDrawContext->surfaceProps(),
1073                                        &grPaint)) {
1074         return;
1075     }
1076 
1077     if (info.isAlphaOnly()) {
1078         // If we were doing this with an FP graph we'd use a kDstIn blend between the texture
1079         // and the paint color.
1080         view.concatSwizzle(skgpu::Swizzle("aaaa"));
1081     }
1082     auto csxf = GrColorSpaceXform::Make(info, fSurfaceDrawContext->colorInfo());
1083 
1084     fSurfaceDrawContext->drawImageLattice(this->clip(),
1085                                           std::move(grPaint),
1086                                           this->localToDevice(),
1087                                           std::move(view),
1088                                           info.alphaType(),
1089                                           std::move(csxf),
1090                                           filter,
1091                                           std::move(iter),
1092                                           dst);
1093 }
1094 
drawImageLattice(const SkImage * image,const SkCanvas::Lattice & lattice,const SkRect & dst,SkFilterMode filter,const SkPaint & paint)1095 void Device::drawImageLattice(const SkImage* image,
1096                               const SkCanvas::Lattice& lattice,
1097                               const SkRect& dst,
1098                               SkFilterMode filter,
1099                               const SkPaint& paint) {
1100     ASSERT_SINGLE_OWNER
1101     auto iter = std::make_unique<SkLatticeIter>(lattice, dst);
1102 
1103     auto [view, ct] = skgpu::ganesh::AsView(this->recordingContext(), image, skgpu::Mipmapped::kNo);
1104     if (view) {
1105         GrColorInfo colorInfo(ct, image->alphaType(), image->refColorSpace());
1106         this->drawViewLattice(
1107                 std::move(view), std::move(colorInfo), std::move(iter), dst, filter, paint);
1108     }
1109 }
1110 
drawVertices(const SkVertices * vertices,sk_sp<SkBlender> blender,const SkPaint & paint,bool skipColorXform)1111 void Device::drawVertices(const SkVertices* vertices,
1112                           sk_sp<SkBlender> blender,
1113                           const SkPaint& paint,
1114                           bool skipColorXform) {
1115     ASSERT_SINGLE_OWNER
1116     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawVertices", fContext.get());
1117     SkASSERT(vertices);
1118 
1119 #ifdef SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER
1120     if (!paint.getShader()) {
1121         blender = SkBlender::Mode(SkBlendMode::kDst);
1122     }
1123 #endif
1124 
1125     SkVerticesPriv info(vertices->priv());
1126 
1127     GrPaint grPaint;
1128     if (!init_vertices_paint(fContext.get(),
1129                              fSurfaceDrawContext->colorInfo(),
1130                              paint,
1131                              this->localToDevice(),
1132                              blender.get(),
1133                              info.hasColors(),
1134                              fSurfaceDrawContext->surfaceProps(),
1135                              &grPaint)) {
1136         return;
1137     }
1138     fSurfaceDrawContext->drawVertices(this->clip(),
1139                                       std::move(grPaint),
1140                                       this->localToDevice(),
1141                                       sk_ref_sp(const_cast<SkVertices*>(vertices)),
1142                                       nullptr,
1143                                       skipColorXform);
1144 }
1145 
drawMesh(const SkMesh & mesh,sk_sp<SkBlender> blender,const SkPaint & paint)1146 void Device::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
1147     ASSERT_SINGLE_OWNER
1148     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawMesh", fContext.get());
1149     if (!mesh.isValid()) {
1150         return;
1151     }
1152 
1153     GrPaint grPaint;
1154     if (!init_vertices_paint(fContext.get(),
1155                              fSurfaceDrawContext->colorInfo(),
1156                              paint,
1157                              this->localToDevice(),
1158                              blender.get(),
1159                              SkMeshSpecificationPriv::HasColors(*mesh.spec()),
1160                              fSurfaceDrawContext->surfaceProps(),
1161                              &grPaint)) {
1162         return;
1163     }
1164 
1165     TArray<std::unique_ptr<GrFragmentProcessor>> meshChildFPs;
1166     if (!init_mesh_child_effects(fContext.get(),
1167                                  fSurfaceDrawContext->colorInfo(),
1168                                  fSurfaceDrawContext->surfaceProps(),
1169                                  mesh,
1170                                  &meshChildFPs)) {
1171         return;
1172     }
1173     fSurfaceDrawContext->drawMesh(this->clip(), std::move(grPaint), this->localToDevice(), mesh,
1174                                   std::move(meshChildFPs));
1175 }
1176 
1177 ///////////////////////////////////////////////////////////////////////////////
1178 
1179 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
drawShadow(const SkPath & path,const SkDrawShadowRec & rec)1180 void Device::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
1181 #if defined(GPU_TEST_UTILS)
1182     if (fContext->priv().options().fAllPathsVolatile && !path.isVolatile()) {
1183         this->drawShadow(SkPath(path).setIsVolatile(true), rec);
1184         return;
1185     }
1186 #endif
1187     ASSERT_SINGLE_OWNER
1188     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawShadow", fContext.get());
1189 
1190     if (!fSurfaceDrawContext->drawFastShadow(this->clip(), this->localToDevice(), path, rec)) {
1191         // failed to find an accelerated case
1192         this->SkDevice::drawShadow(path, rec);
1193     }
1194 }
1195 #endif  // SK_ENABLE_OPTIMIZE_SIZE
1196 
1197 ///////////////////////////////////////////////////////////////////////////////
1198 
drawAtlas(const SkRSXform xform[],const SkRect texRect[],const SkColor colors[],int count,sk_sp<SkBlender> blender,const SkPaint & paint)1199 void Device::drawAtlas(const SkRSXform xform[],
1200                        const SkRect texRect[],
1201                        const SkColor colors[],
1202                        int count,
1203                        sk_sp<SkBlender> blender,
1204                        const SkPaint& paint) {
1205     ASSERT_SINGLE_OWNER
1206     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawAtlas", fContext.get());
1207 
1208     GrPaint grPaint;
1209     if (colors) {
1210         if (!SkPaintToGrPaintWithBlend(this->recordingContext(),
1211                                        fSurfaceDrawContext->colorInfo(),
1212                                        paint,
1213                                        this->localToDevice(),
1214                                        blender.get(),
1215                                        fSurfaceDrawContext->surfaceProps(),
1216                                        &grPaint)) {
1217             return;
1218         }
1219     } else {
1220         if (!SkPaintToGrPaint(this->recordingContext(),
1221                               fSurfaceDrawContext->colorInfo(),
1222                               paint,
1223                               this->localToDevice(),
1224                               fSurfaceDrawContext->surfaceProps(),
1225                               &grPaint)) {
1226             return;
1227         }
1228     }
1229 
1230     fSurfaceDrawContext->drawAtlas(this->clip(), std::move(grPaint), this->localToDevice(), count,
1231                                    xform, texRect, colors);
1232 }
1233 
1234 ///////////////////////////////////////////////////////////////////////////////
1235 
onDrawGlyphRunList(SkCanvas * canvas,const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1236 void Device::onDrawGlyphRunList(SkCanvas* canvas,
1237                                 const sktext::GlyphRunList& glyphRunList,
1238                                 const SkPaint& paint) {
1239     ASSERT_SINGLE_OWNER
1240     GR_CREATE_TRACE_MARKER_CONTEXT("skgpu::ganesh::Device", "drawGlyphRunList", fContext.get());
1241     SkASSERT(!glyphRunList.hasRSXForm());
1242 
1243     if (glyphRunList.blob() == nullptr) {
1244         // If the glyphRunList does not have an associated text blob, then it was created by one of
1245         // the direct draw APIs (drawGlyphs, etc.). Use a Slug to draw the glyphs.
1246         auto slug = this->convertGlyphRunListToSlug(glyphRunList, paint);
1247         if (slug != nullptr) {
1248             this->drawSlug(canvas, slug.get(), paint);
1249         }
1250     } else {
1251         fSurfaceDrawContext->drawGlyphRunList(canvas,
1252                                               this->clip(),
1253                                               this->localToDevice(),
1254                                               glyphRunList,
1255                                               this->strikeDeviceInfo(),
1256                                               paint);
1257     }
1258 }
1259 
1260 ///////////////////////////////////////////////////////////////////////////////
1261 
drawDrawable(SkCanvas * canvas,SkDrawable * drawable,const SkMatrix * matrix)1262 void Device::drawDrawable(SkCanvas* canvas, SkDrawable* drawable, const SkMatrix* matrix) {
1263     ASSERT_SINGLE_OWNER
1264 
1265     GrBackendApi api = this->recordingContext()->backend();
1266     if (GrBackendApi::kVulkan == api) {
1267         const SkMatrix& ctm = this->localToDevice();
1268         const SkMatrix& combinedMatrix = matrix ? SkMatrix::Concat(ctm, *matrix) : ctm;
1269         std::unique_ptr<SkDrawable::GpuDrawHandler> gpuDraw =
1270                 drawable->snapGpuDrawHandler(api, combinedMatrix, this->devClipBounds(),
1271                                              this->imageInfo());
1272         if (gpuDraw) {
1273             fSurfaceDrawContext->drawDrawable(
1274                     std::move(gpuDraw), combinedMatrix.mapRect(drawable->getBounds()));
1275             return;
1276         }
1277     }
1278     this->SkDevice::drawDrawable(canvas, drawable, matrix);
1279 }
1280 
1281 
1282 ///////////////////////////////////////////////////////////////////////////////
1283 
readSurfaceView()1284 GrSurfaceProxyView Device::readSurfaceView() {
1285     return this->surfaceFillContext()->readSurfaceView();
1286 }
1287 
targetProxy()1288 GrRenderTargetProxy* Device::targetProxy() {
1289     return this->readSurfaceView().asRenderTargetProxy();
1290 }
1291 
wait(int numSemaphores,const GrBackendSemaphore * waitSemaphores,bool deleteSemaphoresAfterWait)1292 bool Device::wait(int numSemaphores,
1293                   const GrBackendSemaphore* waitSemaphores,
1294                   bool deleteSemaphoresAfterWait) {
1295     ASSERT_SINGLE_OWNER
1296 
1297     return fSurfaceDrawContext->waitOnSemaphores(numSemaphores, waitSemaphores,
1298                                                  deleteSemaphoresAfterWait);
1299 }
1300 
discard()1301 void Device::discard() {
1302     fSurfaceDrawContext->discard();
1303 }
1304 
resolveMSAA()1305 void Device::resolveMSAA() {
1306     fSurfaceDrawContext->resolveMSAA();
1307 }
1308 
replaceBackingProxy(SkSurface::ContentChangeMode mode,sk_sp<GrRenderTargetProxy> newRTP,GrColorType grColorType,sk_sp<SkColorSpace> colorSpace,GrSurfaceOrigin origin,const SkSurfaceProps & props)1309 bool Device::replaceBackingProxy(SkSurface::ContentChangeMode mode,
1310                                  sk_sp<GrRenderTargetProxy> newRTP,
1311                                  GrColorType grColorType,
1312                                  sk_sp<SkColorSpace> colorSpace,
1313                                  GrSurfaceOrigin origin,
1314                                  const SkSurfaceProps& props) {
1315     auto sdc = SurfaceDrawContext::Make(fContext.get(), grColorType, std::move(newRTP),
1316                                         std::move(colorSpace), origin, props);
1317     if (!sdc) {
1318         return false;
1319     }
1320 
1321     SkASSERT(sdc->dimensions() == fSurfaceDrawContext->dimensions());
1322     SkASSERT(sdc->numSamples() == fSurfaceDrawContext->numSamples());
1323     SkASSERT(sdc->asSurfaceProxy()->priv().isExact());
1324     if (mode == SkSurface::kRetain_ContentChangeMode) {
1325         if (fContext->abandoned()) {
1326             return false;
1327         }
1328 
1329         SkASSERT(fSurfaceDrawContext->asTextureProxy());
1330         SkAssertResult(sdc->blitTexture(fSurfaceDrawContext->readSurfaceView(),
1331                                         SkIRect::MakeWH(this->width(), this->height()),
1332                                         SkIPoint::Make(0, 0)));
1333     }
1334 
1335     fSurfaceDrawContext = std::move(sdc);
1336     return true;
1337 }
1338 
replaceBackingProxy(SkSurface::ContentChangeMode mode)1339 bool Device::replaceBackingProxy(SkSurface::ContentChangeMode mode) {
1340     ASSERT_SINGLE_OWNER
1341 
1342     const SkImageInfo& ii = this->imageInfo();
1343     GrRenderTargetProxy* oldRTP = this->targetProxy();
1344     GrSurfaceProxyView oldView = this->readSurfaceView();
1345 
1346     auto grColorType = SkColorTypeToGrColorType(ii.colorType());
1347     auto format = fContext->priv().caps()->getDefaultBackendFormat(grColorType, GrRenderable::kYes);
1348     if (!format.isValid()) {
1349         return false;
1350     }
1351 
1352     GrProxyProvider* proxyProvider = fContext->priv().proxyProvider();
1353     // This entry point is used by SkSurface_Ganesh::onCopyOnWrite so it must create a
1354     // kExact-backed render target proxy
1355     sk_sp<GrTextureProxy> proxy =
1356             proxyProvider->createProxy(format,
1357                                        ii.dimensions(),
1358                                        GrRenderable::kYes,
1359                                        oldRTP->numSamples(),
1360                                        oldView.mipmapped(),
1361                                        SkBackingFit::kExact,
1362                                        oldRTP->isBudgeted(),
1363                                        oldRTP->isProtected(),
1364                                        /*label=*/"BaseDevice_ReplaceBackingProxy");
1365     if (!proxy) {
1366         return false;
1367     }
1368 
1369     return this->replaceBackingProxy(mode, sk_ref_sp(proxy->asRenderTargetProxy()),
1370                                      grColorType, ii.refColorSpace(), oldView.origin(),
1371                                      this->surfaceProps());
1372 }
1373 
asyncRescaleAndReadPixels(const SkImageInfo & info,const SkIRect & srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)1374 void Device::asyncRescaleAndReadPixels(const SkImageInfo& info,
1375                                        const SkIRect& srcRect,
1376                                        RescaleGamma rescaleGamma,
1377                                        RescaleMode rescaleMode,
1378                                        ReadPixelsCallback callback,
1379                                        ReadPixelsContext context) {
1380     auto* sdc = fSurfaceDrawContext.get();
1381     // Context TODO: Elevate direct context requirement to public API.
1382     auto dContext = sdc->recordingContext()->asDirectContext();
1383     if (!dContext) {
1384         return;
1385     }
1386     sdc->asyncRescaleAndReadPixels(dContext, info, srcRect, rescaleGamma, rescaleMode, callback,
1387                                    context);
1388 }
1389 
asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,bool readAlpha,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)1390 void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
1391                                              bool readAlpha,
1392                                              sk_sp<SkColorSpace> dstColorSpace,
1393                                              const SkIRect& srcRect,
1394                                              SkISize dstSize,
1395                                              RescaleGamma rescaleGamma,
1396                                              RescaleMode rescaleMode,
1397                                              ReadPixelsCallback callback,
1398                                              ReadPixelsContext context) {
1399     auto* sdc = fSurfaceDrawContext.get();
1400     // Context TODO: Elevate direct context requirement to public API.
1401     auto dContext = sdc->recordingContext()->asDirectContext();
1402     if (!dContext) {
1403         return;
1404     }
1405     sdc->asyncRescaleAndReadPixelsYUV420(dContext,
1406                                          yuvColorSpace,
1407                                          readAlpha,
1408                                          std::move(dstColorSpace),
1409                                          srcRect,
1410                                          dstSize,
1411                                          rescaleGamma,
1412                                          rescaleMode,
1413                                          callback,
1414                                          context);
1415 }
1416 
1417 ///////////////////////////////////////////////////////////////////////////////
1418 
createDevice(const CreateInfo & cinfo,const SkPaint *)1419 sk_sp<SkDevice> Device::createDevice(const CreateInfo& cinfo, const SkPaint*) {
1420     ASSERT_SINGLE_OWNER
1421 
1422     SkSurfaceProps props =
1423         this->surfaceProps().cloneWithPixelGeometry(cinfo.fPixelGeometry);
1424 
1425     SkASSERT(cinfo.fInfo.colorType() != kRGBA_1010102_SkColorType);
1426 
1427     auto sdc = SurfaceDrawContext::MakeWithFallback(
1428             fContext.get(),
1429             SkColorTypeToGrColorType(cinfo.fInfo.colorType()),
1430             cinfo.fInfo.refColorSpace(),
1431             SkBackingFit::kApprox,
1432             cinfo.fInfo.dimensions(),
1433             props,
1434             fSurfaceDrawContext->numSamples(),
1435             skgpu::Mipmapped::kNo,
1436             fSurfaceDrawContext->asSurfaceProxy()->isProtected(),
1437             fSurfaceDrawContext->origin(),
1438             skgpu::Budgeted::kYes);
1439     if (!sdc) {
1440         return nullptr;
1441     }
1442 
1443     // Skia's convention is to only clear a device if it is non-opaque.
1444     InitContents init = cinfo.fInfo.isOpaque() ? InitContents::kUninit : InitContents::kClear;
1445 
1446     return Device::Make(std::move(sdc), cinfo.fInfo.alphaType(), init);
1447 }
1448 
makeSurface(const SkImageInfo & info,const SkSurfaceProps & props)1449 sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1450     ASSERT_SINGLE_OWNER
1451     // TODO: Change the signature of newSurface to take a budgeted parameter.
1452     static const skgpu::Budgeted kBudgeted = skgpu::Budgeted::kNo;
1453     bool isProtected = this->targetProxy()->isProtected() == GrProtected::kYes;
1454     return SkSurfaces::RenderTarget(fContext.get(),
1455                                     kBudgeted,
1456                                     info,
1457                                     fSurfaceDrawContext->numSamples(),
1458                                     fSurfaceDrawContext->origin(),
1459                                     &props,
1460                                     /* shouldCreateWithMips= */ false,
1461                                     isProtected);
1462 }
1463 
1464 ////////////////////////////////////////////////////////////////////////////////////
1465 
android_utils_clipWithStencil()1466 bool Device::android_utils_clipWithStencil() {
1467     SkRegion clipRegion;
1468     this->android_utils_clipAsRgn(&clipRegion);
1469     if (clipRegion.isEmpty()) {
1470         return false;
1471     }
1472     auto sdc = fSurfaceDrawContext.get();
1473     SkASSERT(sdc);
1474     GrPaint grPaint;
1475     grPaint.setXPFactory(GrDisableColorXPFactory::Get());
1476     static constexpr GrUserStencilSettings kDrawToStencil(
1477         GrUserStencilSettings::StaticInit<
1478             0x1,
1479             GrUserStencilTest::kAlways,
1480             0x1,
1481             GrUserStencilOp::kReplace,
1482             GrUserStencilOp::kReplace,
1483             0x1>()
1484     );
1485     // Regions don't actually need AA, but in DMSAA mode everything is antialiased.
1486     GrAA aa = GrAA(fSurfaceDrawContext->alwaysAntialias());
1487     sdc->drawRegion(nullptr, std::move(grPaint), aa, SkMatrix::I(), clipRegion,
1488                     GrStyle::SimpleFill(), &kDrawToStencil);
1489     return true;
1490 }
1491 
strikeDeviceInfo() const1492 SkStrikeDeviceInfo Device::strikeDeviceInfo() const {
1493     return {this->surfaceProps(), this->scalerContextFlags(), &fSubRunControl};
1494 }
1495 
convertGlyphRunListToSlug(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1496 sk_sp<sktext::gpu::Slug> Device::convertGlyphRunListToSlug(const sktext::GlyphRunList& glyphRunList,
1497                                                            const SkPaint& paint) {
1498     return sktext::gpu::SlugImpl::Make(this->localToDevice(),
1499                                        glyphRunList,
1500                                        paint,
1501                                        this->strikeDeviceInfo(),
1502                                        SkStrikeCache::GlobalStrikeCache());
1503 }
1504 
1505 #if defined(SK_DEBUG)
valid_slug_matrices(const SkMatrix & creationMatrix,const SkMatrix & positionMatrix)1506 static bool valid_slug_matrices(const SkMatrix& creationMatrix, const SkMatrix& positionMatrix) {
1507     // A Slug can be drawn with:
1508     //   The exact same matrix that was used for creation
1509     // - OR -
1510     //   A non-perspective matrix that has the same 2x2 and a relative integer translation
1511     //
1512     // For the second case, calculate the translation in source space to a translation in
1513     // device space by mapping (0, 0) through both the creationMatrix and the positionMatrix;
1514     // take the difference.
1515     if (creationMatrix == positionMatrix) {
1516         return true;
1517     }
1518 
1519     SkVector translation = positionMatrix.mapOrigin() - creationMatrix.mapOrigin();
1520     return creationMatrix.getScaleX() == positionMatrix.getScaleX() &&
1521            creationMatrix.getScaleY() == positionMatrix.getScaleY() &&
1522            creationMatrix.getSkewX() == positionMatrix.getSkewX() &&
1523            creationMatrix.getSkewY() == positionMatrix.getSkewY() &&
1524            !positionMatrix.hasPerspective() && !creationMatrix.hasPerspective() &&
1525            SkScalarIsInt(translation.x()) && SkScalarIsInt(translation.y());
1526 }
1527 #endif
1528 
1529 
drawSlug(SkCanvas * canvas,const sktext::gpu::Slug * slug,const SkPaint & paint)1530 void Device::drawSlug(SkCanvas* canvas, const sktext::gpu::Slug* slug, const SkPaint& paint) {
1531     SkASSERT(canvas);
1532     SkASSERT(slug);
1533     const sktext::gpu::SlugImpl* slugImpl = static_cast<const sktext::gpu::SlugImpl*>(slug);
1534 #if defined(SK_DEBUG)
1535     if (!fContext->priv().options().fSupportBilerpFromGlyphAtlas) {
1536         // We can draw a slug if the atlas has padding or if the creation matrix and the
1537         // drawing matrix are the same (up to integer translation).
1538         // If they are the same, then the Slug will use the direct drawing code and not use bi-lerp.
1539         SkMatrix slugMatrix = slugImpl->initialPositionMatrix();
1540         SkMatrix positionMatrix = this->localToDevice();
1541         positionMatrix.preTranslate(slugImpl->origin().x(), slugImpl->origin().y());
1542         SkASSERT(valid_slug_matrices(slugMatrix, positionMatrix));
1543     }
1544 #endif
1545     auto atlasDelegate = [&](const sktext::gpu::AtlasSubRun* subRun,
1546                              SkPoint drawOrigin,
1547                              const SkPaint& paint,
1548                              sk_sp<SkRefCnt> subRunStorage,
1549                              sktext::gpu::RendererData) {
1550         auto[drawingClip, op] = subRun->makeAtlasTextOp(
1551                 this->clip(), this->localToDevice(), drawOrigin, paint,
1552                 std::move(subRunStorage), fSurfaceDrawContext.get());
1553         if (op != nullptr) {
1554             fSurfaceDrawContext->addDrawOp(drawingClip, std::move(op));
1555         }
1556     };
1557 
1558     slugImpl->subRuns()->draw(canvas, slugImpl->origin(), paint, slugImpl, atlasDelegate);
1559 }
1560 
1561 }  // namespace skgpu::ganesh
1562