xref: /aosp_15_r20/external/skia/src/gpu/graphite/Device.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Device.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Recorder.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Recording.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/graphite/Surface.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/AtlasTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/BlurUtils.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/SkBackingFit.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/AtlasProvider.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Buffer.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Caps.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/CommandBuffer.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextOptionsPriv.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextPriv.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ContextUtils.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawContext.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawList.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/DrawParams.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Image_Graphite.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Log.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/PathAtlas.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RasterPathAtlas.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RecorderPriv.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Renderer.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/RendererProvider.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/ResourceTypes.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/SharedContext.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/SpecialImage_Graphite.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Surface_Graphite.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/TextureProxy.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/TextureUtils.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/BoundsManager.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Geometry.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/IntersectionTree.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Shape.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/geom/Transform_graphite.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/text/TextAtlasManager.h"
45*c8dee2aaSAndroid Build Coastguard Worker 
46*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlenderBase.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlurMaskFilterImpl.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpacePriv.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkConvertPixels.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkImageFilterTypes.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkImageInfoPriv.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkImagePriv.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMatrixPriv.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPaintPriv.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRRectPriv.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSpecialImage.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkStrikeCache.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkVerticesPriv.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/TiledTextureUtils.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/GlyphRun.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/GlyphVector.h"
68*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/SlugImpl.h"
69*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/SubRunContainer.h"
70*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/TextBlobRedrawCoordinator.h"
71*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/gpu/VertexFiller.h"
72*c8dee2aaSAndroid Build Coastguard Worker 
73*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
74*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
75*c8dee2aaSAndroid Build Coastguard Worker #include <unordered_map>
76*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
77*c8dee2aaSAndroid Build Coastguard Worker 
78*c8dee2aaSAndroid Build Coastguard Worker using RescaleGamma       = SkImage::RescaleGamma;
79*c8dee2aaSAndroid Build Coastguard Worker using RescaleMode        = SkImage::RescaleMode;
80*c8dee2aaSAndroid Build Coastguard Worker using ReadPixelsCallback = SkImage::ReadPixelsCallback;
81*c8dee2aaSAndroid Build Coastguard Worker using ReadPixelsContext  = SkImage::ReadPixelsContext;
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
84*c8dee2aaSAndroid Build Coastguard Worker int gOverrideMaxTextureSizeGraphite = 0;
85*c8dee2aaSAndroid Build Coastguard Worker // Allows tests to check how many tiles were drawn on the most recent call to
86*c8dee2aaSAndroid Build Coastguard Worker // Device::drawAsTiledImageRect. This is an atomic because we can write to it from
87*c8dee2aaSAndroid Build Coastguard Worker // multiple threads during "normal" operations. However, the tests that actually
88*c8dee2aaSAndroid Build Coastguard Worker // read from it are done single-threaded.
89*c8dee2aaSAndroid Build Coastguard Worker std::atomic<int> gNumTilesDrawnGraphite{0};
90*c8dee2aaSAndroid Build Coastguard Worker #endif
91*c8dee2aaSAndroid Build Coastguard Worker 
92*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
93*c8dee2aaSAndroid Build Coastguard Worker 
94*c8dee2aaSAndroid Build Coastguard Worker #define ASSERT_SINGLE_OWNER SkASSERT(fRecorder); SKGPU_ASSERT_SINGLE_OWNER(fRecorder->singleOwner())
95*c8dee2aaSAndroid Build Coastguard Worker 
96*c8dee2aaSAndroid Build Coastguard Worker namespace {
97*c8dee2aaSAndroid Build Coastguard Worker 
DefaultFillStyle()98*c8dee2aaSAndroid Build Coastguard Worker const SkStrokeRec& DefaultFillStyle() {
99*c8dee2aaSAndroid Build Coastguard Worker     static const SkStrokeRec kFillStyle(SkStrokeRec::kFill_InitStyle);
100*c8dee2aaSAndroid Build Coastguard Worker     return kFillStyle;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker 
blender_depends_on_dst(const SkBlender * blender,bool srcIsTransparent)103*c8dee2aaSAndroid Build Coastguard Worker bool blender_depends_on_dst(const SkBlender* blender, bool srcIsTransparent) {
104*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SkBlendMode> bm = blender ? as_BB(blender)->asBlendMode() : SkBlendMode::kSrcOver;
105*c8dee2aaSAndroid Build Coastguard Worker     if (!bm.has_value()) {
106*c8dee2aaSAndroid Build Coastguard Worker         return true;
107*c8dee2aaSAndroid Build Coastguard Worker     }
108*c8dee2aaSAndroid Build Coastguard Worker     if (bm.value() == SkBlendMode::kSrc || bm.value() == SkBlendMode::kClear) {
109*c8dee2aaSAndroid Build Coastguard Worker         // src and clear blending never depends on dst
110*c8dee2aaSAndroid Build Coastguard Worker         return false;
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker     if (bm.value() == SkBlendMode::kSrcOver) {
113*c8dee2aaSAndroid Build Coastguard Worker         // src-over depends on dst if src is transparent (a != 1)
114*c8dee2aaSAndroid Build Coastguard Worker         return srcIsTransparent;
115*c8dee2aaSAndroid Build Coastguard Worker     }
116*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Are their other modes that don't depend on dst that can be trivially detected?
117*c8dee2aaSAndroid Build Coastguard Worker     return true;
118*c8dee2aaSAndroid Build Coastguard Worker }
119*c8dee2aaSAndroid Build Coastguard Worker 
paint_depends_on_dst(SkColor4f color,const SkShader * shader,const SkColorFilter * colorFilter,const SkBlender * finalBlender,const SkBlender * primitiveBlender)120*c8dee2aaSAndroid Build Coastguard Worker bool paint_depends_on_dst(SkColor4f color,
121*c8dee2aaSAndroid Build Coastguard Worker                           const SkShader* shader,
122*c8dee2aaSAndroid Build Coastguard Worker                           const SkColorFilter* colorFilter,
123*c8dee2aaSAndroid Build Coastguard Worker                           const SkBlender* finalBlender,
124*c8dee2aaSAndroid Build Coastguard Worker                           const SkBlender* primitiveBlender) {
125*c8dee2aaSAndroid Build Coastguard Worker     const bool srcIsTransparent = !color.isOpaque() || (shader && !shader->isOpaque()) ||
126*c8dee2aaSAndroid Build Coastguard Worker                                   (colorFilter && !colorFilter->isAlphaUnchanged());
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker     if (primitiveBlender && blender_depends_on_dst(primitiveBlender, srcIsTransparent)) {
129*c8dee2aaSAndroid Build Coastguard Worker         return true;
130*c8dee2aaSAndroid Build Coastguard Worker     }
131*c8dee2aaSAndroid Build Coastguard Worker 
132*c8dee2aaSAndroid Build Coastguard Worker     return blender_depends_on_dst(finalBlender, srcIsTransparent);
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker 
paint_depends_on_dst(const PaintParams & paintParams)135*c8dee2aaSAndroid Build Coastguard Worker bool paint_depends_on_dst(const PaintParams& paintParams) {
136*c8dee2aaSAndroid Build Coastguard Worker     return paint_depends_on_dst(paintParams.color(),
137*c8dee2aaSAndroid Build Coastguard Worker                                 paintParams.shader(),
138*c8dee2aaSAndroid Build Coastguard Worker                                 paintParams.colorFilter(),
139*c8dee2aaSAndroid Build Coastguard Worker                                 paintParams.finalBlender(),
140*c8dee2aaSAndroid Build Coastguard Worker                                 paintParams.primitiveBlender());
141*c8dee2aaSAndroid Build Coastguard Worker }
142*c8dee2aaSAndroid Build Coastguard Worker 
paint_depends_on_dst(const SkPaint & paint)143*c8dee2aaSAndroid Build Coastguard Worker bool paint_depends_on_dst(const SkPaint& paint) {
144*c8dee2aaSAndroid Build Coastguard Worker     // CAUTION: getMaskFilter is intentionally ignored here.
145*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!paint.getImageFilter());  // no paints in SkDevice should have an image filter
146*c8dee2aaSAndroid Build Coastguard Worker     return paint_depends_on_dst(paint.getColor4f(),
147*c8dee2aaSAndroid Build Coastguard Worker                                 paint.getShader(),
148*c8dee2aaSAndroid Build Coastguard Worker                                 paint.getColorFilter(),
149*c8dee2aaSAndroid Build Coastguard Worker                                 paint.getBlender(),
150*c8dee2aaSAndroid Build Coastguard Worker                                 /*primitiveBlender=*/nullptr);
151*c8dee2aaSAndroid Build Coastguard Worker }
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker /** If the paint can be reduced to a solid flood-fill, determine the correct color to fill with. */
extract_paint_color(const SkPaint & paint,const SkColorInfo & dstColorInfo)154*c8dee2aaSAndroid Build Coastguard Worker std::optional<SkColor4f> extract_paint_color(const SkPaint& paint,
155*c8dee2aaSAndroid Build Coastguard Worker                                              const SkColorInfo& dstColorInfo) {
156*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!paint_depends_on_dst(paint));
157*c8dee2aaSAndroid Build Coastguard Worker     if (paint.getShader()) {
158*c8dee2aaSAndroid Build Coastguard Worker         return std::nullopt;
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker     SkColor4f dstPaintColor = PaintParams::Color4fPrepForDst(paint.getColor4f(), dstColorInfo);
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     if (SkColorFilter* filter = paint.getColorFilter()) {
164*c8dee2aaSAndroid Build Coastguard Worker         SkColorSpace* dstCS = dstColorInfo.colorSpace();
165*c8dee2aaSAndroid Build Coastguard Worker         return filter->filterColor4f(dstPaintColor, dstCS, dstCS);
166*c8dee2aaSAndroid Build Coastguard Worker     }
167*c8dee2aaSAndroid Build Coastguard Worker     return dstPaintColor;
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker 
170*c8dee2aaSAndroid Build Coastguard Worker // Returns a local rect that has been adjusted such that when it's rasterized with `localToDevice`
171*c8dee2aaSAndroid Build Coastguard Worker // it will be pixel aligned. If this adjustment is not possible (due to transform type or precision)
172*c8dee2aaSAndroid Build Coastguard Worker // then this returns the original local rect unmodified.
173*c8dee2aaSAndroid Build Coastguard Worker //
174*c8dee2aaSAndroid Build Coastguard Worker // If `strokeWidth` is null, it's assumed to be a filled rectangle. If it's not null, on input it
175*c8dee2aaSAndroid Build Coastguard Worker // should hold the stroke width (or 0 for a hairline). After this returns, the stroke width may
176*c8dee2aaSAndroid Build Coastguard Worker // have been adjusted so that outer and inner stroked edges are pixel aligned (in which case the
177*c8dee2aaSAndroid Build Coastguard Worker // underlying rectangle geometry probably won't be pixel aligned).
178*c8dee2aaSAndroid Build Coastguard Worker //
179*c8dee2aaSAndroid Build Coastguard Worker // A best effort is made to align the stroke edges when there's a non-uniform scale factor that
180*c8dee2aaSAndroid Build Coastguard Worker // prevents exactly aligning both X and Y axes.
snap_rect_to_pixels(const Transform & localToDevice,const Rect & rect,float * strokeWidth=nullptr)181*c8dee2aaSAndroid Build Coastguard Worker Rect snap_rect_to_pixels(const Transform& localToDevice,
182*c8dee2aaSAndroid Build Coastguard Worker                          const Rect& rect,
183*c8dee2aaSAndroid Build Coastguard Worker                          float* strokeWidth=nullptr) {
184*c8dee2aaSAndroid Build Coastguard Worker     if (localToDevice.type() > Transform::Type::kRectStaysRect) {
185*c8dee2aaSAndroid Build Coastguard Worker         return rect;
186*c8dee2aaSAndroid Build Coastguard Worker     }
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker     Rect snappedDeviceRect;
189*c8dee2aaSAndroid Build Coastguard Worker     if (!strokeWidth) {
190*c8dee2aaSAndroid Build Coastguard Worker         // Just a fill, use round() to emulate non-AA rasterization (vs. roundOut() to get the
191*c8dee2aaSAndroid Build Coastguard Worker         // covering bounds). This matches how ClipStack treats clipRects with PixelSnapping::kYes.
192*c8dee2aaSAndroid Build Coastguard Worker         snappedDeviceRect = localToDevice.mapRect(rect).round();
193*c8dee2aaSAndroid Build Coastguard Worker     } else if (strokeWidth) {
194*c8dee2aaSAndroid Build Coastguard Worker         if (*strokeWidth == 0.f) {
195*c8dee2aaSAndroid Build Coastguard Worker             // Hairline case needs to be outset by 1/2 device pixels *before* rounding, and then
196*c8dee2aaSAndroid Build Coastguard Worker             // inset by 1/2px to get the base shape while leaving the stroke width as 0.
197*c8dee2aaSAndroid Build Coastguard Worker             snappedDeviceRect = localToDevice.mapRect(rect);
198*c8dee2aaSAndroid Build Coastguard Worker             snappedDeviceRect.outset(0.5f).round().inset(0.5f);
199*c8dee2aaSAndroid Build Coastguard Worker         } else {
200*c8dee2aaSAndroid Build Coastguard Worker             // For regular strokes, outset by the stroke radius *before* mapping to device space,
201*c8dee2aaSAndroid Build Coastguard Worker             // and then round.
202*c8dee2aaSAndroid Build Coastguard Worker             snappedDeviceRect = localToDevice.mapRect(rect.makeOutset(0.5f*(*strokeWidth))).round();
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker             // devScales.x() holds scale factor affecting device-space X axis (so max of |m00| or
205*c8dee2aaSAndroid Build Coastguard Worker             // |m01|) and y() holds the device Y axis scale (max of |m10| or |m11|).
206*c8dee2aaSAndroid Build Coastguard Worker             skvx::float2 devScales = max(abs(skvx::float2(localToDevice.matrix().rc(0,0),
207*c8dee2aaSAndroid Build Coastguard Worker                                                           localToDevice.matrix().rc(1,0))),
208*c8dee2aaSAndroid Build Coastguard Worker                                          abs(skvx::float2(localToDevice.matrix().rc(0,1),
209*c8dee2aaSAndroid Build Coastguard Worker                                                           localToDevice.matrix().rc(1,1))));
210*c8dee2aaSAndroid Build Coastguard Worker             skvx::float2 devStrokeWidth = max(round(*strokeWidth * devScales), 1.f);
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker             // Prioritize the axis that has the largest device-space radius (any error from a
213*c8dee2aaSAndroid Build Coastguard Worker             // non-uniform scale factor will go into the inner edge of the opposite axis).
214*c8dee2aaSAndroid Build Coastguard Worker             // During animating scale factors, preserving the large axis leads to better behavior.
215*c8dee2aaSAndroid Build Coastguard Worker             if (devStrokeWidth.x() > devStrokeWidth.y()) {
216*c8dee2aaSAndroid Build Coastguard Worker                 *strokeWidth = devStrokeWidth.x() / devScales.x();
217*c8dee2aaSAndroid Build Coastguard Worker             } else {
218*c8dee2aaSAndroid Build Coastguard Worker                 *strokeWidth = devStrokeWidth.y() / devScales.y();
219*c8dee2aaSAndroid Build Coastguard Worker             }
220*c8dee2aaSAndroid Build Coastguard Worker 
221*c8dee2aaSAndroid Build Coastguard Worker             snappedDeviceRect.inset(0.5f * devScales * (*strokeWidth));
222*c8dee2aaSAndroid Build Coastguard Worker         }
223*c8dee2aaSAndroid Build Coastguard Worker     }
224*c8dee2aaSAndroid Build Coastguard Worker 
225*c8dee2aaSAndroid Build Coastguard Worker     // Map back to local space so that it can be drawn with appropriate coord interpolation.
226*c8dee2aaSAndroid Build Coastguard Worker     Rect snappedLocalRect = localToDevice.inverseMapRect(snappedDeviceRect);
227*c8dee2aaSAndroid Build Coastguard Worker     // If the transform has an extreme scale factor or large translation, it's possible for floating
228*c8dee2aaSAndroid Build Coastguard Worker     // point precision to round `snappedLocalRect` in such a way that re-transforming it by the
229*c8dee2aaSAndroid Build Coastguard Worker     // local-to-device matrix no longer matches the expected device bounds.
230*c8dee2aaSAndroid Build Coastguard Worker     if (snappedDeviceRect.nearlyEquals(localToDevice.mapRect(snappedLocalRect))) {
231*c8dee2aaSAndroid Build Coastguard Worker         return snappedLocalRect;
232*c8dee2aaSAndroid Build Coastguard Worker     } else {
233*c8dee2aaSAndroid Build Coastguard Worker         // In this case we will just return the original geometry and the pixels will show
234*c8dee2aaSAndroid Build Coastguard Worker         // fractional coverage.
235*c8dee2aaSAndroid Build Coastguard Worker         return rect;
236*c8dee2aaSAndroid Build Coastguard Worker     }
237*c8dee2aaSAndroid Build Coastguard Worker }
238*c8dee2aaSAndroid Build Coastguard Worker 
239*c8dee2aaSAndroid Build Coastguard Worker // If possible, snaps `dstRect` such that its device-space transformation lands on pixel bounds,
240*c8dee2aaSAndroid Build Coastguard Worker // and then updates `srcRect` to match the original src-to-dst coordinate mapping.
snap_src_and_dst_rect_to_pixels(const Transform & localToDevice,SkRect * srcRect,SkRect * dstRect)241*c8dee2aaSAndroid Build Coastguard Worker void snap_src_and_dst_rect_to_pixels(const Transform& localToDevice,
242*c8dee2aaSAndroid Build Coastguard Worker                                      SkRect* srcRect,
243*c8dee2aaSAndroid Build Coastguard Worker                                      SkRect* dstRect) {
244*c8dee2aaSAndroid Build Coastguard Worker     if (localToDevice.type() > Transform::Type::kRectStaysRect) {
245*c8dee2aaSAndroid Build Coastguard Worker         return;
246*c8dee2aaSAndroid Build Coastguard Worker     }
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker     // Assume snapping will succeed and always update 'src' to match; in the event snapping
249*c8dee2aaSAndroid Build Coastguard Worker     // returns the original dst rect, then the recalculated src rect is a no-op.
250*c8dee2aaSAndroid Build Coastguard Worker     SkMatrix dstToSrc = SkMatrix::RectToRect(*dstRect, *srcRect);
251*c8dee2aaSAndroid Build Coastguard Worker     *dstRect = snap_rect_to_pixels(localToDevice, *dstRect).asSkRect();
252*c8dee2aaSAndroid Build Coastguard Worker     *srcRect = dstToSrc.mapRect(*dstRect);
253*c8dee2aaSAndroid Build Coastguard Worker }
254*c8dee2aaSAndroid Build Coastguard Worker 
255*c8dee2aaSAndroid Build Coastguard Worker // Returns the inner bounds of `geometry` that is known to have full coverage. This does not worry
256*c8dee2aaSAndroid Build Coastguard Worker // about identifying draws that are equivalent pixel aligned and thus entirely full coverage, as
257*c8dee2aaSAndroid Build Coastguard Worker // that should have been caught earlier and used a coverage-less renderer from the beginning.
258*c8dee2aaSAndroid Build Coastguard Worker //
259*c8dee2aaSAndroid Build Coastguard Worker // An empty Rect is returned if there is no available inner bounds, or if it's not worth performing.
get_inner_bounds(const Geometry & geometry,const Transform & localToDevice)260*c8dee2aaSAndroid Build Coastguard Worker Rect get_inner_bounds(const Geometry& geometry, const Transform& localToDevice) {
261*c8dee2aaSAndroid Build Coastguard Worker     auto applyAAInset = [&](Rect rect) {
262*c8dee2aaSAndroid Build Coastguard Worker         // If the aa inset is too large, rect becomes empty and the inner bounds draw is
263*c8dee2aaSAndroid Build Coastguard Worker         // automatically skipped
264*c8dee2aaSAndroid Build Coastguard Worker         float aaInset = localToDevice.localAARadius(rect);
265*c8dee2aaSAndroid Build Coastguard Worker         rect.inset(aaInset);
266*c8dee2aaSAndroid Build Coastguard Worker         // Only add a second draw if it will have a reasonable number of covered pixels; otherwise
267*c8dee2aaSAndroid Build Coastguard Worker         // we are just adding draws to sort and pipelines to switch around.
268*c8dee2aaSAndroid Build Coastguard Worker         static constexpr float kInnerFillArea = 64*64;
269*c8dee2aaSAndroid Build Coastguard Worker         // Approximate the device-space area based on the minimum scale factor of the transform.
270*c8dee2aaSAndroid Build Coastguard Worker         float scaleFactor = sk_ieee_float_divide(1.f, aaInset);
271*c8dee2aaSAndroid Build Coastguard Worker         return scaleFactor*rect.area() >= kInnerFillArea ? rect : Rect::InfiniteInverted();
272*c8dee2aaSAndroid Build Coastguard Worker     };
273*c8dee2aaSAndroid Build Coastguard Worker 
274*c8dee2aaSAndroid Build Coastguard Worker     if (geometry.isEdgeAAQuad()) {
275*c8dee2aaSAndroid Build Coastguard Worker         const EdgeAAQuad& quad = geometry.edgeAAQuad();
276*c8dee2aaSAndroid Build Coastguard Worker         if (quad.isRect()) {
277*c8dee2aaSAndroid Build Coastguard Worker             return applyAAInset(quad.bounds());
278*c8dee2aaSAndroid Build Coastguard Worker         }
279*c8dee2aaSAndroid Build Coastguard Worker         // else currently we don't have a function to calculate the largest interior axis aligned
280*c8dee2aaSAndroid Build Coastguard Worker         // bounding box of a quadrilateral so skip the inner fill draw.
281*c8dee2aaSAndroid Build Coastguard Worker     } else if (geometry.isShape()) {
282*c8dee2aaSAndroid Build Coastguard Worker         const Shape& shape = geometry.shape();
283*c8dee2aaSAndroid Build Coastguard Worker         if (shape.isRect()) {
284*c8dee2aaSAndroid Build Coastguard Worker             return applyAAInset(shape.rect());
285*c8dee2aaSAndroid Build Coastguard Worker         } else if (shape.isRRect()) {
286*c8dee2aaSAndroid Build Coastguard Worker             return applyAAInset(SkRRectPriv::InnerBounds(shape.rrect()));
287*c8dee2aaSAndroid Build Coastguard Worker         }
288*c8dee2aaSAndroid Build Coastguard Worker     }
289*c8dee2aaSAndroid Build Coastguard Worker 
290*c8dee2aaSAndroid Build Coastguard Worker     return Rect::InfiniteInverted();
291*c8dee2aaSAndroid Build Coastguard Worker }
292*c8dee2aaSAndroid Build Coastguard Worker 
rect_to_pixelbounds(const Rect & r)293*c8dee2aaSAndroid Build Coastguard Worker SkIRect rect_to_pixelbounds(const Rect& r) {
294*c8dee2aaSAndroid Build Coastguard Worker     return r.makeRoundOut().asSkIRect();
295*c8dee2aaSAndroid Build Coastguard Worker }
296*c8dee2aaSAndroid Build Coastguard Worker 
is_simple_shape(const Shape & shape,SkStrokeRec::Style type)297*c8dee2aaSAndroid Build Coastguard Worker bool is_simple_shape(const Shape& shape, SkStrokeRec::Style type) {
298*c8dee2aaSAndroid Build Coastguard Worker     // We send regular filled and hairline [round] rectangles, stroked/hairline lines, and stroked
299*c8dee2aaSAndroid Build Coastguard Worker     // [r]rects with circular corners to a single Renderer that does not trigger MSAA.
300*c8dee2aaSAndroid Build Coastguard Worker     // Per-edge AA quadrilaterals also use the same Renderer but those are not "Shapes".
301*c8dee2aaSAndroid Build Coastguard Worker     // These shapes and quads may also be combined with a second non-AA inner fill. This fill step
302*c8dee2aaSAndroid Build Coastguard Worker     // is also directly used for flooding the clip
303*c8dee2aaSAndroid Build Coastguard Worker     return (shape.isEmpty() && shape.inverted()) ||
304*c8dee2aaSAndroid Build Coastguard Worker            (!shape.inverted() && type != SkStrokeRec::kStrokeAndFill_Style &&
305*c8dee2aaSAndroid Build Coastguard Worker             (shape.isRect() ||
306*c8dee2aaSAndroid Build Coastguard Worker              (shape.isLine() && type != SkStrokeRec::kFill_Style) ||
307*c8dee2aaSAndroid Build Coastguard Worker              (shape.isRRect() && (type != SkStrokeRec::kStroke_Style ||
308*c8dee2aaSAndroid Build Coastguard Worker                                   SkRRectPriv::AllCornersCircular(shape.rrect())))));
309*c8dee2aaSAndroid Build Coastguard Worker }
310*c8dee2aaSAndroid Build Coastguard Worker 
use_compute_atlas_when_available(PathRendererStrategy strategy)311*c8dee2aaSAndroid Build Coastguard Worker bool use_compute_atlas_when_available(PathRendererStrategy strategy) {
312*c8dee2aaSAndroid Build Coastguard Worker     return strategy == PathRendererStrategy::kComputeAnalyticAA ||
313*c8dee2aaSAndroid Build Coastguard Worker            strategy == PathRendererStrategy::kComputeMSAA16 ||
314*c8dee2aaSAndroid Build Coastguard Worker            strategy == PathRendererStrategy::kComputeMSAA8 ||
315*c8dee2aaSAndroid Build Coastguard Worker            strategy == PathRendererStrategy::kDefault;
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
319*c8dee2aaSAndroid Build Coastguard Worker 
320*c8dee2aaSAndroid Build Coastguard Worker /**
321*c8dee2aaSAndroid Build Coastguard Worker  * IntersectionTreeSet controls multiple IntersectionTrees to organize all add rectangles into
322*c8dee2aaSAndroid Build Coastguard Worker  * disjoint sets. For a given CompressedPaintersOrder and bounds, it returns the smallest
323*c8dee2aaSAndroid Build Coastguard Worker  * DisjointStencilIndex that guarantees the bounds are disjoint from all other draws that use the
324*c8dee2aaSAndroid Build Coastguard Worker  * same painters order and stencil index.
325*c8dee2aaSAndroid Build Coastguard Worker  */
326*c8dee2aaSAndroid Build Coastguard Worker class Device::IntersectionTreeSet {
327*c8dee2aaSAndroid Build Coastguard Worker public:
328*c8dee2aaSAndroid Build Coastguard Worker     IntersectionTreeSet() = default;
329*c8dee2aaSAndroid Build Coastguard Worker 
add(CompressedPaintersOrder drawOrder,Rect rect)330*c8dee2aaSAndroid Build Coastguard Worker     DisjointStencilIndex add(CompressedPaintersOrder drawOrder, Rect rect) {
331*c8dee2aaSAndroid Build Coastguard Worker         auto& trees = fTrees[drawOrder];
332*c8dee2aaSAndroid Build Coastguard Worker         DisjointStencilIndex stencil = DrawOrder::kUnassigned.next();
333*c8dee2aaSAndroid Build Coastguard Worker         for (auto&& tree : trees) {
334*c8dee2aaSAndroid Build Coastguard Worker             if (tree->add(rect)) {
335*c8dee2aaSAndroid Build Coastguard Worker                 return stencil;
336*c8dee2aaSAndroid Build Coastguard Worker             }
337*c8dee2aaSAndroid Build Coastguard Worker             stencil = stencil.next(); // advance to the next tree's index
338*c8dee2aaSAndroid Build Coastguard Worker         }
339*c8dee2aaSAndroid Build Coastguard Worker 
340*c8dee2aaSAndroid Build Coastguard Worker         // If here, no existing intersection tree can hold the rect so add a new one
341*c8dee2aaSAndroid Build Coastguard Worker         IntersectionTree* newTree = this->makeTree();
342*c8dee2aaSAndroid Build Coastguard Worker         SkAssertResult(newTree->add(rect));
343*c8dee2aaSAndroid Build Coastguard Worker         trees.push_back(newTree);
344*c8dee2aaSAndroid Build Coastguard Worker         return stencil;
345*c8dee2aaSAndroid Build Coastguard Worker     }
346*c8dee2aaSAndroid Build Coastguard Worker 
reset()347*c8dee2aaSAndroid Build Coastguard Worker     void reset() {
348*c8dee2aaSAndroid Build Coastguard Worker         fTrees.clear();
349*c8dee2aaSAndroid Build Coastguard Worker         fTreeStore.reset();
350*c8dee2aaSAndroid Build Coastguard Worker     }
351*c8dee2aaSAndroid Build Coastguard Worker 
352*c8dee2aaSAndroid Build Coastguard Worker private:
353*c8dee2aaSAndroid Build Coastguard Worker     struct Hash {
operator ()skgpu::graphite::Device::IntersectionTreeSet::Hash354*c8dee2aaSAndroid Build Coastguard Worker         size_t operator()(const CompressedPaintersOrder& o) const noexcept { return o.bits(); }
355*c8dee2aaSAndroid Build Coastguard Worker     };
356*c8dee2aaSAndroid Build Coastguard Worker 
makeTree()357*c8dee2aaSAndroid Build Coastguard Worker     IntersectionTree* makeTree() {
358*c8dee2aaSAndroid Build Coastguard Worker         return fTreeStore.make<IntersectionTree>();
359*c8dee2aaSAndroid Build Coastguard Worker     }
360*c8dee2aaSAndroid Build Coastguard Worker 
361*c8dee2aaSAndroid Build Coastguard Worker     // Each compressed painters order defines a barrier around draws so each order's set of draws
362*c8dee2aaSAndroid Build Coastguard Worker     // are independent, even if they may intersect. Within each order, the list of trees holds the
363*c8dee2aaSAndroid Build Coastguard Worker     // IntersectionTrees representing each disjoint set.
364*c8dee2aaSAndroid Build Coastguard Worker     // TODO: This organization of trees is logically convenient but may need to be optimized based
365*c8dee2aaSAndroid Build Coastguard Worker     // on real world data (e.g. how sparse is the map, how long is each vector of trees,...)
366*c8dee2aaSAndroid Build Coastguard Worker     std::unordered_map<CompressedPaintersOrder, std::vector<IntersectionTree*>, Hash> fTrees;
367*c8dee2aaSAndroid Build Coastguard Worker     SkSTArenaAllocWithReset<4 * sizeof(IntersectionTree)> fTreeStore;
368*c8dee2aaSAndroid Build Coastguard Worker };
369*c8dee2aaSAndroid Build Coastguard Worker 
Make(Recorder * recorder,const SkImageInfo & ii,skgpu::Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,const SkSurfaceProps & props,LoadOp initialLoadOp,std::string_view label,bool registerWithRecorder)370*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Device> Device::Make(Recorder* recorder,
371*c8dee2aaSAndroid Build Coastguard Worker                            const SkImageInfo& ii,
372*c8dee2aaSAndroid Build Coastguard Worker                            skgpu::Budgeted budgeted,
373*c8dee2aaSAndroid Build Coastguard Worker                            Mipmapped mipmapped,
374*c8dee2aaSAndroid Build Coastguard Worker                            SkBackingFit backingFit,
375*c8dee2aaSAndroid Build Coastguard Worker                            const SkSurfaceProps& props,
376*c8dee2aaSAndroid Build Coastguard Worker                            LoadOp initialLoadOp,
377*c8dee2aaSAndroid Build Coastguard Worker                            std::string_view label,
378*c8dee2aaSAndroid Build Coastguard Worker                            bool registerWithRecorder) {
379*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
380*c8dee2aaSAndroid Build Coastguard Worker     if (!recorder) {
381*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
382*c8dee2aaSAndroid Build Coastguard Worker     }
383*c8dee2aaSAndroid Build Coastguard Worker 
384*c8dee2aaSAndroid Build Coastguard Worker     const Caps* caps = recorder->priv().caps();
385*c8dee2aaSAndroid Build Coastguard Worker     SkISize backingDimensions = backingFit == SkBackingFit::kApprox ? GetApproxSize(ii.dimensions())
386*c8dee2aaSAndroid Build Coastguard Worker                                                                     : ii.dimensions();
387*c8dee2aaSAndroid Build Coastguard Worker     auto textureInfo = caps->getDefaultSampledTextureInfo(ii.colorType(),
388*c8dee2aaSAndroid Build Coastguard Worker                                                           mipmapped,
389*c8dee2aaSAndroid Build Coastguard Worker                                                           recorder->priv().isProtected(),
390*c8dee2aaSAndroid Build Coastguard Worker                                                           Renderable::kYes);
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker     return Make(recorder,
393*c8dee2aaSAndroid Build Coastguard Worker                 TextureProxy::Make(caps, recorder->priv().resourceProvider(),
394*c8dee2aaSAndroid Build Coastguard Worker                                    backingDimensions, textureInfo, std::move(label), budgeted),
395*c8dee2aaSAndroid Build Coastguard Worker                 ii.dimensions(),
396*c8dee2aaSAndroid Build Coastguard Worker                 ii.colorInfo(),
397*c8dee2aaSAndroid Build Coastguard Worker                 props,
398*c8dee2aaSAndroid Build Coastguard Worker                 initialLoadOp,
399*c8dee2aaSAndroid Build Coastguard Worker                 registerWithRecorder);
400*c8dee2aaSAndroid Build Coastguard Worker }
401*c8dee2aaSAndroid Build Coastguard Worker 
Make(Recorder * recorder,sk_sp<TextureProxy> target,SkISize deviceSize,const SkColorInfo & colorInfo,const SkSurfaceProps & props,LoadOp initialLoadOp,bool registerWithRecorder)402*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Device> Device::Make(Recorder* recorder,
403*c8dee2aaSAndroid Build Coastguard Worker                            sk_sp<TextureProxy> target,
404*c8dee2aaSAndroid Build Coastguard Worker                            SkISize deviceSize,
405*c8dee2aaSAndroid Build Coastguard Worker                            const SkColorInfo& colorInfo,
406*c8dee2aaSAndroid Build Coastguard Worker                            const SkSurfaceProps& props,
407*c8dee2aaSAndroid Build Coastguard Worker                            LoadOp initialLoadOp,
408*c8dee2aaSAndroid Build Coastguard Worker                            bool registerWithRecorder) {
409*c8dee2aaSAndroid Build Coastguard Worker     if (!recorder) {
410*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
411*c8dee2aaSAndroid Build Coastguard Worker     }
412*c8dee2aaSAndroid Build Coastguard Worker 
413*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<DrawContext> dc = DrawContext::Make(recorder->priv().caps(),
414*c8dee2aaSAndroid Build Coastguard Worker                                               std::move(target),
415*c8dee2aaSAndroid Build Coastguard Worker                                               deviceSize,
416*c8dee2aaSAndroid Build Coastguard Worker                                               colorInfo,
417*c8dee2aaSAndroid Build Coastguard Worker                                               props);
418*c8dee2aaSAndroid Build Coastguard Worker     if (!dc) {
419*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
420*c8dee2aaSAndroid Build Coastguard Worker     } else if (initialLoadOp == LoadOp::kClear) {
421*c8dee2aaSAndroid Build Coastguard Worker         dc->clear(SkColors::kTransparent);
422*c8dee2aaSAndroid Build Coastguard Worker     } else if (initialLoadOp == LoadOp::kDiscard) {
423*c8dee2aaSAndroid Build Coastguard Worker         dc->discard();
424*c8dee2aaSAndroid Build Coastguard Worker     } // else kLoad is the default initial op for a DrawContext
425*c8dee2aaSAndroid Build Coastguard Worker 
426*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<Device> device{new Device(recorder, std::move(dc))};
427*c8dee2aaSAndroid Build Coastguard Worker     if (registerWithRecorder) {
428*c8dee2aaSAndroid Build Coastguard Worker         // We don't register the device with the recorder until after the constructor has returned.
429*c8dee2aaSAndroid Build Coastguard Worker         recorder->registerDevice(device);
430*c8dee2aaSAndroid Build Coastguard Worker     } else {
431*c8dee2aaSAndroid Build Coastguard Worker         // Since it's not registered, it should go out of scope before nextRecordingID() changes
432*c8dee2aaSAndroid Build Coastguard Worker         // from what is saved to fScopedRecordingID.
433*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE(device->fScopedRecordingID = recorder->priv().nextRecordingID();)
434*c8dee2aaSAndroid Build Coastguard Worker     }
435*c8dee2aaSAndroid Build Coastguard Worker     return device;
436*c8dee2aaSAndroid Build Coastguard Worker }
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker // These default tuning numbers for the HybridBoundsManager were chosen from looking at performance
439*c8dee2aaSAndroid Build Coastguard Worker // and accuracy curves produced by the BoundsManagerBench for random draw bounding boxes. This
440*c8dee2aaSAndroid Build Coastguard Worker // config will use brute force for the first 64 draw calls to the Device and then switch to a grid
441*c8dee2aaSAndroid Build Coastguard Worker // that is dynamically sized to produce cells that are 16x16, up to a grid that's 32x32 cells.
442*c8dee2aaSAndroid Build Coastguard Worker // This seemed like a sweet spot balancing accuracy for low-draw count surfaces and overhead for
443*c8dee2aaSAndroid Build Coastguard Worker // high-draw count and high-resolution surfaces. With the 32x32 grid limit, cell size will increase
444*c8dee2aaSAndroid Build Coastguard Worker // above 16px when the surface dimension goes above 512px.
445*c8dee2aaSAndroid Build Coastguard Worker // TODO: These could be exposed as context options or surface options, and we may want to have
446*c8dee2aaSAndroid Build Coastguard Worker // different strategies in place for a base device vs. a layer's device.
447*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kGridCellSize = 16;
448*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxBruteForceN = 64;
449*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kMaxGridSize = 32;
450*c8dee2aaSAndroid Build Coastguard Worker 
Device(Recorder * recorder,sk_sp<DrawContext> dc)451*c8dee2aaSAndroid Build Coastguard Worker Device::Device(Recorder* recorder, sk_sp<DrawContext> dc)
452*c8dee2aaSAndroid Build Coastguard Worker         : SkDevice(dc->imageInfo(), dc->surfaceProps())
453*c8dee2aaSAndroid Build Coastguard Worker         , fRecorder(recorder)
454*c8dee2aaSAndroid Build Coastguard Worker         , fDC(std::move(dc))
455*c8dee2aaSAndroid Build Coastguard Worker         , fClip(this)
456*c8dee2aaSAndroid Build Coastguard Worker         , fColorDepthBoundsManager(std::make_unique<HybridBoundsManager>(
457*c8dee2aaSAndroid Build Coastguard Worker                   fDC->imageInfo().dimensions(), kGridCellSize, kMaxBruteForceN, kMaxGridSize))
458*c8dee2aaSAndroid Build Coastguard Worker         , fDisjointStencilSet(std::make_unique<IntersectionTreeSet>())
459*c8dee2aaSAndroid Build Coastguard Worker         , fCachedLocalToDevice(SkM44())
460*c8dee2aaSAndroid Build Coastguard Worker         , fCurrentDepth(DrawOrder::kClearDepth)
461*c8dee2aaSAndroid Build Coastguard Worker         , fSubRunControl(recorder->priv().caps()->getSubRunControl(
462*c8dee2aaSAndroid Build Coastguard Worker                 fDC->surfaceProps().isUseDeviceIndependentFonts())) {
463*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(SkToBool(fDC) && SkToBool(fRecorder));
464*c8dee2aaSAndroid Build Coastguard Worker     if (fRecorder->priv().caps()->defaultMSAASamplesCount() > 1) {
465*c8dee2aaSAndroid Build Coastguard Worker         if (fRecorder->priv().caps()->msaaRenderToSingleSampledSupport()) {
466*c8dee2aaSAndroid Build Coastguard Worker             fMSAASupported = true;
467*c8dee2aaSAndroid Build Coastguard Worker         } else {
468*c8dee2aaSAndroid Build Coastguard Worker             TextureInfo msaaTexInfo =
469*c8dee2aaSAndroid Build Coastguard Worker                    fRecorder->priv().caps()->getDefaultMSAATextureInfo(fDC->target()->textureInfo(),
470*c8dee2aaSAndroid Build Coastguard Worker                                                                        Discardable::kYes);
471*c8dee2aaSAndroid Build Coastguard Worker             fMSAASupported = msaaTexInfo.isValid();
472*c8dee2aaSAndroid Build Coastguard Worker         }
473*c8dee2aaSAndroid Build Coastguard Worker     }
474*c8dee2aaSAndroid Build Coastguard Worker }
475*c8dee2aaSAndroid Build Coastguard Worker 
~Device()476*c8dee2aaSAndroid Build Coastguard Worker Device::~Device() {
477*c8dee2aaSAndroid Build Coastguard Worker     // The Device should have been marked immutable before it's destroyed, or the Recorder was the
478*c8dee2aaSAndroid Build Coastguard Worker     // last holder of a reference to it and de-registered the device as part of its cleanup.
479*c8dee2aaSAndroid Build Coastguard Worker     // However, if the Device was not registered with the recorder (i.e. a scratch device) we don't
480*c8dee2aaSAndroid Build Coastguard Worker     // require that its recorder be adandoned. Scratch devices must either have been marked
481*c8dee2aaSAndroid Build Coastguard Worker     // immutable or be destroyed before the recorder has been snapped.
482*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fRecorder || fScopedRecordingID != 0);
483*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
484*c8dee2aaSAndroid Build Coastguard Worker     if (fScopedRecordingID != 0 && fRecorder) {
485*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fScopedRecordingID == fRecorder->priv().nextRecordingID());
486*c8dee2aaSAndroid Build Coastguard Worker     }
487*c8dee2aaSAndroid Build Coastguard Worker     // else it wasn't a scratch device, or it was a scratch device that was marked immutable so its
488*c8dee2aaSAndroid Build Coastguard Worker     // lifetime was validated when setImmutable() was called.
489*c8dee2aaSAndroid Build Coastguard Worker #endif
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker 
setImmutable()492*c8dee2aaSAndroid Build Coastguard Worker void Device::setImmutable() {
493*c8dee2aaSAndroid Build Coastguard Worker     if (fRecorder) {
494*c8dee2aaSAndroid Build Coastguard Worker         // Push any pending work to the Recorder now. setImmutable() is only called by the
495*c8dee2aaSAndroid Build Coastguard Worker         // destructor of a client-owned Surface, or explicitly in layer/filtering workflows. In
496*c8dee2aaSAndroid Build Coastguard Worker         // both cases this is restricted to the Recorder's thread. This is in contrast to ~Device(),
497*c8dee2aaSAndroid Build Coastguard Worker         // which might be called from another thread if it was linked to an Image used in multiple
498*c8dee2aaSAndroid Build Coastguard Worker         // recorders.
499*c8dee2aaSAndroid Build Coastguard Worker         this->flushPendingWorkToRecorder();
500*c8dee2aaSAndroid Build Coastguard Worker         fRecorder->deregisterDevice(this);
501*c8dee2aaSAndroid Build Coastguard Worker         // Abandoning the recorder ensures that there are no further operations that can be recorded
502*c8dee2aaSAndroid Build Coastguard Worker         // and is relied on by Image::notifyInUse() to detect when it can unlink from a Device.
503*c8dee2aaSAndroid Build Coastguard Worker         this->abandonRecorder();
504*c8dee2aaSAndroid Build Coastguard Worker     }
505*c8dee2aaSAndroid Build Coastguard Worker }
506*c8dee2aaSAndroid Build Coastguard Worker 
localToDeviceTransform()507*c8dee2aaSAndroid Build Coastguard Worker const Transform& Device::localToDeviceTransform() {
508*c8dee2aaSAndroid Build Coastguard Worker     if (this->checkLocalToDeviceDirty()) {
509*c8dee2aaSAndroid Build Coastguard Worker         fCachedLocalToDevice = Transform{this->localToDevice44()};
510*c8dee2aaSAndroid Build Coastguard Worker     }
511*c8dee2aaSAndroid Build Coastguard Worker     return fCachedLocalToDevice;
512*c8dee2aaSAndroid Build Coastguard Worker }
513*c8dee2aaSAndroid Build Coastguard Worker 
strikeDeviceInfo() const514*c8dee2aaSAndroid Build Coastguard Worker SkStrikeDeviceInfo Device::strikeDeviceInfo() const {
515*c8dee2aaSAndroid Build Coastguard Worker     return {this->surfaceProps(), this->scalerContextFlags(), &fSubRunControl};
516*c8dee2aaSAndroid Build Coastguard Worker }
517*c8dee2aaSAndroid Build Coastguard Worker 
createDevice(const CreateInfo & info,const SkPaint *)518*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkDevice> Device::createDevice(const CreateInfo& info, const SkPaint*) {
519*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Inspect the paint and create info to determine if there's anything that has to be
520*c8dee2aaSAndroid Build Coastguard Worker     // modified to support inline subpasses.
521*c8dee2aaSAndroid Build Coastguard Worker     SkSurfaceProps props =
522*c8dee2aaSAndroid Build Coastguard Worker         this->surfaceProps().cloneWithPixelGeometry(info.fPixelGeometry);
523*c8dee2aaSAndroid Build Coastguard Worker 
524*c8dee2aaSAndroid Build Coastguard Worker     // Skia's convention is to only clear a device if it is non-opaque.
525*c8dee2aaSAndroid Build Coastguard Worker     LoadOp initialLoadOp = info.fInfo.isOpaque() ? LoadOp::kDiscard : LoadOp::kClear;
526*c8dee2aaSAndroid Build Coastguard Worker 
527*c8dee2aaSAndroid Build Coastguard Worker     std::string label = this->target()->label();
528*c8dee2aaSAndroid Build Coastguard Worker     if (label.empty()) {
529*c8dee2aaSAndroid Build Coastguard Worker         label = "ChildDevice";
530*c8dee2aaSAndroid Build Coastguard Worker     } else {
531*c8dee2aaSAndroid Build Coastguard Worker         label += "_ChildDevice";
532*c8dee2aaSAndroid Build Coastguard Worker     }
533*c8dee2aaSAndroid Build Coastguard Worker 
534*c8dee2aaSAndroid Build Coastguard Worker     return Make(fRecorder,
535*c8dee2aaSAndroid Build Coastguard Worker                 info.fInfo,
536*c8dee2aaSAndroid Build Coastguard Worker                 skgpu::Budgeted::kYes,
537*c8dee2aaSAndroid Build Coastguard Worker                 Mipmapped::kNo,
538*c8dee2aaSAndroid Build Coastguard Worker                 SkBackingFit::kApprox,
539*c8dee2aaSAndroid Build Coastguard Worker                 props,
540*c8dee2aaSAndroid Build Coastguard Worker                 initialLoadOp,
541*c8dee2aaSAndroid Build Coastguard Worker                 label);
542*c8dee2aaSAndroid Build Coastguard Worker }
543*c8dee2aaSAndroid Build Coastguard Worker 
makeSurface(const SkImageInfo & ii,const SkSurfaceProps & props)544*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& ii, const SkSurfaceProps& props) {
545*c8dee2aaSAndroid Build Coastguard Worker     return SkSurfaces::RenderTarget(fRecorder, ii, Mipmapped::kNo, &props);
546*c8dee2aaSAndroid Build Coastguard Worker }
547*c8dee2aaSAndroid Build Coastguard Worker 
makeImageCopy(const SkIRect & subset,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit)548*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Image> Device::makeImageCopy(const SkIRect& subset,
549*c8dee2aaSAndroid Build Coastguard Worker                                    Budgeted budgeted,
550*c8dee2aaSAndroid Build Coastguard Worker                                    Mipmapped mipmapped,
551*c8dee2aaSAndroid Build Coastguard Worker                                    SkBackingFit backingFit) {
552*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
553*c8dee2aaSAndroid Build Coastguard Worker     this->flushPendingWorkToRecorder();
554*c8dee2aaSAndroid Build Coastguard Worker 
555*c8dee2aaSAndroid Build Coastguard Worker     const SkColorInfo& colorInfo = this->imageInfo().colorInfo();
556*c8dee2aaSAndroid Build Coastguard Worker     TextureProxyView srcView = this->readSurfaceView();
557*c8dee2aaSAndroid Build Coastguard Worker     if (!srcView) {
558*c8dee2aaSAndroid Build Coastguard Worker         // readSurfaceView() returns an empty view when the target is not texturable. Create an
559*c8dee2aaSAndroid Build Coastguard Worker         // equivalent view for the blitting operation.
560*c8dee2aaSAndroid Build Coastguard Worker         Swizzle readSwizzle = fRecorder->priv().caps()->getReadSwizzle(
561*c8dee2aaSAndroid Build Coastguard Worker                 colorInfo.colorType(), this->target()->textureInfo());
562*c8dee2aaSAndroid Build Coastguard Worker         srcView = {sk_ref_sp(this->target()), readSwizzle};
563*c8dee2aaSAndroid Build Coastguard Worker     }
564*c8dee2aaSAndroid Build Coastguard Worker     std::string label = this->target()->label();
565*c8dee2aaSAndroid Build Coastguard Worker     if (label.empty()) {
566*c8dee2aaSAndroid Build Coastguard Worker         label = "CopyDeviceTexture";
567*c8dee2aaSAndroid Build Coastguard Worker     } else {
568*c8dee2aaSAndroid Build Coastguard Worker         label += "_DeviceCopy";
569*c8dee2aaSAndroid Build Coastguard Worker     }
570*c8dee2aaSAndroid Build Coastguard Worker 
571*c8dee2aaSAndroid Build Coastguard Worker     return Image::Copy(fRecorder, srcView, colorInfo, subset, budgeted, mipmapped, backingFit,
572*c8dee2aaSAndroid Build Coastguard Worker                        label);
573*c8dee2aaSAndroid Build Coastguard Worker }
574*c8dee2aaSAndroid Build Coastguard Worker 
onReadPixels(const SkPixmap & pm,int srcX,int srcY)575*c8dee2aaSAndroid Build Coastguard Worker bool Device::onReadPixels(const SkPixmap& pm, int srcX, int srcY) {
576*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
577*c8dee2aaSAndroid Build Coastguard Worker     // This testing-only function should only be called before the Device has detached from its
578*c8dee2aaSAndroid Build Coastguard Worker     // Recorder, since it's accessed via the test-held Surface.
579*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
580*c8dee2aaSAndroid Build Coastguard Worker     if (Context* context = fRecorder->priv().context()) {
581*c8dee2aaSAndroid Build Coastguard Worker         // Add all previous commands generated to the command buffer.
582*c8dee2aaSAndroid Build Coastguard Worker         // If the client snaps later they'll only get post-read commands in their Recording,
583*c8dee2aaSAndroid Build Coastguard Worker         // but since they're doing a readPixels in the middle that shouldn't be unexpected.
584*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Recording> recording = fRecorder->snap();
585*c8dee2aaSAndroid Build Coastguard Worker         if (!recording) {
586*c8dee2aaSAndroid Build Coastguard Worker             return false;
587*c8dee2aaSAndroid Build Coastguard Worker         }
588*c8dee2aaSAndroid Build Coastguard Worker         InsertRecordingInfo info;
589*c8dee2aaSAndroid Build Coastguard Worker         info.fRecording = recording.get();
590*c8dee2aaSAndroid Build Coastguard Worker         if (!context->insertRecording(info)) {
591*c8dee2aaSAndroid Build Coastguard Worker             return false;
592*c8dee2aaSAndroid Build Coastguard Worker         }
593*c8dee2aaSAndroid Build Coastguard Worker         return context->priv().readPixels(pm, fDC->target(), this->imageInfo(), srcX, srcY);
594*c8dee2aaSAndroid Build Coastguard Worker     }
595*c8dee2aaSAndroid Build Coastguard Worker #endif
596*c8dee2aaSAndroid Build Coastguard Worker     // We have no access to a context to do a read pixels here.
597*c8dee2aaSAndroid Build Coastguard Worker     return false;
598*c8dee2aaSAndroid Build Coastguard Worker }
599*c8dee2aaSAndroid Build Coastguard Worker 
onWritePixels(const SkPixmap & src,int x,int y)600*c8dee2aaSAndroid Build Coastguard Worker bool Device::onWritePixels(const SkPixmap& src, int x, int y) {
601*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
602*c8dee2aaSAndroid Build Coastguard Worker     // TODO: we may need to share this in a more central place to handle uploads
603*c8dee2aaSAndroid Build Coastguard Worker     // to backend textures
604*c8dee2aaSAndroid Build Coastguard Worker 
605*c8dee2aaSAndroid Build Coastguard Worker     const TextureProxy* target = fDC->target();
606*c8dee2aaSAndroid Build Coastguard Worker 
607*c8dee2aaSAndroid Build Coastguard Worker     // TODO: add mipmap support for createBackendTexture
608*c8dee2aaSAndroid Build Coastguard Worker 
609*c8dee2aaSAndroid Build Coastguard Worker     if (src.colorType() == kUnknown_SkColorType) {
610*c8dee2aaSAndroid Build Coastguard Worker         return false;
611*c8dee2aaSAndroid Build Coastguard Worker     }
612*c8dee2aaSAndroid Build Coastguard Worker 
613*c8dee2aaSAndroid Build Coastguard Worker     // If one alpha type is unknown and the other isn't, it's too underspecified.
614*c8dee2aaSAndroid Build Coastguard Worker     if ((src.alphaType() == kUnknown_SkAlphaType) !=
615*c8dee2aaSAndroid Build Coastguard Worker         (this->imageInfo().alphaType() == kUnknown_SkAlphaType)) {
616*c8dee2aaSAndroid Build Coastguard Worker         return false;
617*c8dee2aaSAndroid Build Coastguard Worker     }
618*c8dee2aaSAndroid Build Coastguard Worker 
619*c8dee2aaSAndroid Build Coastguard Worker     // TODO: canvas2DFastPath?
620*c8dee2aaSAndroid Build Coastguard Worker 
621*c8dee2aaSAndroid Build Coastguard Worker     if (!fRecorder->priv().caps()->supportsWritePixels(target->textureInfo())) {
622*c8dee2aaSAndroid Build Coastguard Worker         auto image = SkImages::RasterFromPixmap(src, nullptr, nullptr);
623*c8dee2aaSAndroid Build Coastguard Worker         image = SkImages::TextureFromImage(fRecorder, image.get());
624*c8dee2aaSAndroid Build Coastguard Worker         if (!image) {
625*c8dee2aaSAndroid Build Coastguard Worker             return false;
626*c8dee2aaSAndroid Build Coastguard Worker         }
627*c8dee2aaSAndroid Build Coastguard Worker 
628*c8dee2aaSAndroid Build Coastguard Worker         SkPaint paint;
629*c8dee2aaSAndroid Build Coastguard Worker         paint.setBlendMode(SkBlendMode::kSrc);
630*c8dee2aaSAndroid Build Coastguard Worker         this->drawImageRect(image.get(),
631*c8dee2aaSAndroid Build Coastguard Worker                             /*src=*/nullptr,
632*c8dee2aaSAndroid Build Coastguard Worker                             SkRect::MakeXYWH(x, y, src.width(), src.height()),
633*c8dee2aaSAndroid Build Coastguard Worker                             SkFilterMode::kNearest,
634*c8dee2aaSAndroid Build Coastguard Worker                             paint,
635*c8dee2aaSAndroid Build Coastguard Worker                             SkCanvas::kFast_SrcRectConstraint);
636*c8dee2aaSAndroid Build Coastguard Worker         return true;
637*c8dee2aaSAndroid Build Coastguard Worker     }
638*c8dee2aaSAndroid Build Coastguard Worker 
639*c8dee2aaSAndroid Build Coastguard Worker     // TODO: check for flips and either handle here or pass info to UploadTask
640*c8dee2aaSAndroid Build Coastguard Worker 
641*c8dee2aaSAndroid Build Coastguard Worker     // Determine rect to copy
642*c8dee2aaSAndroid Build Coastguard Worker     SkIRect dstRect = SkIRect::MakePtSize({x, y}, src.dimensions());
643*c8dee2aaSAndroid Build Coastguard Worker     if (!target->isFullyLazy() && !dstRect.intersect(SkIRect::MakeSize(target->dimensions()))) {
644*c8dee2aaSAndroid Build Coastguard Worker         return false;
645*c8dee2aaSAndroid Build Coastguard Worker     }
646*c8dee2aaSAndroid Build Coastguard Worker 
647*c8dee2aaSAndroid Build Coastguard Worker     // Set up copy location
648*c8dee2aaSAndroid Build Coastguard Worker     const void* addr = src.addr(dstRect.fLeft - x, dstRect.fTop - y);
649*c8dee2aaSAndroid Build Coastguard Worker     std::vector<MipLevel> levels;
650*c8dee2aaSAndroid Build Coastguard Worker     levels.push_back({addr, src.rowBytes()});
651*c8dee2aaSAndroid Build Coastguard Worker 
652*c8dee2aaSAndroid Build Coastguard Worker     // The writePixels() still respects painter's order, so flush everything to tasks before this
653*c8dee2aaSAndroid Build Coastguard Worker     // recording the upload for the pixel data.
654*c8dee2aaSAndroid Build Coastguard Worker     this->internalFlush();
655*c8dee2aaSAndroid Build Coastguard Worker     // The new upload will be executed before any new draws are recorded and also ensures that
656*c8dee2aaSAndroid Build Coastguard Worker     // the next call to flushDeviceToRecorder() will produce a non-null DrawTask. If this Device's
657*c8dee2aaSAndroid Build Coastguard Worker     // target is mipmapped, mipmap generation tasks will be added automatically at that point.
658*c8dee2aaSAndroid Build Coastguard Worker     return fDC->recordUpload(fRecorder, fDC->refTarget(), src.info().colorInfo(),
659*c8dee2aaSAndroid Build Coastguard Worker                              this->imageInfo().colorInfo(), levels, dstRect, nullptr);
660*c8dee2aaSAndroid Build Coastguard Worker }
661*c8dee2aaSAndroid Build Coastguard Worker 
662*c8dee2aaSAndroid Build Coastguard Worker 
663*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
664*c8dee2aaSAndroid Build Coastguard Worker 
isClipAntiAliased() const665*c8dee2aaSAndroid Build Coastguard Worker bool Device::isClipAntiAliased() const {
666*c8dee2aaSAndroid Build Coastguard Worker     // All clips are AA'ed unless it's wide-open, empty, or a device-rect with integer coordinates
667*c8dee2aaSAndroid Build Coastguard Worker     ClipStack::ClipState type = fClip.clipState();
668*c8dee2aaSAndroid Build Coastguard Worker     if (type == ClipStack::ClipState::kWideOpen || type == ClipStack::ClipState::kEmpty) {
669*c8dee2aaSAndroid Build Coastguard Worker         return false;
670*c8dee2aaSAndroid Build Coastguard Worker     } else if (type == ClipStack::ClipState::kDeviceRect) {
671*c8dee2aaSAndroid Build Coastguard Worker         const ClipStack::Element rect = *fClip.begin();
672*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(rect.fShape.isRect() && rect.fLocalToDevice.type() == Transform::Type::kIdentity);
673*c8dee2aaSAndroid Build Coastguard Worker         return rect.fShape.rect() != rect.fShape.rect().makeRoundOut();
674*c8dee2aaSAndroid Build Coastguard Worker     } else {
675*c8dee2aaSAndroid Build Coastguard Worker         return true;
676*c8dee2aaSAndroid Build Coastguard Worker     }
677*c8dee2aaSAndroid Build Coastguard Worker }
678*c8dee2aaSAndroid Build Coastguard Worker 
devClipBounds() const679*c8dee2aaSAndroid Build Coastguard Worker SkIRect Device::devClipBounds() const {
680*c8dee2aaSAndroid Build Coastguard Worker     return rect_to_pixelbounds(fClip.conservativeBounds());
681*c8dee2aaSAndroid Build Coastguard Worker }
682*c8dee2aaSAndroid Build Coastguard Worker 
683*c8dee2aaSAndroid Build Coastguard Worker // TODO: This is easy enough to support, but do we still need this API in Skia at all?
android_utils_clipAsRgn(SkRegion * region) const684*c8dee2aaSAndroid Build Coastguard Worker void Device::android_utils_clipAsRgn(SkRegion* region) const {
685*c8dee2aaSAndroid Build Coastguard Worker     SkIRect bounds = this->devClipBounds();
686*c8dee2aaSAndroid Build Coastguard Worker     // Assume wide open and then perform intersect/difference operations reducing the region
687*c8dee2aaSAndroid Build Coastguard Worker     region->setRect(bounds);
688*c8dee2aaSAndroid Build Coastguard Worker     const SkRegion deviceBounds(bounds);
689*c8dee2aaSAndroid Build Coastguard Worker     for (const ClipStack::Element& e : fClip) {
690*c8dee2aaSAndroid Build Coastguard Worker         SkRegion tmp;
691*c8dee2aaSAndroid Build Coastguard Worker         if (e.fShape.isRect() && e.fLocalToDevice.type() == Transform::Type::kIdentity) {
692*c8dee2aaSAndroid Build Coastguard Worker             tmp.setRect(rect_to_pixelbounds(e.fShape.rect()));
693*c8dee2aaSAndroid Build Coastguard Worker         } else {
694*c8dee2aaSAndroid Build Coastguard Worker             SkPath tmpPath = e.fShape.asPath();
695*c8dee2aaSAndroid Build Coastguard Worker             tmpPath.transform(e.fLocalToDevice);
696*c8dee2aaSAndroid Build Coastguard Worker             tmp.setPath(tmpPath, deviceBounds);
697*c8dee2aaSAndroid Build Coastguard Worker         }
698*c8dee2aaSAndroid Build Coastguard Worker 
699*c8dee2aaSAndroid Build Coastguard Worker         region->op(tmp, (SkRegion::Op) e.fOp);
700*c8dee2aaSAndroid Build Coastguard Worker     }
701*c8dee2aaSAndroid Build Coastguard Worker }
702*c8dee2aaSAndroid Build Coastguard Worker 
clipRect(const SkRect & rect,SkClipOp op,bool aa)703*c8dee2aaSAndroid Build Coastguard Worker void Device::clipRect(const SkRect& rect, SkClipOp op, bool aa) {
704*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
705*c8dee2aaSAndroid Build Coastguard Worker     auto snapping = aa ? ClipStack::PixelSnapping::kNo : ClipStack::PixelSnapping::kYes;
706*c8dee2aaSAndroid Build Coastguard Worker     fClip.clipShape(this->localToDeviceTransform(), Shape{rect}, op, snapping);
707*c8dee2aaSAndroid Build Coastguard Worker }
708*c8dee2aaSAndroid Build Coastguard Worker 
clipRRect(const SkRRect & rrect,SkClipOp op,bool aa)709*c8dee2aaSAndroid Build Coastguard Worker void Device::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
710*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
711*c8dee2aaSAndroid Build Coastguard Worker     auto snapping = aa ? ClipStack::PixelSnapping::kNo : ClipStack::PixelSnapping::kYes;
712*c8dee2aaSAndroid Build Coastguard Worker     fClip.clipShape(this->localToDeviceTransform(), Shape{rrect}, op, snapping);
713*c8dee2aaSAndroid Build Coastguard Worker }
714*c8dee2aaSAndroid Build Coastguard Worker 
clipPath(const SkPath & path,SkClipOp op,bool aa)715*c8dee2aaSAndroid Build Coastguard Worker void Device::clipPath(const SkPath& path, SkClipOp op, bool aa) {
716*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
717*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Ensure all path inspection is handled here or in SkCanvas, and that non-AA rects as
718*c8dee2aaSAndroid Build Coastguard Worker     // paths are routed appropriately.
719*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Must also detect paths that are lines so the clip stack can be set to empty
720*c8dee2aaSAndroid Build Coastguard Worker     fClip.clipShape(this->localToDeviceTransform(), Shape{path}, op);
721*c8dee2aaSAndroid Build Coastguard Worker }
722*c8dee2aaSAndroid Build Coastguard Worker 
onClipShader(sk_sp<SkShader> shader)723*c8dee2aaSAndroid Build Coastguard Worker void Device::onClipShader(sk_sp<SkShader> shader) {
724*c8dee2aaSAndroid Build Coastguard Worker     fClip.clipShader(std::move(shader));
725*c8dee2aaSAndroid Build Coastguard Worker }
726*c8dee2aaSAndroid Build Coastguard Worker 
727*c8dee2aaSAndroid Build Coastguard Worker // TODO: Is clipRegion() on the deprecation chopping block. If not it should be...
clipRegion(const SkRegion & globalRgn,SkClipOp op)728*c8dee2aaSAndroid Build Coastguard Worker void Device::clipRegion(const SkRegion& globalRgn, SkClipOp op) {
729*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
730*c8dee2aaSAndroid Build Coastguard Worker 
731*c8dee2aaSAndroid Build Coastguard Worker     Transform globalToDevice{this->globalToDevice()};
732*c8dee2aaSAndroid Build Coastguard Worker 
733*c8dee2aaSAndroid Build Coastguard Worker     if (globalRgn.isEmpty()) {
734*c8dee2aaSAndroid Build Coastguard Worker         fClip.clipShape(globalToDevice, Shape{}, op);
735*c8dee2aaSAndroid Build Coastguard Worker     } else if (globalRgn.isRect()) {
736*c8dee2aaSAndroid Build Coastguard Worker         fClip.clipShape(globalToDevice, Shape{SkRect::Make(globalRgn.getBounds())}, op,
737*c8dee2aaSAndroid Build Coastguard Worker                         ClipStack::PixelSnapping::kYes);
738*c8dee2aaSAndroid Build Coastguard Worker     } else {
739*c8dee2aaSAndroid Build Coastguard Worker         // TODO: Can we just iterate the region and do non-AA rects for each chunk?
740*c8dee2aaSAndroid Build Coastguard Worker         SkPath path;
741*c8dee2aaSAndroid Build Coastguard Worker         globalRgn.getBoundaryPath(&path);
742*c8dee2aaSAndroid Build Coastguard Worker         fClip.clipShape(globalToDevice, Shape{path}, op);
743*c8dee2aaSAndroid Build Coastguard Worker     }
744*c8dee2aaSAndroid Build Coastguard Worker }
745*c8dee2aaSAndroid Build Coastguard Worker 
replaceClip(const SkIRect & rect)746*c8dee2aaSAndroid Build Coastguard Worker void Device::replaceClip(const SkIRect& rect) {
747*c8dee2aaSAndroid Build Coastguard Worker     // ReplaceClip() is currently not intended to be supported in Graphite since it's only used
748*c8dee2aaSAndroid Build Coastguard Worker     // for emulating legacy clip ops in Android Framework, and apps/devices that require that
749*c8dee2aaSAndroid Build Coastguard Worker     // should not use Graphite. However, if it needs to be supported, we could probably implement
750*c8dee2aaSAndroid Build Coastguard Worker     // it by:
751*c8dee2aaSAndroid Build Coastguard Worker     //  1. Flush all pending clip element depth draws.
752*c8dee2aaSAndroid Build Coastguard Worker     //  2. Draw a fullscreen rect to the depth attachment using a Z value greater than what's
753*c8dee2aaSAndroid Build Coastguard Worker     //     been used so far.
754*c8dee2aaSAndroid Build Coastguard Worker     //  3. Make sure all future "unclipped" draws use this Z value instead of 0 so they aren't
755*c8dee2aaSAndroid Build Coastguard Worker     //     sorted before the depth reset.
756*c8dee2aaSAndroid Build Coastguard Worker     //  4. Make sure all prior elements are inactive so they can't affect subsequent draws.
757*c8dee2aaSAndroid Build Coastguard Worker     //
758*c8dee2aaSAndroid Build Coastguard Worker     // For now, just ignore it.
759*c8dee2aaSAndroid Build Coastguard Worker }
760*c8dee2aaSAndroid Build Coastguard Worker 
761*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////
762*c8dee2aaSAndroid Build Coastguard Worker 
drawPaint(const SkPaint & paint)763*c8dee2aaSAndroid Build Coastguard Worker void Device::drawPaint(const SkPaint& paint) {
764*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
765*c8dee2aaSAndroid Build Coastguard Worker     // We never want to do a fullscreen clear on a fully-lazy render target, because the device size
766*c8dee2aaSAndroid Build Coastguard Worker     // may be smaller than the final surface we draw to, in which case we don't want to fill the
767*c8dee2aaSAndroid Build Coastguard Worker     // entire final surface.
768*c8dee2aaSAndroid Build Coastguard Worker     if (this->isClipWideOpen() && !fDC->target()->isFullyLazy()) {
769*c8dee2aaSAndroid Build Coastguard Worker         if (!paint_depends_on_dst(paint)) {
770*c8dee2aaSAndroid Build Coastguard Worker             if (std::optional<SkColor4f> color = extract_paint_color(paint, fDC->colorInfo())) {
771*c8dee2aaSAndroid Build Coastguard Worker                 // do fullscreen clear
772*c8dee2aaSAndroid Build Coastguard Worker                 fDC->clear(*color);
773*c8dee2aaSAndroid Build Coastguard Worker                 return;
774*c8dee2aaSAndroid Build Coastguard Worker             } else {
775*c8dee2aaSAndroid Build Coastguard Worker                 // This paint does not depend on the destination and covers the entire surface, so
776*c8dee2aaSAndroid Build Coastguard Worker                 // discard everything previously recorded and proceed with the draw.
777*c8dee2aaSAndroid Build Coastguard Worker                 fDC->discard();
778*c8dee2aaSAndroid Build Coastguard Worker             }
779*c8dee2aaSAndroid Build Coastguard Worker         }
780*c8dee2aaSAndroid Build Coastguard Worker     }
781*c8dee2aaSAndroid Build Coastguard Worker 
782*c8dee2aaSAndroid Build Coastguard Worker     Shape inverseFill; // defaults to empty
783*c8dee2aaSAndroid Build Coastguard Worker     inverseFill.setInverted(true);
784*c8dee2aaSAndroid Build Coastguard Worker     // An empty shape with an inverse fill completely floods the clip
785*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(inverseFill.isEmpty() && inverseFill.inverted());
786*c8dee2aaSAndroid Build Coastguard Worker 
787*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(),
788*c8dee2aaSAndroid Build Coastguard Worker                        Geometry(inverseFill),
789*c8dee2aaSAndroid Build Coastguard Worker                        paint,
790*c8dee2aaSAndroid Build Coastguard Worker                        DefaultFillStyle(),
791*c8dee2aaSAndroid Build Coastguard Worker                        DrawFlags::kIgnorePathEffect);
792*c8dee2aaSAndroid Build Coastguard Worker }
793*c8dee2aaSAndroid Build Coastguard Worker 
drawRect(const SkRect & r,const SkPaint & paint)794*c8dee2aaSAndroid Build Coastguard Worker void Device::drawRect(const SkRect& r, const SkPaint& paint) {
795*c8dee2aaSAndroid Build Coastguard Worker     Rect rectToDraw(r);
796*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec style(paint);
797*c8dee2aaSAndroid Build Coastguard Worker     if (!paint.isAntiAlias()) {
798*c8dee2aaSAndroid Build Coastguard Worker         // Graphite assumes everything is anti-aliased. In the case of axis-aligned non-aa requested
799*c8dee2aaSAndroid Build Coastguard Worker         // rectangles, we snap the local geometry to land on pixel boundaries to emulate non-aa.
800*c8dee2aaSAndroid Build Coastguard Worker         if (style.isFillStyle()) {
801*c8dee2aaSAndroid Build Coastguard Worker             rectToDraw = snap_rect_to_pixels(this->localToDeviceTransform(), rectToDraw);
802*c8dee2aaSAndroid Build Coastguard Worker         } else {
803*c8dee2aaSAndroid Build Coastguard Worker             const bool strokeAndFill = style.getStyle() == SkStrokeRec::kStrokeAndFill_Style;
804*c8dee2aaSAndroid Build Coastguard Worker             float strokeWidth = style.getWidth();
805*c8dee2aaSAndroid Build Coastguard Worker             rectToDraw = snap_rect_to_pixels(this->localToDeviceTransform(),
806*c8dee2aaSAndroid Build Coastguard Worker                                              rectToDraw, &strokeWidth);
807*c8dee2aaSAndroid Build Coastguard Worker             style.setStrokeStyle(strokeWidth, strokeAndFill);
808*c8dee2aaSAndroid Build Coastguard Worker         }
809*c8dee2aaSAndroid Build Coastguard Worker     }
810*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(rectToDraw)), paint, style);
811*c8dee2aaSAndroid Build Coastguard Worker }
812*c8dee2aaSAndroid Build Coastguard Worker 
drawVertices(const SkVertices * vertices,sk_sp<SkBlender> blender,const SkPaint & paint,bool skipColorXform)813*c8dee2aaSAndroid Build Coastguard Worker void Device::drawVertices(const SkVertices* vertices, sk_sp<SkBlender> blender,
814*c8dee2aaSAndroid Build Coastguard Worker                           const SkPaint& paint, bool skipColorXform)  {
815*c8dee2aaSAndroid Build Coastguard Worker   // TODO - Add GPU handling of skipColorXform once Graphite has its color system more fleshed out.
816*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(),
817*c8dee2aaSAndroid Build Coastguard Worker                        Geometry(sk_ref_sp(vertices)),
818*c8dee2aaSAndroid Build Coastguard Worker                        paint,
819*c8dee2aaSAndroid Build Coastguard Worker                        DefaultFillStyle(),
820*c8dee2aaSAndroid Build Coastguard Worker                        DrawFlags::kIgnorePathEffect,
821*c8dee2aaSAndroid Build Coastguard Worker                        std::move(blender),
822*c8dee2aaSAndroid Build Coastguard Worker                        skipColorXform);
823*c8dee2aaSAndroid Build Coastguard Worker }
824*c8dee2aaSAndroid Build Coastguard Worker 
drawAsTiledImageRect(SkCanvas * canvas,const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)825*c8dee2aaSAndroid Build Coastguard Worker bool Device::drawAsTiledImageRect(SkCanvas* canvas,
826*c8dee2aaSAndroid Build Coastguard Worker                                   const SkImage* image,
827*c8dee2aaSAndroid Build Coastguard Worker                                   const SkRect* src,
828*c8dee2aaSAndroid Build Coastguard Worker                                   const SkRect& dst,
829*c8dee2aaSAndroid Build Coastguard Worker                                   const SkSamplingOptions& sampling,
830*c8dee2aaSAndroid Build Coastguard Worker                                   const SkPaint& paint,
831*c8dee2aaSAndroid Build Coastguard Worker                                   SkCanvas::SrcRectConstraint constraint) {
832*c8dee2aaSAndroid Build Coastguard Worker     auto recorder = canvas->recorder();
833*c8dee2aaSAndroid Build Coastguard Worker     if (!recorder) {
834*c8dee2aaSAndroid Build Coastguard Worker         return false;
835*c8dee2aaSAndroid Build Coastguard Worker     }
836*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(src);
837*c8dee2aaSAndroid Build Coastguard Worker 
838*c8dee2aaSAndroid Build Coastguard Worker     // For Graphite this is a pretty loose heuristic. The Recorder-local cache size (relative
839*c8dee2aaSAndroid Build Coastguard Worker     // to the large image's size) is used as a proxy for how conservative we should be when
840*c8dee2aaSAndroid Build Coastguard Worker     // allocating tiles. Since the tiles will actually be owned by the client (via an
841*c8dee2aaSAndroid Build Coastguard Worker     // ImageProvider) they won't actually add any memory pressure directly to Graphite.
842*c8dee2aaSAndroid Build Coastguard Worker     size_t cacheSize = recorder->priv().getResourceCacheLimit();
843*c8dee2aaSAndroid Build Coastguard Worker     size_t maxTextureSize = recorder->priv().caps()->maxTextureSize();
844*c8dee2aaSAndroid Build Coastguard Worker 
845*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
846*c8dee2aaSAndroid Build Coastguard Worker     if (gOverrideMaxTextureSizeGraphite) {
847*c8dee2aaSAndroid Build Coastguard Worker         maxTextureSize = gOverrideMaxTextureSizeGraphite;
848*c8dee2aaSAndroid Build Coastguard Worker     }
849*c8dee2aaSAndroid Build Coastguard Worker     gNumTilesDrawnGraphite.store(0, std::memory_order_relaxed);
850*c8dee2aaSAndroid Build Coastguard Worker #endif
851*c8dee2aaSAndroid Build Coastguard Worker 
852*c8dee2aaSAndroid Build Coastguard Worker     // DrawAsTiledImageRect produces per-edge AA quads, which do not participate in non-AA pixel
853*c8dee2aaSAndroid Build Coastguard Worker     // snapping emulation. To match an un-tiled drawImageRect, round the src and dst geometry
854*c8dee2aaSAndroid Build Coastguard Worker     // before any tiling occurs.
855*c8dee2aaSAndroid Build Coastguard Worker     SkRect finalSrc = *src;
856*c8dee2aaSAndroid Build Coastguard Worker     SkRect finalDst = dst;
857*c8dee2aaSAndroid Build Coastguard Worker     if (!paint.isAntiAlias()) {
858*c8dee2aaSAndroid Build Coastguard Worker         snap_src_and_dst_rect_to_pixels(this->localToDeviceTransform(),
859*c8dee2aaSAndroid Build Coastguard Worker                                         &finalSrc, &finalDst);
860*c8dee2aaSAndroid Build Coastguard Worker     }
861*c8dee2aaSAndroid Build Coastguard Worker 
862*c8dee2aaSAndroid Build Coastguard Worker     [[maybe_unused]] auto [wasTiled, numTiles] =
863*c8dee2aaSAndroid Build Coastguard Worker             skgpu::TiledTextureUtils::DrawAsTiledImageRect(canvas,
864*c8dee2aaSAndroid Build Coastguard Worker                                                            image,
865*c8dee2aaSAndroid Build Coastguard Worker                                                            finalSrc,
866*c8dee2aaSAndroid Build Coastguard Worker                                                            finalDst,
867*c8dee2aaSAndroid Build Coastguard Worker                                                            SkCanvas::kAll_QuadAAFlags,
868*c8dee2aaSAndroid Build Coastguard Worker                                                            sampling,
869*c8dee2aaSAndroid Build Coastguard Worker                                                            &paint,
870*c8dee2aaSAndroid Build Coastguard Worker                                                            constraint,
871*c8dee2aaSAndroid Build Coastguard Worker                                                            /* sharpenMM= */ true,
872*c8dee2aaSAndroid Build Coastguard Worker                                                            cacheSize,
873*c8dee2aaSAndroid Build Coastguard Worker                                                            maxTextureSize);
874*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
875*c8dee2aaSAndroid Build Coastguard Worker     gNumTilesDrawnGraphite.store(numTiles, std::memory_order_relaxed);
876*c8dee2aaSAndroid Build Coastguard Worker #endif
877*c8dee2aaSAndroid Build Coastguard Worker     return wasTiled;
878*c8dee2aaSAndroid Build Coastguard Worker }
879*c8dee2aaSAndroid Build Coastguard Worker 
drawOval(const SkRect & oval,const SkPaint & paint)880*c8dee2aaSAndroid Build Coastguard Worker void Device::drawOval(const SkRect& oval, const SkPaint& paint) {
881*c8dee2aaSAndroid Build Coastguard Worker     if (paint.getPathEffect()) {
882*c8dee2aaSAndroid Build Coastguard Worker         // Dashing requires that the oval path starts on the right side and travels clockwise. This
883*c8dee2aaSAndroid Build Coastguard Worker         // is the default for the SkPath::Oval constructor, as used by SkBitmapDevice.
884*c8dee2aaSAndroid Build Coastguard Worker         this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(SkPath::Oval(oval))),
885*c8dee2aaSAndroid Build Coastguard Worker                            paint, SkStrokeRec(paint));
886*c8dee2aaSAndroid Build Coastguard Worker     } else {
887*c8dee2aaSAndroid Build Coastguard Worker         // TODO: This has wasted effort from the SkCanvas level since it instead converts rrects
888*c8dee2aaSAndroid Build Coastguard Worker         // that happen to be ovals into this, only for us to go right back to rrect.
889*c8dee2aaSAndroid Build Coastguard Worker         this->drawRRect(SkRRect::MakeOval(oval), paint);
890*c8dee2aaSAndroid Build Coastguard Worker     }
891*c8dee2aaSAndroid Build Coastguard Worker }
892*c8dee2aaSAndroid Build Coastguard Worker 
drawArc(const SkArc & arc,const SkPaint & paint)893*c8dee2aaSAndroid Build Coastguard Worker void Device::drawArc(const SkArc& arc, const SkPaint& paint) {
894*c8dee2aaSAndroid Build Coastguard Worker     // For sweeps >= 360°, simple fills and simple strokes without the center point or square caps
895*c8dee2aaSAndroid Build Coastguard Worker     // are ovals. Culling these here simplifies the path processing in Shape.
896*c8dee2aaSAndroid Build Coastguard Worker     if (!paint.getPathEffect() &&
897*c8dee2aaSAndroid Build Coastguard Worker         SkScalarAbs(arc.sweepAngle()) >= 360.f &&
898*c8dee2aaSAndroid Build Coastguard Worker         (paint.getStyle() == SkPaint::kFill_Style ||
899*c8dee2aaSAndroid Build Coastguard Worker          (paint.getStyle() == SkPaint::kStroke_Style &&
900*c8dee2aaSAndroid Build Coastguard Worker           // square caps can stick out from the shape so we can't do this with an rrect draw
901*c8dee2aaSAndroid Build Coastguard Worker           paint.getStrokeCap() != SkPaint::kSquare_Cap &&
902*c8dee2aaSAndroid Build Coastguard Worker           // non-wedge cases with strokes will draw lines to the center
903*c8dee2aaSAndroid Build Coastguard Worker           !arc.isWedge()))) {
904*c8dee2aaSAndroid Build Coastguard Worker         this->drawRRect(SkRRect::MakeOval(arc.oval()), paint);
905*c8dee2aaSAndroid Build Coastguard Worker     } else {
906*c8dee2aaSAndroid Build Coastguard Worker         this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(arc)),
907*c8dee2aaSAndroid Build Coastguard Worker                            paint, SkStrokeRec(paint));
908*c8dee2aaSAndroid Build Coastguard Worker     }
909*c8dee2aaSAndroid Build Coastguard Worker }
910*c8dee2aaSAndroid Build Coastguard Worker 
drawRRect(const SkRRect & rr,const SkPaint & paint)911*c8dee2aaSAndroid Build Coastguard Worker void Device::drawRRect(const SkRRect& rr, const SkPaint& paint) {
912*c8dee2aaSAndroid Build Coastguard Worker     Shape rrectToDraw;
913*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec style(paint);
914*c8dee2aaSAndroid Build Coastguard Worker 
915*c8dee2aaSAndroid Build Coastguard Worker     if (paint.isAntiAlias()) {
916*c8dee2aaSAndroid Build Coastguard Worker         rrectToDraw.setRRect(rr);
917*c8dee2aaSAndroid Build Coastguard Worker     } else {
918*c8dee2aaSAndroid Build Coastguard Worker         // Snap the horizontal and vertical edges of the rounded rectangle to pixel edges to match
919*c8dee2aaSAndroid Build Coastguard Worker         // the behavior of drawRect(rr.bounds()), to partially emulate non-AA rendering while
920*c8dee2aaSAndroid Build Coastguard Worker         // preserving the anti-aliasing of the curved corners.
921*c8dee2aaSAndroid Build Coastguard Worker         Rect snappedBounds;
922*c8dee2aaSAndroid Build Coastguard Worker         if (style.isFillStyle()) {
923*c8dee2aaSAndroid Build Coastguard Worker             snappedBounds = snap_rect_to_pixels(this->localToDeviceTransform(), rr.rect());
924*c8dee2aaSAndroid Build Coastguard Worker         } else {
925*c8dee2aaSAndroid Build Coastguard Worker             const bool strokeAndFill = style.getStyle() == SkStrokeRec::kStrokeAndFill_Style;
926*c8dee2aaSAndroid Build Coastguard Worker             float strokeWidth = style.getWidth();
927*c8dee2aaSAndroid Build Coastguard Worker             snappedBounds = snap_rect_to_pixels(this->localToDeviceTransform(),
928*c8dee2aaSAndroid Build Coastguard Worker                                                 rr.rect(), &strokeWidth);
929*c8dee2aaSAndroid Build Coastguard Worker             style.setStrokeStyle(strokeWidth, strokeAndFill);
930*c8dee2aaSAndroid Build Coastguard Worker         }
931*c8dee2aaSAndroid Build Coastguard Worker 
932*c8dee2aaSAndroid Build Coastguard Worker         SkRRect snappedRRect;
933*c8dee2aaSAndroid Build Coastguard Worker         snappedRRect.setRectRadii(snappedBounds.asSkRect(), rr.radii().data());
934*c8dee2aaSAndroid Build Coastguard Worker         rrectToDraw.setRRect(snappedRRect);
935*c8dee2aaSAndroid Build Coastguard Worker     }
936*c8dee2aaSAndroid Build Coastguard Worker 
937*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(), Geometry(rrectToDraw), paint, style);
938*c8dee2aaSAndroid Build Coastguard Worker }
939*c8dee2aaSAndroid Build Coastguard Worker 
drawPath(const SkPath & path,const SkPaint & paint,bool pathIsMutable)940*c8dee2aaSAndroid Build Coastguard Worker void Device::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
941*c8dee2aaSAndroid Build Coastguard Worker     // Alternatively, we could move this analysis to SkCanvas. Also, we could consider applying the
942*c8dee2aaSAndroid Build Coastguard Worker     // path effect, being careful about starting point and direction.
943*c8dee2aaSAndroid Build Coastguard Worker     if (!paint.getPathEffect() && !path.isInverseFillType()) {
944*c8dee2aaSAndroid Build Coastguard Worker         if (SkRect oval; path.isOval(&oval)) {
945*c8dee2aaSAndroid Build Coastguard Worker             this->drawOval(oval, paint);
946*c8dee2aaSAndroid Build Coastguard Worker             return;
947*c8dee2aaSAndroid Build Coastguard Worker         }
948*c8dee2aaSAndroid Build Coastguard Worker         if (SkRRect rrect; path.isRRect(&rrect)) {
949*c8dee2aaSAndroid Build Coastguard Worker             this->drawRRect(rrect, paint);
950*c8dee2aaSAndroid Build Coastguard Worker             return;
951*c8dee2aaSAndroid Build Coastguard Worker         }
952*c8dee2aaSAndroid Build Coastguard Worker         // For rects, if the path is not explicitly closed and the paint style is stroked then it
953*c8dee2aaSAndroid Build Coastguard Worker         // represents a rectangle with only 3 sides rasterized (and with any caps). If it's filled
954*c8dee2aaSAndroid Build Coastguard Worker         // or is closed+stroked, then the path renders identically to the rectangle.
955*c8dee2aaSAndroid Build Coastguard Worker         bool isClosed = false;
956*c8dee2aaSAndroid Build Coastguard Worker         if (SkRect rect; path.isRect(&rect, &isClosed) &&
957*c8dee2aaSAndroid Build Coastguard Worker             (paint.getStyle() == SkPaint::kFill_Style || isClosed)) {
958*c8dee2aaSAndroid Build Coastguard Worker             this->drawRect(rect, paint);
959*c8dee2aaSAndroid Build Coastguard Worker             return;
960*c8dee2aaSAndroid Build Coastguard Worker         }
961*c8dee2aaSAndroid Build Coastguard Worker     }
962*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(path)),
963*c8dee2aaSAndroid Build Coastguard Worker                        paint, SkStrokeRec(paint));
964*c8dee2aaSAndroid Build Coastguard Worker }
965*c8dee2aaSAndroid Build Coastguard Worker 
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & paint)966*c8dee2aaSAndroid Build Coastguard Worker void Device::drawPoints(SkCanvas::PointMode mode, size_t count,
967*c8dee2aaSAndroid Build Coastguard Worker                         const SkPoint* points, const SkPaint& paint) {
968*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec stroke(paint, SkPaint::kStroke_Style);
969*c8dee2aaSAndroid Build Coastguard Worker     size_t next = 0;
970*c8dee2aaSAndroid Build Coastguard Worker     if (mode == SkCanvas::kPoints_PointMode) {
971*c8dee2aaSAndroid Build Coastguard Worker         // Treat kPoints mode as stroking zero-length path segments, which produce caps so that
972*c8dee2aaSAndroid Build Coastguard Worker         // both hairlines and round vs. square geometry are handled entirely on the GPU.
973*c8dee2aaSAndroid Build Coastguard Worker         // TODO: SkCanvas should probably do the butt to square cap correction.
974*c8dee2aaSAndroid Build Coastguard Worker         if (paint.getStrokeCap() == SkPaint::kButt_Cap) {
975*c8dee2aaSAndroid Build Coastguard Worker             stroke.setStrokeParams(SkPaint::kSquare_Cap,
976*c8dee2aaSAndroid Build Coastguard Worker                                    paint.getStrokeJoin(),
977*c8dee2aaSAndroid Build Coastguard Worker                                    paint.getStrokeMiter());
978*c8dee2aaSAndroid Build Coastguard Worker         }
979*c8dee2aaSAndroid Build Coastguard Worker     } else {
980*c8dee2aaSAndroid Build Coastguard Worker         next = 1;
981*c8dee2aaSAndroid Build Coastguard Worker         count--;
982*c8dee2aaSAndroid Build Coastguard Worker     }
983*c8dee2aaSAndroid Build Coastguard Worker 
984*c8dee2aaSAndroid Build Coastguard Worker     size_t inc = mode == SkCanvas::kLines_PointMode ? 2 : 1;
985*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < count; i += inc) {
986*c8dee2aaSAndroid Build Coastguard Worker         this->drawGeometry(this->localToDeviceTransform(),
987*c8dee2aaSAndroid Build Coastguard Worker                            Geometry(Shape(points[i], points[i + next])),
988*c8dee2aaSAndroid Build Coastguard Worker                            paint, stroke);
989*c8dee2aaSAndroid Build Coastguard Worker     }
990*c8dee2aaSAndroid Build Coastguard Worker }
991*c8dee2aaSAndroid Build Coastguard Worker 
drawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],SkCanvas::QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)992*c8dee2aaSAndroid Build Coastguard Worker void Device::drawEdgeAAQuad(const SkRect& rect,
993*c8dee2aaSAndroid Build Coastguard Worker                             const SkPoint clip[4],
994*c8dee2aaSAndroid Build Coastguard Worker                             SkCanvas::QuadAAFlags aaFlags,
995*c8dee2aaSAndroid Build Coastguard Worker                             const SkColor4f& color,
996*c8dee2aaSAndroid Build Coastguard Worker                             SkBlendMode mode) {
997*c8dee2aaSAndroid Build Coastguard Worker     SkPaint solidColorPaint;
998*c8dee2aaSAndroid Build Coastguard Worker     solidColorPaint.setColor4f(color, /*colorSpace=*/nullptr);
999*c8dee2aaSAndroid Build Coastguard Worker     solidColorPaint.setBlendMode(mode);
1000*c8dee2aaSAndroid Build Coastguard Worker 
1001*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: We do not snap edge AA quads that are fully non-AA because we need their edges to seam
1002*c8dee2aaSAndroid Build Coastguard Worker     // with quads that have mixed edge flags (so both need to match the GPU rasterization, not our
1003*c8dee2aaSAndroid Build Coastguard Worker     // CPU rounding).
1004*c8dee2aaSAndroid Build Coastguard Worker     auto flags = SkEnumBitMask<EdgeAAQuad::Flags>(static_cast<EdgeAAQuad::Flags>(aaFlags));
1005*c8dee2aaSAndroid Build Coastguard Worker     EdgeAAQuad quad = clip ? EdgeAAQuad(clip, flags) : EdgeAAQuad(rect, flags);
1006*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(),
1007*c8dee2aaSAndroid Build Coastguard Worker                        Geometry(quad),
1008*c8dee2aaSAndroid Build Coastguard Worker                        solidColorPaint,
1009*c8dee2aaSAndroid Build Coastguard Worker                        DefaultFillStyle(),
1010*c8dee2aaSAndroid Build Coastguard Worker                        DrawFlags::kIgnorePathEffect);
1011*c8dee2aaSAndroid Build Coastguard Worker }
1012*c8dee2aaSAndroid Build Coastguard Worker 
drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[],int count,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)1013*c8dee2aaSAndroid Build Coastguard Worker void Device::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry set[], int count,
1014*c8dee2aaSAndroid Build Coastguard Worker                                 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
1015*c8dee2aaSAndroid Build Coastguard Worker                                 const SkSamplingOptions& sampling, const SkPaint& paint,
1016*c8dee2aaSAndroid Build Coastguard Worker                                 SkCanvas::SrcRectConstraint constraint) {
1017*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(count > 0);
1018*c8dee2aaSAndroid Build Coastguard Worker 
1019*c8dee2aaSAndroid Build Coastguard Worker     SkPaint paintWithShader(paint);
1020*c8dee2aaSAndroid Build Coastguard Worker     int dstClipIndex = 0;
1021*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < count; ++i) {
1022*c8dee2aaSAndroid Build Coastguard Worker         // If the entry is clipped by 'dstClips', that must be provided
1023*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!set[i].fHasClip || dstClips);
1024*c8dee2aaSAndroid Build Coastguard Worker         // Similarly, if it has an extra transform, those must be provided
1025*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(set[i].fMatrixIndex < 0 || preViewMatrices);
1026*c8dee2aaSAndroid Build Coastguard Worker 
1027*c8dee2aaSAndroid Build Coastguard Worker         auto [ imageToDraw, newSampling ] =
1028*c8dee2aaSAndroid Build Coastguard Worker                 skgpu::graphite::GetGraphiteBacked(this->recorder(), set[i].fImage.get(), sampling);
1029*c8dee2aaSAndroid Build Coastguard Worker         if (!imageToDraw) {
1030*c8dee2aaSAndroid Build Coastguard Worker             SKGPU_LOG_W("Device::drawImageRect: Creation of Graphite-backed image failed");
1031*c8dee2aaSAndroid Build Coastguard Worker             return;
1032*c8dee2aaSAndroid Build Coastguard Worker         }
1033*c8dee2aaSAndroid Build Coastguard Worker 
1034*c8dee2aaSAndroid Build Coastguard Worker         // TODO: Produce an image shading paint key and data directly without having to reconstruct
1035*c8dee2aaSAndroid Build Coastguard Worker         // the equivalent SkPaint for each entry. Reuse the key and data between entries if possible
1036*c8dee2aaSAndroid Build Coastguard Worker         paintWithShader.setShader(paint.refShader());
1037*c8dee2aaSAndroid Build Coastguard Worker         paintWithShader.setAlphaf(paint.getAlphaf() * set[i].fAlpha);
1038*c8dee2aaSAndroid Build Coastguard Worker         SkRect dst = SkModifyPaintAndDstForDrawImageRect(
1039*c8dee2aaSAndroid Build Coastguard Worker                     imageToDraw.get(), newSampling, set[i].fSrcRect, set[i].fDstRect,
1040*c8dee2aaSAndroid Build Coastguard Worker                     constraint == SkCanvas::kStrict_SrcRectConstraint,
1041*c8dee2aaSAndroid Build Coastguard Worker                     &paintWithShader);
1042*c8dee2aaSAndroid Build Coastguard Worker         if (dst.isEmpty()) {
1043*c8dee2aaSAndroid Build Coastguard Worker             return;
1044*c8dee2aaSAndroid Build Coastguard Worker         }
1045*c8dee2aaSAndroid Build Coastguard Worker 
1046*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: See drawEdgeAAQuad for details, we do not snap non-AA quads.
1047*c8dee2aaSAndroid Build Coastguard Worker         auto flags =
1048*c8dee2aaSAndroid Build Coastguard Worker                 SkEnumBitMask<EdgeAAQuad::Flags>(static_cast<EdgeAAQuad::Flags>(set[i].fAAFlags));
1049*c8dee2aaSAndroid Build Coastguard Worker         EdgeAAQuad quad = set[i].fHasClip ? EdgeAAQuad(dstClips + dstClipIndex, flags)
1050*c8dee2aaSAndroid Build Coastguard Worker                                           : EdgeAAQuad(dst, flags);
1051*c8dee2aaSAndroid Build Coastguard Worker 
1052*c8dee2aaSAndroid Build Coastguard Worker         // TODO: Calling drawGeometry() for each entry re-evaluates the clip stack every time, which
1053*c8dee2aaSAndroid Build Coastguard Worker         // is consistent with Ganesh's behavior. It also matches the behavior if edge-AA images were
1054*c8dee2aaSAndroid Build Coastguard Worker         // submitted one at a time by SkiaRenderer (a nice client simplification). However, we
1055*c8dee2aaSAndroid Build Coastguard Worker         // should explore the performance trade off with doing one bulk evaluation for the whole set
1056*c8dee2aaSAndroid Build Coastguard Worker         if (set[i].fMatrixIndex < 0) {
1057*c8dee2aaSAndroid Build Coastguard Worker             this->drawGeometry(this->localToDeviceTransform(),
1058*c8dee2aaSAndroid Build Coastguard Worker                                Geometry(quad),
1059*c8dee2aaSAndroid Build Coastguard Worker                                paintWithShader,
1060*c8dee2aaSAndroid Build Coastguard Worker                                DefaultFillStyle(),
1061*c8dee2aaSAndroid Build Coastguard Worker                                DrawFlags::kIgnorePathEffect);
1062*c8dee2aaSAndroid Build Coastguard Worker         } else {
1063*c8dee2aaSAndroid Build Coastguard Worker             SkM44 xtraTransform(preViewMatrices[set[i].fMatrixIndex]);
1064*c8dee2aaSAndroid Build Coastguard Worker             this->drawGeometry(this->localToDeviceTransform().concat(xtraTransform),
1065*c8dee2aaSAndroid Build Coastguard Worker                                Geometry(quad),
1066*c8dee2aaSAndroid Build Coastguard Worker                                paintWithShader,
1067*c8dee2aaSAndroid Build Coastguard Worker                                DefaultFillStyle(),
1068*c8dee2aaSAndroid Build Coastguard Worker                                DrawFlags::kIgnorePathEffect);
1069*c8dee2aaSAndroid Build Coastguard Worker         }
1070*c8dee2aaSAndroid Build Coastguard Worker 
1071*c8dee2aaSAndroid Build Coastguard Worker         dstClipIndex += 4 * set[i].fHasClip;
1072*c8dee2aaSAndroid Build Coastguard Worker     }
1073*c8dee2aaSAndroid Build Coastguard Worker }
1074*c8dee2aaSAndroid Build Coastguard Worker 
drawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)1075*c8dee2aaSAndroid Build Coastguard Worker void Device::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
1076*c8dee2aaSAndroid Build Coastguard Worker                            const SkSamplingOptions& sampling, const SkPaint& paint,
1077*c8dee2aaSAndroid Build Coastguard Worker                            SkCanvas::SrcRectConstraint constraint) {
1078*c8dee2aaSAndroid Build Coastguard Worker     SkCanvas::ImageSetEntry single{sk_ref_sp(image),
1079*c8dee2aaSAndroid Build Coastguard Worker                                    src ? *src : SkRect::Make(image->bounds()),
1080*c8dee2aaSAndroid Build Coastguard Worker                                    dst,
1081*c8dee2aaSAndroid Build Coastguard Worker                                    /*alpha=*/1.f,
1082*c8dee2aaSAndroid Build Coastguard Worker                                    SkCanvas::kAll_QuadAAFlags};
1083*c8dee2aaSAndroid Build Coastguard Worker     // While this delegates to drawEdgeAAImageSet() for the image shading logic, semantically a
1084*c8dee2aaSAndroid Build Coastguard Worker     // drawImageRect()'s non-AA behavior should match that of drawRect() so we snap dst (and update
1085*c8dee2aaSAndroid Build Coastguard Worker     // src to match) if needed before hand.
1086*c8dee2aaSAndroid Build Coastguard Worker     if (!paint.isAntiAlias()) {
1087*c8dee2aaSAndroid Build Coastguard Worker         snap_src_and_dst_rect_to_pixels(this->localToDeviceTransform(),
1088*c8dee2aaSAndroid Build Coastguard Worker                                         &single.fSrcRect, &single.fDstRect);
1089*c8dee2aaSAndroid Build Coastguard Worker     }
1090*c8dee2aaSAndroid Build Coastguard Worker     this->drawEdgeAAImageSet(&single, 1, nullptr, nullptr, sampling, paint, constraint);
1091*c8dee2aaSAndroid Build Coastguard Worker }
1092*c8dee2aaSAndroid Build Coastguard Worker 
atlasDelegate()1093*c8dee2aaSAndroid Build Coastguard Worker sktext::gpu::AtlasDrawDelegate Device::atlasDelegate() {
1094*c8dee2aaSAndroid Build Coastguard Worker     return [&](const sktext::gpu::AtlasSubRun* subRun,
1095*c8dee2aaSAndroid Build Coastguard Worker                SkPoint drawOrigin,
1096*c8dee2aaSAndroid Build Coastguard Worker                const SkPaint& paint,
1097*c8dee2aaSAndroid Build Coastguard Worker                sk_sp<SkRefCnt> subRunStorage,
1098*c8dee2aaSAndroid Build Coastguard Worker                sktext::gpu::RendererData rendererData) {
1099*c8dee2aaSAndroid Build Coastguard Worker         this->drawAtlasSubRun(subRun, drawOrigin, paint, std::move(subRunStorage), rendererData);
1100*c8dee2aaSAndroid Build Coastguard Worker     };
1101*c8dee2aaSAndroid Build Coastguard Worker }
1102*c8dee2aaSAndroid Build Coastguard Worker 
onDrawGlyphRunList(SkCanvas * canvas,const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1103*c8dee2aaSAndroid Build Coastguard Worker void Device::onDrawGlyphRunList(SkCanvas* canvas,
1104*c8dee2aaSAndroid Build Coastguard Worker                                 const sktext::GlyphRunList& glyphRunList,
1105*c8dee2aaSAndroid Build Coastguard Worker                                 const SkPaint& paint) {
1106*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
1107*c8dee2aaSAndroid Build Coastguard Worker     fRecorder->priv().textBlobCache()->drawGlyphRunList(canvas,
1108*c8dee2aaSAndroid Build Coastguard Worker                                                         this->localToDevice(),
1109*c8dee2aaSAndroid Build Coastguard Worker                                                         glyphRunList,
1110*c8dee2aaSAndroid Build Coastguard Worker                                                         paint,
1111*c8dee2aaSAndroid Build Coastguard Worker                                                         this->strikeDeviceInfo(),
1112*c8dee2aaSAndroid Build Coastguard Worker                                                         this->atlasDelegate());
1113*c8dee2aaSAndroid Build Coastguard Worker }
1114*c8dee2aaSAndroid Build Coastguard Worker 
drawAtlasSubRun(const sktext::gpu::AtlasSubRun * subRun,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage,sktext::gpu::RendererData rendererData)1115*c8dee2aaSAndroid Build Coastguard Worker void Device::drawAtlasSubRun(const sktext::gpu::AtlasSubRun* subRun,
1116*c8dee2aaSAndroid Build Coastguard Worker                              SkPoint drawOrigin,
1117*c8dee2aaSAndroid Build Coastguard Worker                              const SkPaint& paint,
1118*c8dee2aaSAndroid Build Coastguard Worker                              sk_sp<SkRefCnt> subRunStorage,
1119*c8dee2aaSAndroid Build Coastguard Worker                              sktext::gpu::RendererData rendererData) {
1120*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
1121*c8dee2aaSAndroid Build Coastguard Worker 
1122*c8dee2aaSAndroid Build Coastguard Worker     const int subRunEnd = subRun->glyphCount();
1123*c8dee2aaSAndroid Build Coastguard Worker     auto regenerateDelegate = [&](sktext::gpu::GlyphVector* glyphs,
1124*c8dee2aaSAndroid Build Coastguard Worker                                   int begin,
1125*c8dee2aaSAndroid Build Coastguard Worker                                   int end,
1126*c8dee2aaSAndroid Build Coastguard Worker                                   skgpu::MaskFormat maskFormat,
1127*c8dee2aaSAndroid Build Coastguard Worker                                   int padding) {
1128*c8dee2aaSAndroid Build Coastguard Worker         return glyphs->regenerateAtlasForGraphite(begin, end, maskFormat, padding, fRecorder);
1129*c8dee2aaSAndroid Build Coastguard Worker     };
1130*c8dee2aaSAndroid Build Coastguard Worker     for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
1131*c8dee2aaSAndroid Build Coastguard Worker         // For the remainder of the run, add any atlas uploads to the Recorder's TextAtlasManager
1132*c8dee2aaSAndroid Build Coastguard Worker         auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd,
1133*c8dee2aaSAndroid Build Coastguard Worker                                                               regenerateDelegate);
1134*c8dee2aaSAndroid Build Coastguard Worker         // There was a problem allocating the glyph in the atlas. Bail.
1135*c8dee2aaSAndroid Build Coastguard Worker         if (!ok) {
1136*c8dee2aaSAndroid Build Coastguard Worker             return;
1137*c8dee2aaSAndroid Build Coastguard Worker         }
1138*c8dee2aaSAndroid Build Coastguard Worker         if (glyphsRegenerated) {
1139*c8dee2aaSAndroid Build Coastguard Worker             auto [bounds, localToDevice] = subRun->vertexFiller().boundsAndDeviceMatrix(
1140*c8dee2aaSAndroid Build Coastguard Worker                                                    this->localToDeviceTransform(), drawOrigin);
1141*c8dee2aaSAndroid Build Coastguard Worker             SkPaint subRunPaint = paint;
1142*c8dee2aaSAndroid Build Coastguard Worker             // For color emoji, shaders don't affect the final color
1143*c8dee2aaSAndroid Build Coastguard Worker             if (subRun->maskFormat() == skgpu::MaskFormat::kARGB) {
1144*c8dee2aaSAndroid Build Coastguard Worker                 subRunPaint.setShader(nullptr);
1145*c8dee2aaSAndroid Build Coastguard Worker             }
1146*c8dee2aaSAndroid Build Coastguard Worker 
1147*c8dee2aaSAndroid Build Coastguard Worker             bool useGammaCorrectDistanceTable =
1148*c8dee2aaSAndroid Build Coastguard Worker                     this->imageInfo().colorSpace() &&
1149*c8dee2aaSAndroid Build Coastguard Worker                     this->imageInfo().colorSpace()->gammaIsLinear();
1150*c8dee2aaSAndroid Build Coastguard Worker             this->drawGeometry(localToDevice,
1151*c8dee2aaSAndroid Build Coastguard Worker                                Geometry(SubRunData(subRun,
1152*c8dee2aaSAndroid Build Coastguard Worker                                                    subRunStorage,
1153*c8dee2aaSAndroid Build Coastguard Worker                                                    bounds,
1154*c8dee2aaSAndroid Build Coastguard Worker                                                    this->localToDeviceTransform().inverse(),
1155*c8dee2aaSAndroid Build Coastguard Worker                                                    subRunCursor,
1156*c8dee2aaSAndroid Build Coastguard Worker                                                    glyphsRegenerated,
1157*c8dee2aaSAndroid Build Coastguard Worker                                                    SkPaintPriv::ComputeLuminanceColor(subRunPaint),
1158*c8dee2aaSAndroid Build Coastguard Worker                                                    useGammaCorrectDistanceTable,
1159*c8dee2aaSAndroid Build Coastguard Worker                                                    this->surfaceProps().pixelGeometry(),
1160*c8dee2aaSAndroid Build Coastguard Worker                                                    fRecorder,
1161*c8dee2aaSAndroid Build Coastguard Worker                                                    rendererData)),
1162*c8dee2aaSAndroid Build Coastguard Worker                                subRunPaint,
1163*c8dee2aaSAndroid Build Coastguard Worker                                DefaultFillStyle(),
1164*c8dee2aaSAndroid Build Coastguard Worker                                DrawFlags::kIgnorePathEffect,
1165*c8dee2aaSAndroid Build Coastguard Worker                                SkBlender::Mode(SkBlendMode::kDstIn));
1166*c8dee2aaSAndroid Build Coastguard Worker         }
1167*c8dee2aaSAndroid Build Coastguard Worker         subRunCursor += glyphsRegenerated;
1168*c8dee2aaSAndroid Build Coastguard Worker 
1169*c8dee2aaSAndroid Build Coastguard Worker         if (subRunCursor < subRunEnd) {
1170*c8dee2aaSAndroid Build Coastguard Worker             // Flush if not all the glyphs are handled because the atlas is out of space.
1171*c8dee2aaSAndroid Build Coastguard Worker             // We flush every Device because the glyphs that are being flushed/referenced are not
1172*c8dee2aaSAndroid Build Coastguard Worker             // necessarily specific to this Device. This addresses both multiple SkSurfaces within
1173*c8dee2aaSAndroid Build Coastguard Worker             // a Recorder, and nested layers.
1174*c8dee2aaSAndroid Build Coastguard Worker             TRACE_EVENT_INSTANT0("skia.gpu", "Glyph atlas full", TRACE_EVENT_SCOPE_NAME_THREAD);
1175*c8dee2aaSAndroid Build Coastguard Worker             fRecorder->priv().flushTrackedDevices();
1176*c8dee2aaSAndroid Build Coastguard Worker         }
1177*c8dee2aaSAndroid Build Coastguard Worker     }
1178*c8dee2aaSAndroid Build Coastguard Worker }
1179*c8dee2aaSAndroid Build Coastguard Worker 
drawGeometry(const Transform & localToDevice,const Geometry & geometry,const SkPaint & paint,const SkStrokeRec & style,SkEnumBitMask<DrawFlags> flags,sk_sp<SkBlender> primitiveBlender,bool skipColorXform)1180*c8dee2aaSAndroid Build Coastguard Worker void Device::drawGeometry(const Transform& localToDevice,
1181*c8dee2aaSAndroid Build Coastguard Worker                           const Geometry& geometry,
1182*c8dee2aaSAndroid Build Coastguard Worker                           const SkPaint& paint,
1183*c8dee2aaSAndroid Build Coastguard Worker                           const SkStrokeRec& style,
1184*c8dee2aaSAndroid Build Coastguard Worker                           SkEnumBitMask<DrawFlags> flags,
1185*c8dee2aaSAndroid Build Coastguard Worker                           sk_sp<SkBlender> primitiveBlender,
1186*c8dee2aaSAndroid Build Coastguard Worker                           bool skipColorXform) {
1187*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
1188*c8dee2aaSAndroid Build Coastguard Worker 
1189*c8dee2aaSAndroid Build Coastguard Worker     if (!localToDevice.valid()) {
1190*c8dee2aaSAndroid Build Coastguard Worker         // If the transform is not invertible or not finite then drawing isn't well defined.
1191*c8dee2aaSAndroid Build Coastguard Worker         SKGPU_LOG_W("Skipping draw with non-invertible/non-finite transform.");
1192*c8dee2aaSAndroid Build Coastguard Worker         return;
1193*c8dee2aaSAndroid Build Coastguard Worker     }
1194*c8dee2aaSAndroid Build Coastguard Worker 
1195*c8dee2aaSAndroid Build Coastguard Worker     // Heavy weight paint options like path effects, mask filters, and stroke-and-fill style are
1196*c8dee2aaSAndroid Build Coastguard Worker     // applied on the CPU by generating a new shape and recursing on drawGeometry with updated flags
1197*c8dee2aaSAndroid Build Coastguard Worker     if (!(flags & DrawFlags::kIgnorePathEffect) && paint.getPathEffect()) {
1198*c8dee2aaSAndroid Build Coastguard Worker         // Apply the path effect before anything else, which if we are applying here, means that we
1199*c8dee2aaSAndroid Build Coastguard Worker         // are dealing with a Shape. drawVertices (and a SkVertices geometry) should pass in
1200*c8dee2aaSAndroid Build Coastguard Worker         // kIgnorePathEffect per SkCanvas spec. Text geometry also should pass in kIgnorePathEffect
1201*c8dee2aaSAndroid Build Coastguard Worker         // because the path effect is applied per glyph by the SkStrikeSpec already.
1202*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(geometry.isShape());
1203*c8dee2aaSAndroid Build Coastguard Worker 
1204*c8dee2aaSAndroid Build Coastguard Worker         // TODO: If asADash() returns true and the base path matches the dashing fast path, then
1205*c8dee2aaSAndroid Build Coastguard Worker         // that should be detected now as well. Maybe add dashPath to Device so canvas can handle it
1206*c8dee2aaSAndroid Build Coastguard Worker         SkStrokeRec newStyle = style;
1207*c8dee2aaSAndroid Build Coastguard Worker         float maxScaleFactor = localToDevice.maxScaleFactor();
1208*c8dee2aaSAndroid Build Coastguard Worker         if (localToDevice.type() == Transform::Type::kPerspective) {
1209*c8dee2aaSAndroid Build Coastguard Worker             auto bounds = geometry.bounds();
1210*c8dee2aaSAndroid Build Coastguard Worker             float tl = std::get<1>(localToDevice.scaleFactors({bounds.left(), bounds.top()}));
1211*c8dee2aaSAndroid Build Coastguard Worker             float tr = std::get<1>(localToDevice.scaleFactors({bounds.right(), bounds.top()}));
1212*c8dee2aaSAndroid Build Coastguard Worker             float br = std::get<1>(localToDevice.scaleFactors({bounds.right(), bounds.bot()}));
1213*c8dee2aaSAndroid Build Coastguard Worker             float bl = std::get<1>(localToDevice.scaleFactors({bounds.left(), bounds.bot()}));
1214*c8dee2aaSAndroid Build Coastguard Worker             maxScaleFactor = std::max(std::max(tl, tr), std::max(bl, br));
1215*c8dee2aaSAndroid Build Coastguard Worker         }
1216*c8dee2aaSAndroid Build Coastguard Worker         newStyle.setResScale(maxScaleFactor);
1217*c8dee2aaSAndroid Build Coastguard Worker         SkPath dst;
1218*c8dee2aaSAndroid Build Coastguard Worker         if (paint.getPathEffect()->filterPath(&dst, geometry.shape().asPath(), &newStyle,
1219*c8dee2aaSAndroid Build Coastguard Worker                                               nullptr, localToDevice)) {
1220*c8dee2aaSAndroid Build Coastguard Worker             dst.setIsVolatile(true);
1221*c8dee2aaSAndroid Build Coastguard Worker             // Recurse using the path and new style, while disabling downstream path effect handling
1222*c8dee2aaSAndroid Build Coastguard Worker             this->drawGeometry(localToDevice, Geometry(Shape(dst)), paint, newStyle,
1223*c8dee2aaSAndroid Build Coastguard Worker                                flags | DrawFlags::kIgnorePathEffect, std::move(primitiveBlender),
1224*c8dee2aaSAndroid Build Coastguard Worker                                skipColorXform);
1225*c8dee2aaSAndroid Build Coastguard Worker             return;
1226*c8dee2aaSAndroid Build Coastguard Worker         } else {
1227*c8dee2aaSAndroid Build Coastguard Worker             SKGPU_LOG_W("Path effect failed to apply, drawing original path.");
1228*c8dee2aaSAndroid Build Coastguard Worker             this->drawGeometry(localToDevice, geometry, paint, style,
1229*c8dee2aaSAndroid Build Coastguard Worker                                flags | DrawFlags::kIgnorePathEffect, std::move(primitiveBlender),
1230*c8dee2aaSAndroid Build Coastguard Worker                                skipColorXform);
1231*c8dee2aaSAndroid Build Coastguard Worker             return;
1232*c8dee2aaSAndroid Build Coastguard Worker         }
1233*c8dee2aaSAndroid Build Coastguard Worker     }
1234*c8dee2aaSAndroid Build Coastguard Worker 
1235*c8dee2aaSAndroid Build Coastguard Worker     // TODO: The tessellating and atlas path renderers haven't implemented perspective yet, so
1236*c8dee2aaSAndroid Build Coastguard Worker     // transform to device space so we draw something approximately correct (barring local coord
1237*c8dee2aaSAndroid Build Coastguard Worker     // issues).
1238*c8dee2aaSAndroid Build Coastguard Worker     if (geometry.isShape() && localToDevice.type() == Transform::Type::kPerspective &&
1239*c8dee2aaSAndroid Build Coastguard Worker         !is_simple_shape(geometry.shape(), style.getStyle())) {
1240*c8dee2aaSAndroid Build Coastguard Worker         SkPath devicePath = geometry.shape().asPath();
1241*c8dee2aaSAndroid Build Coastguard Worker         devicePath.transform(localToDevice.matrix().asM33());
1242*c8dee2aaSAndroid Build Coastguard Worker         devicePath.setIsVolatile(true);
1243*c8dee2aaSAndroid Build Coastguard Worker         this->drawGeometry(Transform::Identity(), Geometry(Shape(devicePath)), paint, style, flags,
1244*c8dee2aaSAndroid Build Coastguard Worker                            std::move(primitiveBlender), skipColorXform);
1245*c8dee2aaSAndroid Build Coastguard Worker         return;
1246*c8dee2aaSAndroid Build Coastguard Worker     }
1247*c8dee2aaSAndroid Build Coastguard Worker 
1248*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Manually snap pixels for rects, rrects, and lines if paint is non-AA (ideally also
1249*c8dee2aaSAndroid Build Coastguard Worker     // consider snapping stroke width and/or adjusting geometry for hairlines). This pixel snapping
1250*c8dee2aaSAndroid Build Coastguard Worker     // math should be consistent with how non-AA clip [r]rects are handled.
1251*c8dee2aaSAndroid Build Coastguard Worker 
1252*c8dee2aaSAndroid Build Coastguard Worker     // If we got here, then path effects should have been handled and the style should be fill or
1253*c8dee2aaSAndroid Build Coastguard Worker     // stroke/hairline. Stroke-and-fill is not handled by DrawContext, but is emulated here by
1254*c8dee2aaSAndroid Build Coastguard Worker     // drawing twice--one stroke and one fill--using the same depth value.
1255*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!SkToBool(paint.getPathEffect()) || (flags & DrawFlags::kIgnorePathEffect));
1256*c8dee2aaSAndroid Build Coastguard Worker 
1257*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Some renderer decisions could depend on the clip (see PathAtlas::addShape for
1258*c8dee2aaSAndroid Build Coastguard Worker     // one workaround) so we should figure out how to remove this circular dependency.
1259*c8dee2aaSAndroid Build Coastguard Worker 
1260*c8dee2aaSAndroid Build Coastguard Worker     // We assume that we will receive a renderer, or a PathAtlas. If it's a PathAtlas,
1261*c8dee2aaSAndroid Build Coastguard Worker     // then we assume that the renderer chosen in PathAtlas::addShape() will have
1262*c8dee2aaSAndroid Build Coastguard Worker     // single-channel coverage, require AA bounds outsetting, and have a single renderStep.
1263*c8dee2aaSAndroid Build Coastguard Worker     auto [renderer, pathAtlas] =
1264*c8dee2aaSAndroid Build Coastguard Worker             this->chooseRenderer(localToDevice, geometry, style, /*requireMSAA=*/false);
1265*c8dee2aaSAndroid Build Coastguard Worker     if (!renderer && !pathAtlas) {
1266*c8dee2aaSAndroid Build Coastguard Worker         SKGPU_LOG_W("Skipping draw with no supported renderer or PathAtlas.");
1267*c8dee2aaSAndroid Build Coastguard Worker         return;
1268*c8dee2aaSAndroid Build Coastguard Worker     }
1269*c8dee2aaSAndroid Build Coastguard Worker 
1270*c8dee2aaSAndroid Build Coastguard Worker     // Calculate the clipped bounds of the draw and determine the clip elements that affect the
1271*c8dee2aaSAndroid Build Coastguard Worker     // draw without updating the clip stack.
1272*c8dee2aaSAndroid Build Coastguard Worker     const bool outsetBoundsForAA = renderer ? renderer->outsetBoundsForAA() : true;
1273*c8dee2aaSAndroid Build Coastguard Worker     ClipStack::ElementList clipElements;
1274*c8dee2aaSAndroid Build Coastguard Worker     const Clip clip =
1275*c8dee2aaSAndroid Build Coastguard Worker             fClip.visitClipStackForDraw(localToDevice, geometry, style, outsetBoundsForAA,
1276*c8dee2aaSAndroid Build Coastguard Worker                                         &clipElements);
1277*c8dee2aaSAndroid Build Coastguard Worker     if (clip.isClippedOut()) {
1278*c8dee2aaSAndroid Build Coastguard Worker         // Clipped out, so don't record anything.
1279*c8dee2aaSAndroid Build Coastguard Worker         return;
1280*c8dee2aaSAndroid Build Coastguard Worker     }
1281*c8dee2aaSAndroid Build Coastguard Worker 
1282*c8dee2aaSAndroid Build Coastguard Worker     // Figure out what dst color requirements we have, if any.
1283*c8dee2aaSAndroid Build Coastguard Worker     DstReadRequirement dstReadReq = DstReadRequirement::kNone;
1284*c8dee2aaSAndroid Build Coastguard Worker     const SkBlenderBase* blender = as_BB(paint.getBlender());
1285*c8dee2aaSAndroid Build Coastguard Worker     const std::optional<SkBlendMode> blendMode = blender ? blender->asBlendMode()
1286*c8dee2aaSAndroid Build Coastguard Worker                                                          : SkBlendMode::kSrcOver;
1287*c8dee2aaSAndroid Build Coastguard Worker     Coverage rendererCoverage = renderer ? renderer->coverage()
1288*c8dee2aaSAndroid Build Coastguard Worker                                          : Coverage::kSingleChannel;
1289*c8dee2aaSAndroid Build Coastguard Worker     if ((clip.shader() || !clip.analyticClip().isEmpty()) && rendererCoverage == Coverage::kNone) {
1290*c8dee2aaSAndroid Build Coastguard Worker         // Must upgrade to single channel coverage if there is a clip shader or analytic clip;
1291*c8dee2aaSAndroid Build Coastguard Worker         // but preserve LCD coverage if the Renderer uses that.
1292*c8dee2aaSAndroid Build Coastguard Worker         rendererCoverage = Coverage::kSingleChannel;
1293*c8dee2aaSAndroid Build Coastguard Worker     }
1294*c8dee2aaSAndroid Build Coastguard Worker     dstReadReq = GetDstReadRequirement(fRecorder->priv().caps(), blendMode, rendererCoverage);
1295*c8dee2aaSAndroid Build Coastguard Worker 
1296*c8dee2aaSAndroid Build Coastguard Worker     // A primitive blender should be ignored if there is no primitive color to blend against.
1297*c8dee2aaSAndroid Build Coastguard Worker     // Additionally, if a renderer emits a primitive color, then a null primitive blender should
1298*c8dee2aaSAndroid Build Coastguard Worker     // be interpreted as SrcOver blending mode.
1299*c8dee2aaSAndroid Build Coastguard Worker     if (!renderer || !renderer->emitsPrimitiveColor()) {
1300*c8dee2aaSAndroid Build Coastguard Worker         primitiveBlender = nullptr;
1301*c8dee2aaSAndroid Build Coastguard Worker     } else if (!SkToBool(primitiveBlender)) {
1302*c8dee2aaSAndroid Build Coastguard Worker         primitiveBlender = SkBlender::Mode(SkBlendMode::kSrcOver);
1303*c8dee2aaSAndroid Build Coastguard Worker     }
1304*c8dee2aaSAndroid Build Coastguard Worker 
1305*c8dee2aaSAndroid Build Coastguard Worker     PaintParams shading{paint,
1306*c8dee2aaSAndroid Build Coastguard Worker                         std::move(primitiveBlender),
1307*c8dee2aaSAndroid Build Coastguard Worker                         clip.analyticClip(),
1308*c8dee2aaSAndroid Build Coastguard Worker                         sk_ref_sp(clip.shader()),
1309*c8dee2aaSAndroid Build Coastguard Worker                         dstReadReq,
1310*c8dee2aaSAndroid Build Coastguard Worker                         skipColorXform};
1311*c8dee2aaSAndroid Build Coastguard Worker     const bool dependsOnDst = paint_depends_on_dst(shading) ||
1312*c8dee2aaSAndroid Build Coastguard Worker                               clip.shader() || !clip.analyticClip().isEmpty();
1313*c8dee2aaSAndroid Build Coastguard Worker 
1314*c8dee2aaSAndroid Build Coastguard Worker     // Some shapes and styles combine multiple draws so the total render step count is split between
1315*c8dee2aaSAndroid Build Coastguard Worker     // the main renderer and possibly a secondaryRenderer.
1316*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec::Style styleType = style.getStyle();
1317*c8dee2aaSAndroid Build Coastguard Worker     const Renderer* secondaryRenderer = nullptr;
1318*c8dee2aaSAndroid Build Coastguard Worker     Rect innerFillBounds = Rect::InfiniteInverted();
1319*c8dee2aaSAndroid Build Coastguard Worker     if (renderer) {
1320*c8dee2aaSAndroid Build Coastguard Worker         if (styleType == SkStrokeRec::kStrokeAndFill_Style) {
1321*c8dee2aaSAndroid Build Coastguard Worker             // `renderer` covers the fill, `secondaryRenderer` covers the stroke
1322*c8dee2aaSAndroid Build Coastguard Worker             secondaryRenderer = fRecorder->priv().rendererProvider()->tessellatedStrokes();
1323*c8dee2aaSAndroid Build Coastguard Worker         } else if (style.isFillStyle() && renderer->useNonAAInnerFill() && !dependsOnDst) {
1324*c8dee2aaSAndroid Build Coastguard Worker             // `renderer` opts into drawing a non-AA inner fill
1325*c8dee2aaSAndroid Build Coastguard Worker             innerFillBounds = get_inner_bounds(geometry, localToDevice);
1326*c8dee2aaSAndroid Build Coastguard Worker             if (!innerFillBounds.isEmptyNegativeOrNaN()) {
1327*c8dee2aaSAndroid Build Coastguard Worker                 secondaryRenderer = fRecorder->priv().rendererProvider()->nonAABounds();
1328*c8dee2aaSAndroid Build Coastguard Worker             }
1329*c8dee2aaSAndroid Build Coastguard Worker         }
1330*c8dee2aaSAndroid Build Coastguard Worker     }
1331*c8dee2aaSAndroid Build Coastguard Worker     const int numNewRenderSteps = (renderer ? renderer->numRenderSteps() : 1) +
1332*c8dee2aaSAndroid Build Coastguard Worker                                   (secondaryRenderer ? secondaryRenderer->numRenderSteps() : 0);
1333*c8dee2aaSAndroid Build Coastguard Worker 
1334*c8dee2aaSAndroid Build Coastguard Worker     // Decide if we have any reason to flush pending work. We want to flush before updating the clip
1335*c8dee2aaSAndroid Build Coastguard Worker     // state or making any permanent changes to a path atlas, since otherwise clip operations and/or
1336*c8dee2aaSAndroid Build Coastguard Worker     // atlas entries for the current draw will be flushed.
1337*c8dee2aaSAndroid Build Coastguard Worker     const bool needsFlush = this->needsFlushBeforeDraw(numNewRenderSteps, dstReadReq);
1338*c8dee2aaSAndroid Build Coastguard Worker     if (needsFlush) {
1339*c8dee2aaSAndroid Build Coastguard Worker         if (pathAtlas != nullptr) {
1340*c8dee2aaSAndroid Build Coastguard Worker             // We need to flush work for all devices associated with the current Recorder.
1341*c8dee2aaSAndroid Build Coastguard Worker             // Otherwise we may end up with outstanding draws that depend on past atlas state.
1342*c8dee2aaSAndroid Build Coastguard Worker             fRecorder->priv().flushTrackedDevices();
1343*c8dee2aaSAndroid Build Coastguard Worker         } else {
1344*c8dee2aaSAndroid Build Coastguard Worker             this->flushPendingWorkToRecorder();
1345*c8dee2aaSAndroid Build Coastguard Worker         }
1346*c8dee2aaSAndroid Build Coastguard Worker     }
1347*c8dee2aaSAndroid Build Coastguard Worker 
1348*c8dee2aaSAndroid Build Coastguard Worker     // If an atlas path renderer was chosen we need to insert the shape into the atlas and schedule
1349*c8dee2aaSAndroid Build Coastguard Worker     // it to be drawn.
1350*c8dee2aaSAndroid Build Coastguard Worker     std::optional<PathAtlas::MaskAndOrigin> atlasMask;  // only used if `pathAtlas != nullptr`
1351*c8dee2aaSAndroid Build Coastguard Worker     if (pathAtlas != nullptr) {
1352*c8dee2aaSAndroid Build Coastguard Worker         std::tie(renderer, atlasMask) = pathAtlas->addShape(clip.transformedShapeBounds(),
1353*c8dee2aaSAndroid Build Coastguard Worker                                                             geometry.shape(),
1354*c8dee2aaSAndroid Build Coastguard Worker                                                             localToDevice,
1355*c8dee2aaSAndroid Build Coastguard Worker                                                             style);
1356*c8dee2aaSAndroid Build Coastguard Worker 
1357*c8dee2aaSAndroid Build Coastguard Worker         // If there was no space in the atlas and we haven't flushed already, then flush pending
1358*c8dee2aaSAndroid Build Coastguard Worker         // work to clear up space in the atlas. If we had already flushed once (which would have
1359*c8dee2aaSAndroid Build Coastguard Worker         // cleared the atlas) then the atlas is too small for this shape.
1360*c8dee2aaSAndroid Build Coastguard Worker         if (!atlasMask && !needsFlush) {
1361*c8dee2aaSAndroid Build Coastguard Worker             // We need to flush work for all devices associated with the current Recorder.
1362*c8dee2aaSAndroid Build Coastguard Worker             // Otherwise we may end up with outstanding draws that depend on past atlas state.
1363*c8dee2aaSAndroid Build Coastguard Worker             fRecorder->priv().flushTrackedDevices();
1364*c8dee2aaSAndroid Build Coastguard Worker 
1365*c8dee2aaSAndroid Build Coastguard Worker             // Try inserting the shape again.
1366*c8dee2aaSAndroid Build Coastguard Worker             std::tie(renderer, atlasMask) = pathAtlas->addShape(clip.transformedShapeBounds(),
1367*c8dee2aaSAndroid Build Coastguard Worker                                                                 geometry.shape(),
1368*c8dee2aaSAndroid Build Coastguard Worker                                                                 localToDevice,
1369*c8dee2aaSAndroid Build Coastguard Worker                                                                 style);
1370*c8dee2aaSAndroid Build Coastguard Worker         }
1371*c8dee2aaSAndroid Build Coastguard Worker 
1372*c8dee2aaSAndroid Build Coastguard Worker         if (!atlasMask) {
1373*c8dee2aaSAndroid Build Coastguard Worker             SKGPU_LOG_E("Failed to add shape to atlas!");
1374*c8dee2aaSAndroid Build Coastguard Worker             // TODO(b/285195175): This can happen if the atlas is not large enough or a compatible
1375*c8dee2aaSAndroid Build Coastguard Worker             // atlas texture cannot be created. Handle the first case in `chooseRenderer` and make
1376*c8dee2aaSAndroid Build Coastguard Worker             // sure that the atlas path renderer is not chosen if the path is larger than the atlas
1377*c8dee2aaSAndroid Build Coastguard Worker             // texture.
1378*c8dee2aaSAndroid Build Coastguard Worker             return;
1379*c8dee2aaSAndroid Build Coastguard Worker         }
1380*c8dee2aaSAndroid Build Coastguard Worker         // Since addShape() was successful we should have a valid Renderer now.
1381*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(renderer && renderer->numRenderSteps() == 1 && !renderer->emitsPrimitiveColor());
1382*c8dee2aaSAndroid Build Coastguard Worker     }
1383*c8dee2aaSAndroid Build Coastguard Worker 
1384*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
1385*c8dee2aaSAndroid Build Coastguard Worker     // Renderers and their component RenderSteps have flexibility in defining their
1386*c8dee2aaSAndroid Build Coastguard Worker     // DepthStencilSettings. However, the clipping and ordering managed between Device and ClipStack
1387*c8dee2aaSAndroid Build Coastguard Worker     // requires that only GREATER or GEQUAL depth tests are used for draws recorded through the
1388*c8dee2aaSAndroid Build Coastguard Worker     // client-facing, painters-order-oriented API. We assert here vs. in Renderer's constructor to
1389*c8dee2aaSAndroid Build Coastguard Worker     // allow internal-oriented Renderers that are never selected for a "regular" draw call to have
1390*c8dee2aaSAndroid Build Coastguard Worker     // more flexibility in their settings.
1391*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(renderer);
1392*c8dee2aaSAndroid Build Coastguard Worker     for (const RenderStep* step : renderer->steps()) {
1393*c8dee2aaSAndroid Build Coastguard Worker         auto dss = step->depthStencilSettings();
1394*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT((!step->performsShading() || dss.fDepthTestEnabled) &&
1395*c8dee2aaSAndroid Build Coastguard Worker                  (!dss.fDepthTestEnabled ||
1396*c8dee2aaSAndroid Build Coastguard Worker                   dss.fDepthCompareOp == CompareOp::kGreater ||
1397*c8dee2aaSAndroid Build Coastguard Worker                   dss.fDepthCompareOp == CompareOp::kGEqual));
1398*c8dee2aaSAndroid Build Coastguard Worker     }
1399*c8dee2aaSAndroid Build Coastguard Worker #endif
1400*c8dee2aaSAndroid Build Coastguard Worker 
1401*c8dee2aaSAndroid Build Coastguard Worker     // Update the clip stack after issuing a flush (if it was needed). A draw will be recorded after
1402*c8dee2aaSAndroid Build Coastguard Worker     // this point.
1403*c8dee2aaSAndroid Build Coastguard Worker     DrawOrder order(fCurrentDepth.next());
1404*c8dee2aaSAndroid Build Coastguard Worker     CompressedPaintersOrder clipOrder = fClip.updateClipStateForDraw(
1405*c8dee2aaSAndroid Build Coastguard Worker             clip, clipElements, fColorDepthBoundsManager.get(), order.depth());
1406*c8dee2aaSAndroid Build Coastguard Worker 
1407*c8dee2aaSAndroid Build Coastguard Worker     // A draw's order always depends on the clips that must be drawn before it
1408*c8dee2aaSAndroid Build Coastguard Worker     order.dependsOnPaintersOrder(clipOrder);
1409*c8dee2aaSAndroid Build Coastguard Worker     // If a draw is not opaque, it must be drawn after the most recent draw it intersects with in
1410*c8dee2aaSAndroid Build Coastguard Worker     // order to blend correctly.
1411*c8dee2aaSAndroid Build Coastguard Worker     if (rendererCoverage != Coverage::kNone || dependsOnDst) {
1412*c8dee2aaSAndroid Build Coastguard Worker         CompressedPaintersOrder prevDraw =
1413*c8dee2aaSAndroid Build Coastguard Worker             fColorDepthBoundsManager->getMostRecentDraw(clip.drawBounds());
1414*c8dee2aaSAndroid Build Coastguard Worker         order.dependsOnPaintersOrder(prevDraw);
1415*c8dee2aaSAndroid Build Coastguard Worker     }
1416*c8dee2aaSAndroid Build Coastguard Worker 
1417*c8dee2aaSAndroid Build Coastguard Worker     // Now that the base paint order and draw bounds are finalized, if the Renderer relies on the
1418*c8dee2aaSAndroid Build Coastguard Worker     // stencil attachment, we compute a secondary sorting field to allow disjoint draws to reorder
1419*c8dee2aaSAndroid Build Coastguard Worker     // the RenderSteps across draws instead of in sequence for each draw.
1420*c8dee2aaSAndroid Build Coastguard Worker     if (renderer->depthStencilFlags() & DepthStencilFlags::kStencil) {
1421*c8dee2aaSAndroid Build Coastguard Worker         DisjointStencilIndex setIndex = fDisjointStencilSet->add(order.paintOrder(),
1422*c8dee2aaSAndroid Build Coastguard Worker                                                                  clip.drawBounds());
1423*c8dee2aaSAndroid Build Coastguard Worker         order.dependsOnStencil(setIndex);
1424*c8dee2aaSAndroid Build Coastguard Worker     }
1425*c8dee2aaSAndroid Build Coastguard Worker 
1426*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/330864257): This is an extra traversal of all paint effects, that can be avoided when
1427*c8dee2aaSAndroid Build Coastguard Worker     // the paint key itself is determined inside this function.
1428*c8dee2aaSAndroid Build Coastguard Worker     shading.notifyImagesInUse(fRecorder, fDC.get());
1429*c8dee2aaSAndroid Build Coastguard Worker 
1430*c8dee2aaSAndroid Build Coastguard Worker     // If an atlas path renderer was chosen, then record a single CoverageMaskShape draw.
1431*c8dee2aaSAndroid Build Coastguard Worker     // The shape will be scheduled to be rendered or uploaded into the atlas during the
1432*c8dee2aaSAndroid Build Coastguard Worker     // next invocation of flushPendingWorkToRecorder().
1433*c8dee2aaSAndroid Build Coastguard Worker     if (pathAtlas != nullptr) {
1434*c8dee2aaSAndroid Build Coastguard Worker         // Record the draw as a fill since stroking is handled by the atlas render/upload.
1435*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(atlasMask.has_value());
1436*c8dee2aaSAndroid Build Coastguard Worker         auto [mask, origin] = *atlasMask;
1437*c8dee2aaSAndroid Build Coastguard Worker         fDC->recordDraw(renderer, Transform::Translate(origin.fX, origin.fY), Geometry(mask),
1438*c8dee2aaSAndroid Build Coastguard Worker                         clip, order, &shading, nullptr);
1439*c8dee2aaSAndroid Build Coastguard Worker     } else {
1440*c8dee2aaSAndroid Build Coastguard Worker         if (styleType == SkStrokeRec::kStroke_Style ||
1441*c8dee2aaSAndroid Build Coastguard Worker             styleType == SkStrokeRec::kHairline_Style ||
1442*c8dee2aaSAndroid Build Coastguard Worker             styleType == SkStrokeRec::kStrokeAndFill_Style) {
1443*c8dee2aaSAndroid Build Coastguard Worker             // For stroke-and-fill, 'renderer' is used for the fill and we always use the
1444*c8dee2aaSAndroid Build Coastguard Worker             // TessellatedStrokes renderer; for stroke and hairline, 'renderer' is used.
1445*c8dee2aaSAndroid Build Coastguard Worker             StrokeStyle stroke(style.getWidth(), style.getMiter(), style.getJoin(), style.getCap());
1446*c8dee2aaSAndroid Build Coastguard Worker             fDC->recordDraw(styleType == SkStrokeRec::kStrokeAndFill_Style
1447*c8dee2aaSAndroid Build Coastguard Worker                                    ? fRecorder->priv().rendererProvider()->tessellatedStrokes()
1448*c8dee2aaSAndroid Build Coastguard Worker                                    : renderer,
1449*c8dee2aaSAndroid Build Coastguard Worker                             localToDevice, geometry, clip, order, &shading, &stroke);
1450*c8dee2aaSAndroid Build Coastguard Worker         }
1451*c8dee2aaSAndroid Build Coastguard Worker         if (styleType == SkStrokeRec::kFill_Style ||
1452*c8dee2aaSAndroid Build Coastguard Worker             styleType == SkStrokeRec::kStrokeAndFill_Style) {
1453*c8dee2aaSAndroid Build Coastguard Worker             // Possibly record an additional draw using the non-AA bounds renderer to fill the
1454*c8dee2aaSAndroid Build Coastguard Worker             // interior with a renderer that can disable blending entirely.
1455*c8dee2aaSAndroid Build Coastguard Worker             if (!innerFillBounds.isEmptyNegativeOrNaN()) {
1456*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!dependsOnDst && renderer->useNonAAInnerFill());
1457*c8dee2aaSAndroid Build Coastguard Worker                 DrawOrder orderWithoutCoverage{order.depth()};
1458*c8dee2aaSAndroid Build Coastguard Worker                 orderWithoutCoverage.dependsOnPaintersOrder(clipOrder);
1459*c8dee2aaSAndroid Build Coastguard Worker                 fDC->recordDraw(fRecorder->priv().rendererProvider()->nonAABounds(),
1460*c8dee2aaSAndroid Build Coastguard Worker                                 localToDevice, Geometry(Shape(innerFillBounds)),
1461*c8dee2aaSAndroid Build Coastguard Worker                                 clip, orderWithoutCoverage, &shading, nullptr);
1462*c8dee2aaSAndroid Build Coastguard Worker                 // Force the coverage draw to come after the non-AA draw in order to benefit from
1463*c8dee2aaSAndroid Build Coastguard Worker                 // early depth testing.
1464*c8dee2aaSAndroid Build Coastguard Worker                 order.dependsOnPaintersOrder(orderWithoutCoverage.paintOrder());
1465*c8dee2aaSAndroid Build Coastguard Worker             }
1466*c8dee2aaSAndroid Build Coastguard Worker             fDC->recordDraw(renderer, localToDevice, geometry, clip, order, &shading, nullptr);
1467*c8dee2aaSAndroid Build Coastguard Worker         }
1468*c8dee2aaSAndroid Build Coastguard Worker     }
1469*c8dee2aaSAndroid Build Coastguard Worker 
1470*c8dee2aaSAndroid Build Coastguard Worker     // Post-draw book keeping (bounds manager, depth tracking, etc.)
1471*c8dee2aaSAndroid Build Coastguard Worker     fColorDepthBoundsManager->recordDraw(clip.drawBounds(), order.paintOrder());
1472*c8dee2aaSAndroid Build Coastguard Worker     fCurrentDepth = order.depth();
1473*c8dee2aaSAndroid Build Coastguard Worker 
1474*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/238758897): When we enable layer elision that depends on draws not overlapping, we
1475*c8dee2aaSAndroid Build Coastguard Worker     // can use the `getMostRecentDraw()` query to determine that, although that will mean querying
1476*c8dee2aaSAndroid Build Coastguard Worker     // even if the draw does not depend on dst (so should be only be used when the Device is an
1477*c8dee2aaSAndroid Build Coastguard Worker     // elision candidate).
1478*c8dee2aaSAndroid Build Coastguard Worker }
1479*c8dee2aaSAndroid Build Coastguard Worker 
drawClipShape(const Transform & localToDevice,const Shape & shape,const Clip & clip,DrawOrder order)1480*c8dee2aaSAndroid Build Coastguard Worker void Device::drawClipShape(const Transform& localToDevice,
1481*c8dee2aaSAndroid Build Coastguard Worker                            const Shape& shape,
1482*c8dee2aaSAndroid Build Coastguard Worker                            const Clip& clip,
1483*c8dee2aaSAndroid Build Coastguard Worker                            DrawOrder order) {
1484*c8dee2aaSAndroid Build Coastguard Worker     // A clip draw's state is almost fully defined by the ClipStack. The only thing we need
1485*c8dee2aaSAndroid Build Coastguard Worker     // to account for is selecting a Renderer and tracking the stencil buffer usage.
1486*c8dee2aaSAndroid Build Coastguard Worker     Geometry geometry{shape};
1487*c8dee2aaSAndroid Build Coastguard Worker     auto [renderer, pathAtlas] = this->chooseRenderer(localToDevice,
1488*c8dee2aaSAndroid Build Coastguard Worker                                                       geometry,
1489*c8dee2aaSAndroid Build Coastguard Worker                                                       DefaultFillStyle(),
1490*c8dee2aaSAndroid Build Coastguard Worker                                                       /*requireMSAA=*/true);
1491*c8dee2aaSAndroid Build Coastguard Worker     if (!renderer) {
1492*c8dee2aaSAndroid Build Coastguard Worker         SKGPU_LOG_W("Skipping clip with no supported path renderer.");
1493*c8dee2aaSAndroid Build Coastguard Worker         return;
1494*c8dee2aaSAndroid Build Coastguard Worker     } else if (renderer->depthStencilFlags() & DepthStencilFlags::kStencil) {
1495*c8dee2aaSAndroid Build Coastguard Worker         DisjointStencilIndex setIndex = fDisjointStencilSet->add(order.paintOrder(),
1496*c8dee2aaSAndroid Build Coastguard Worker                                                                  clip.drawBounds());
1497*c8dee2aaSAndroid Build Coastguard Worker         order.dependsOnStencil(setIndex);
1498*c8dee2aaSAndroid Build Coastguard Worker     }
1499*c8dee2aaSAndroid Build Coastguard Worker 
1500*c8dee2aaSAndroid Build Coastguard Worker     // This call represents one of the deferred clip shapes that's already pessimistically counted
1501*c8dee2aaSAndroid Build Coastguard Worker     // in needsFlushBeforeDraw(), so the DrawContext should have room to add it.
1502*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fDC->pendingRenderSteps() + renderer->numRenderSteps() < DrawList::kMaxRenderSteps);
1503*c8dee2aaSAndroid Build Coastguard Worker 
1504*c8dee2aaSAndroid Build Coastguard Worker     // Anti-aliased clipping requires the renderer to use MSAA to modify the depth per sample, so
1505*c8dee2aaSAndroid Build Coastguard Worker     // analytic coverage renderers cannot be used.
1506*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(renderer->coverage() == Coverage::kNone && renderer->requiresMSAA());
1507*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(pathAtlas == nullptr);
1508*c8dee2aaSAndroid Build Coastguard Worker 
1509*c8dee2aaSAndroid Build Coastguard Worker     // Clips draws are depth-only (null PaintParams), and filled (null StrokeStyle).
1510*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Remove this CPU-transform once perspective is supported for all path renderers
1511*c8dee2aaSAndroid Build Coastguard Worker     if (localToDevice.type() == Transform::Type::kPerspective) {
1512*c8dee2aaSAndroid Build Coastguard Worker         SkPath devicePath = geometry.shape().asPath();
1513*c8dee2aaSAndroid Build Coastguard Worker         devicePath.transform(localToDevice.matrix().asM33());
1514*c8dee2aaSAndroid Build Coastguard Worker         fDC->recordDraw(renderer, Transform::Identity(), Geometry(Shape(devicePath)), clip, order,
1515*c8dee2aaSAndroid Build Coastguard Worker                         nullptr, nullptr);
1516*c8dee2aaSAndroid Build Coastguard Worker     } else {
1517*c8dee2aaSAndroid Build Coastguard Worker         fDC->recordDraw(renderer, localToDevice, geometry, clip, order, nullptr, nullptr);
1518*c8dee2aaSAndroid Build Coastguard Worker     }
1519*c8dee2aaSAndroid Build Coastguard Worker     // This ensures that draws recorded after this clip shape has been popped off the stack will
1520*c8dee2aaSAndroid Build Coastguard Worker     // be unaffected by the Z value the clip shape wrote to the depth attachment.
1521*c8dee2aaSAndroid Build Coastguard Worker     if (order.depth() > fCurrentDepth) {
1522*c8dee2aaSAndroid Build Coastguard Worker         fCurrentDepth = order.depth();
1523*c8dee2aaSAndroid Build Coastguard Worker     }
1524*c8dee2aaSAndroid Build Coastguard Worker }
1525*c8dee2aaSAndroid Build Coastguard Worker 
1526*c8dee2aaSAndroid Build Coastguard Worker // TODO: Currently all Renderers are always defined, but with config options and caps that may not
1527*c8dee2aaSAndroid Build Coastguard Worker // be the case, in which case chooseRenderer() will have to go through compatible choices.
chooseRenderer(const Transform & localToDevice,const Geometry & geometry,const SkStrokeRec & style,bool requireMSAA) const1528*c8dee2aaSAndroid Build Coastguard Worker std::pair<const Renderer*, PathAtlas*> Device::chooseRenderer(const Transform& localToDevice,
1529*c8dee2aaSAndroid Build Coastguard Worker                                                               const Geometry& geometry,
1530*c8dee2aaSAndroid Build Coastguard Worker                                                               const SkStrokeRec& style,
1531*c8dee2aaSAndroid Build Coastguard Worker                                                               bool requireMSAA) const {
1532*c8dee2aaSAndroid Build Coastguard Worker     const RendererProvider* renderers = fRecorder->priv().rendererProvider();
1533*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(renderers);
1534*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec::Style type = style.getStyle();
1535*c8dee2aaSAndroid Build Coastguard Worker 
1536*c8dee2aaSAndroid Build Coastguard Worker     if (geometry.isSubRun()) {
1537*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!requireMSAA);
1538*c8dee2aaSAndroid Build Coastguard Worker         sktext::gpu::RendererData rendererData = geometry.subRunData().rendererData();
1539*c8dee2aaSAndroid Build Coastguard Worker         if (!rendererData.isSDF) {
1540*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->bitmapText(rendererData.isLCD, rendererData.maskFormat), nullptr};
1541*c8dee2aaSAndroid Build Coastguard Worker         }
1542*c8dee2aaSAndroid Build Coastguard Worker         // Even though the SkPaint can request subpixel rendering, we still need to match
1543*c8dee2aaSAndroid Build Coastguard Worker         // this with the pixel geometry.
1544*c8dee2aaSAndroid Build Coastguard Worker         bool useLCD = rendererData.isLCD &&
1545*c8dee2aaSAndroid Build Coastguard Worker                       geometry.subRunData().pixelGeometry() != kUnknown_SkPixelGeometry;
1546*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->sdfText(useLCD), nullptr};
1547*c8dee2aaSAndroid Build Coastguard Worker     } else if (geometry.isVertices()) {
1548*c8dee2aaSAndroid Build Coastguard Worker         SkVerticesPriv info(geometry.vertices()->priv());
1549*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->vertices(info.mode(), info.hasColors(), info.hasTexCoords()), nullptr};
1550*c8dee2aaSAndroid Build Coastguard Worker     } else if (geometry.isCoverageMaskShape()) {
1551*c8dee2aaSAndroid Build Coastguard Worker         // drawCoverageMask() passes in CoverageMaskShapes that reference a provided texture.
1552*c8dee2aaSAndroid Build Coastguard Worker         // The CoverageMask renderer can also be chosen later on if the shape is assigned to
1553*c8dee2aaSAndroid Build Coastguard Worker         // to be rendered into the PathAtlas, in which case the 2nd return value is non-null.
1554*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->coverageMask(), nullptr};
1555*c8dee2aaSAndroid Build Coastguard Worker     } else if (geometry.isEdgeAAQuad()) {
1556*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!requireMSAA && style.isFillStyle());
1557*c8dee2aaSAndroid Build Coastguard Worker         // handled by specialized system, simplified from rects and round rects
1558*c8dee2aaSAndroid Build Coastguard Worker         const EdgeAAQuad& quad = geometry.edgeAAQuad();
1559*c8dee2aaSAndroid Build Coastguard Worker         if (quad.isRect() && quad.edgeFlags() == EdgeAAQuad::Flags::kNone) {
1560*c8dee2aaSAndroid Build Coastguard Worker             // For non-AA rectangular quads, it can always use a coverage-less renderer; there's no
1561*c8dee2aaSAndroid Build Coastguard Worker             // need to check for pixel alignment to avoid popping if MSAA is turned on because quad
1562*c8dee2aaSAndroid Build Coastguard Worker             // tile edges will seam with each in either mode.
1563*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->nonAABounds(), nullptr};
1564*c8dee2aaSAndroid Build Coastguard Worker         } else {
1565*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->perEdgeAAQuad(), nullptr};
1566*c8dee2aaSAndroid Build Coastguard Worker         }
1567*c8dee2aaSAndroid Build Coastguard Worker     } else if (geometry.isAnalyticBlur()) {
1568*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->analyticBlur(), nullptr};
1569*c8dee2aaSAndroid Build Coastguard Worker     } else if (!geometry.isShape()) {
1570*c8dee2aaSAndroid Build Coastguard Worker         // We must account for new Geometry types with specific Renderers
1571*c8dee2aaSAndroid Build Coastguard Worker         return {nullptr, nullptr};
1572*c8dee2aaSAndroid Build Coastguard Worker     }
1573*c8dee2aaSAndroid Build Coastguard Worker 
1574*c8dee2aaSAndroid Build Coastguard Worker     const Shape& shape = geometry.shape();
1575*c8dee2aaSAndroid Build Coastguard Worker     // We can't use this renderer if we require MSAA for an effect (i.e. clipping or stroke+fill).
1576*c8dee2aaSAndroid Build Coastguard Worker     if (!requireMSAA && is_simple_shape(shape, type)) {
1577*c8dee2aaSAndroid Build Coastguard Worker         // For pixel-aligned rects, use the the non-AA bounds renderer to avoid triggering any
1578*c8dee2aaSAndroid Build Coastguard Worker         // dst-read requirement due to src blending.
1579*c8dee2aaSAndroid Build Coastguard Worker         bool pixelAlignedRect = false;
1580*c8dee2aaSAndroid Build Coastguard Worker         if (shape.isRect() && style.isFillStyle() &&
1581*c8dee2aaSAndroid Build Coastguard Worker             localToDevice.type() <= Transform::Type::kRectStaysRect) {
1582*c8dee2aaSAndroid Build Coastguard Worker             Rect devRect = localToDevice.mapRect(shape.rect());
1583*c8dee2aaSAndroid Build Coastguard Worker             pixelAlignedRect = devRect.nearlyEquals(devRect.makeRound());
1584*c8dee2aaSAndroid Build Coastguard Worker         }
1585*c8dee2aaSAndroid Build Coastguard Worker 
1586*c8dee2aaSAndroid Build Coastguard Worker         if (shape.isEmpty() || pixelAlignedRect) {
1587*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!shape.isEmpty() || shape.inverted());
1588*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->nonAABounds(), nullptr};
1589*c8dee2aaSAndroid Build Coastguard Worker         } else {
1590*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->analyticRRect(), nullptr};
1591*c8dee2aaSAndroid Build Coastguard Worker         }
1592*c8dee2aaSAndroid Build Coastguard Worker     }
1593*c8dee2aaSAndroid Build Coastguard Worker 
1594*c8dee2aaSAndroid Build Coastguard Worker     if (!requireMSAA && shape.isArc() &&
1595*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(shape.arc().oval().width(), shape.arc().oval().height()) &&
1596*c8dee2aaSAndroid Build Coastguard Worker         SkScalarAbs(shape.arc().sweepAngle()) < 360.f &&
1597*c8dee2aaSAndroid Build Coastguard Worker         localToDevice.type() <= Transform::Type::kAffine) {
1598*c8dee2aaSAndroid Build Coastguard Worker         float maxScale, minScale;
1599*c8dee2aaSAndroid Build Coastguard Worker         std::tie(maxScale, minScale) = localToDevice.scaleFactors({0, 0});
1600*c8dee2aaSAndroid Build Coastguard Worker         if (SkScalarNearlyEqual(maxScale, minScale)) {
1601*c8dee2aaSAndroid Build Coastguard Worker             // Arc support depends on the style.
1602*c8dee2aaSAndroid Build Coastguard Worker             SkStrokeRec::Style recStyle = style.getStyle();
1603*c8dee2aaSAndroid Build Coastguard Worker             switch (recStyle) {
1604*c8dee2aaSAndroid Build Coastguard Worker                 case SkStrokeRec::kStrokeAndFill_Style:
1605*c8dee2aaSAndroid Build Coastguard Worker                     // This produces a strange result that this op doesn't implement.
1606*c8dee2aaSAndroid Build Coastguard Worker                     break;
1607*c8dee2aaSAndroid Build Coastguard Worker                 case SkStrokeRec::kFill_Style:
1608*c8dee2aaSAndroid Build Coastguard Worker                     return {renderers->circularArc(), nullptr};
1609*c8dee2aaSAndroid Build Coastguard Worker                 case SkStrokeRec::kStroke_Style:
1610*c8dee2aaSAndroid Build Coastguard Worker                 case SkStrokeRec::kHairline_Style:
1611*c8dee2aaSAndroid Build Coastguard Worker                     // Strokes that don't use the center point are supported with butt & round caps.
1612*c8dee2aaSAndroid Build Coastguard Worker                     bool isWedge = shape.arc().isWedge();
1613*c8dee2aaSAndroid Build Coastguard Worker                     bool isSquareCap = style.getCap() == SkPaint::kSquare_Cap;
1614*c8dee2aaSAndroid Build Coastguard Worker                     if (!isWedge && !isSquareCap) {
1615*c8dee2aaSAndroid Build Coastguard Worker                         return {renderers->circularArc(), nullptr};
1616*c8dee2aaSAndroid Build Coastguard Worker                     }
1617*c8dee2aaSAndroid Build Coastguard Worker                     break;
1618*c8dee2aaSAndroid Build Coastguard Worker             }
1619*c8dee2aaSAndroid Build Coastguard Worker         }
1620*c8dee2aaSAndroid Build Coastguard Worker     }
1621*c8dee2aaSAndroid Build Coastguard Worker 
1622*c8dee2aaSAndroid Build Coastguard Worker     // Path rendering options. For now the strategy is very simple and not optimal:
1623*c8dee2aaSAndroid Build Coastguard Worker     // I. Use tessellation if MSAA is required for an effect.
1624*c8dee2aaSAndroid Build Coastguard Worker     // II: otherwise:
1625*c8dee2aaSAndroid Build Coastguard Worker     //    1. Always use compute AA if supported unless it was excluded by ContextOptions or the
1626*c8dee2aaSAndroid Build Coastguard Worker     //       compute renderer cannot render the shape efficiently yet (based on the result of
1627*c8dee2aaSAndroid Build Coastguard Worker     //       `isSuitableForAtlasing`).
1628*c8dee2aaSAndroid Build Coastguard Worker     //    2. Fall back to CPU raster AA if hardware MSAA is disabled or it was explicitly requested
1629*c8dee2aaSAndroid Build Coastguard Worker     //       via ContextOptions.
1630*c8dee2aaSAndroid Build Coastguard Worker     //    3. Otherwise use tessellation.
1631*c8dee2aaSAndroid Build Coastguard Worker #if defined(GPU_TEST_UTILS)
1632*c8dee2aaSAndroid Build Coastguard Worker     PathRendererStrategy strategy = fRecorder->priv().caps()->requestedPathRendererStrategy();
1633*c8dee2aaSAndroid Build Coastguard Worker #else
1634*c8dee2aaSAndroid Build Coastguard Worker     PathRendererStrategy strategy = PathRendererStrategy::kDefault;
1635*c8dee2aaSAndroid Build Coastguard Worker #endif
1636*c8dee2aaSAndroid Build Coastguard Worker 
1637*c8dee2aaSAndroid Build Coastguard Worker     PathAtlas* pathAtlas = nullptr;
1638*c8dee2aaSAndroid Build Coastguard Worker     AtlasProvider* atlasProvider = fRecorder->priv().atlasProvider();
1639*c8dee2aaSAndroid Build Coastguard Worker 
1640*c8dee2aaSAndroid Build Coastguard Worker     // Prefer compute atlas draws if supported. This currently implicitly filters out clip draws as
1641*c8dee2aaSAndroid Build Coastguard Worker     // they require MSAA. Eventually we may want to route clip shapes to the atlas as well but not
1642*c8dee2aaSAndroid Build Coastguard Worker     // if hardware MSAA is required.
1643*c8dee2aaSAndroid Build Coastguard Worker     std::optional<Rect> drawBounds;
1644*c8dee2aaSAndroid Build Coastguard Worker     if (atlasProvider->isAvailable(AtlasProvider::PathAtlasFlags::kCompute) &&
1645*c8dee2aaSAndroid Build Coastguard Worker         use_compute_atlas_when_available(strategy)) {
1646*c8dee2aaSAndroid Build Coastguard Worker         PathAtlas* atlas = fDC->getComputePathAtlas(fRecorder);
1647*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(atlas);
1648*c8dee2aaSAndroid Build Coastguard Worker 
1649*c8dee2aaSAndroid Build Coastguard Worker         // Don't use the compute renderer if it can't handle the shape efficiently.
1650*c8dee2aaSAndroid Build Coastguard Worker         //
1651*c8dee2aaSAndroid Build Coastguard Worker         // Use the conservative clip bounds for a rough estimate of the mask size (this avoids
1652*c8dee2aaSAndroid Build Coastguard Worker         // having to evaluate the entire clip stack before choosing the renderer as it will have to
1653*c8dee2aaSAndroid Build Coastguard Worker         // get evaluated again if we fall back to a different renderer).
1654*c8dee2aaSAndroid Build Coastguard Worker         drawBounds = localToDevice.mapRect(shape.bounds());
1655*c8dee2aaSAndroid Build Coastguard Worker         if (atlas->isSuitableForAtlasing(*drawBounds, fClip.conservativeBounds())) {
1656*c8dee2aaSAndroid Build Coastguard Worker             pathAtlas = atlas;
1657*c8dee2aaSAndroid Build Coastguard Worker         }
1658*c8dee2aaSAndroid Build Coastguard Worker     }
1659*c8dee2aaSAndroid Build Coastguard Worker 
1660*c8dee2aaSAndroid Build Coastguard Worker     // Fall back to CPU rendered paths when multisampling is disabled and the compute atlas is not
1661*c8dee2aaSAndroid Build Coastguard Worker     // available.
1662*c8dee2aaSAndroid Build Coastguard Worker     // TODO: enable other uses of the software path renderer
1663*c8dee2aaSAndroid Build Coastguard Worker     if (!pathAtlas && atlasProvider->isAvailable(AtlasProvider::PathAtlasFlags::kRaster) &&
1664*c8dee2aaSAndroid Build Coastguard Worker         (strategy == PathRendererStrategy::kRasterAA ||
1665*c8dee2aaSAndroid Build Coastguard Worker          (strategy == PathRendererStrategy::kDefault && !fMSAASupported))) {
1666*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: RasterPathAtlas doesn't implement `PathAtlas::isSuitableForAtlasing` as it doesn't
1667*c8dee2aaSAndroid Build Coastguard Worker         // reject paths (unlike ComputePathAtlas).
1668*c8dee2aaSAndroid Build Coastguard Worker         pathAtlas = atlasProvider->getRasterPathAtlas();
1669*c8dee2aaSAndroid Build Coastguard Worker     }
1670*c8dee2aaSAndroid Build Coastguard Worker 
1671*c8dee2aaSAndroid Build Coastguard Worker     if (!requireMSAA && pathAtlas) {
1672*c8dee2aaSAndroid Build Coastguard Worker         // If we got here it means that we should draw with an atlas renderer if we can and avoid
1673*c8dee2aaSAndroid Build Coastguard Worker         // resorting to one of the tessellating techniques.
1674*c8dee2aaSAndroid Build Coastguard Worker         return {nullptr, pathAtlas};
1675*c8dee2aaSAndroid Build Coastguard Worker     }
1676*c8dee2aaSAndroid Build Coastguard Worker 
1677*c8dee2aaSAndroid Build Coastguard Worker     // If we got here, it requires tessellated path rendering or an MSAA technique applied to a
1678*c8dee2aaSAndroid Build Coastguard Worker     // simple shape (so we interpret them as paths to reduce the number of pipelines we need).
1679*c8dee2aaSAndroid Build Coastguard Worker 
1680*c8dee2aaSAndroid Build Coastguard Worker     // TODO: All shapes that select a tessellating path renderer need to be "pre-chopped" if they
1681*c8dee2aaSAndroid Build Coastguard Worker     // are large enough to exceed the fixed count tessellation limits. Fills are pre-chopped to the
1682*c8dee2aaSAndroid Build Coastguard Worker     // viewport bounds, strokes and stroke-and-fills are pre-chopped to the viewport bounds outset
1683*c8dee2aaSAndroid Build Coastguard Worker     // by the stroke radius (hence taking the whole style and not just its type).
1684*c8dee2aaSAndroid Build Coastguard Worker 
1685*c8dee2aaSAndroid Build Coastguard Worker     if (type == SkStrokeRec::kStroke_Style ||
1686*c8dee2aaSAndroid Build Coastguard Worker         type == SkStrokeRec::kHairline_Style) {
1687*c8dee2aaSAndroid Build Coastguard Worker         // Unlike in Ganesh, the HW stroke tessellator can work with arbitrary paints since the
1688*c8dee2aaSAndroid Build Coastguard Worker         // depth test prevents double-blending when there is transparency, thus we can HW stroke
1689*c8dee2aaSAndroid Build Coastguard Worker         // any path regardless of its paint.
1690*c8dee2aaSAndroid Build Coastguard Worker         // TODO: We treat inverse-filled strokes as regular strokes. We could handle them by
1691*c8dee2aaSAndroid Build Coastguard Worker         // stenciling first with the HW stroke tessellator and then covering their bounds, but
1692*c8dee2aaSAndroid Build Coastguard Worker         // inverse-filled strokes are not well-specified in our public canvas behavior so we may be
1693*c8dee2aaSAndroid Build Coastguard Worker         // able to remove it.
1694*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->tessellatedStrokes(), nullptr};
1695*c8dee2aaSAndroid Build Coastguard Worker     }
1696*c8dee2aaSAndroid Build Coastguard Worker 
1697*c8dee2aaSAndroid Build Coastguard Worker     // 'type' could be kStrokeAndFill, but in that case chooseRenderer() is meant to return the
1698*c8dee2aaSAndroid Build Coastguard Worker     // fill renderer since tessellatedStrokes() will always be used for the stroke pass.
1699*c8dee2aaSAndroid Build Coastguard Worker     if (shape.convex() && !shape.inverted()) {
1700*c8dee2aaSAndroid Build Coastguard Worker         // TODO: Ganesh doesn't have a curve+middle-out triangles option for convex paths, but it
1701*c8dee2aaSAndroid Build Coastguard Worker         // would be pretty trivial to spin up.
1702*c8dee2aaSAndroid Build Coastguard Worker         return {renderers->convexTessellatedWedges(), nullptr};
1703*c8dee2aaSAndroid Build Coastguard Worker     } else {
1704*c8dee2aaSAndroid Build Coastguard Worker         if (!drawBounds.has_value()) {
1705*c8dee2aaSAndroid Build Coastguard Worker             drawBounds = localToDevice.mapRect(shape.bounds());
1706*c8dee2aaSAndroid Build Coastguard Worker         }
1707*c8dee2aaSAndroid Build Coastguard Worker         drawBounds->intersect(fClip.conservativeBounds());
1708*c8dee2aaSAndroid Build Coastguard Worker         const bool preferWedges =
1709*c8dee2aaSAndroid Build Coastguard Worker                 // If the draw bounds don't intersect with the clip stack's conservative bounds,
1710*c8dee2aaSAndroid Build Coastguard Worker                 // we'll be drawing a very small area at most, accounting for coverage, so just
1711*c8dee2aaSAndroid Build Coastguard Worker                 // stick with drawing wedges in that case.
1712*c8dee2aaSAndroid Build Coastguard Worker                 drawBounds->isEmptyNegativeOrNaN() ||
1713*c8dee2aaSAndroid Build Coastguard Worker 
1714*c8dee2aaSAndroid Build Coastguard Worker                 // TODO: Combine this heuristic with what is used in PathStencilCoverOp to choose
1715*c8dee2aaSAndroid Build Coastguard Worker                 // between wedges curves consistently in Graphite and Ganesh.
1716*c8dee2aaSAndroid Build Coastguard Worker                 (shape.isPath() && shape.path().countVerbs() < 50) ||
1717*c8dee2aaSAndroid Build Coastguard Worker                 drawBounds->area() <= (256 * 256);
1718*c8dee2aaSAndroid Build Coastguard Worker 
1719*c8dee2aaSAndroid Build Coastguard Worker         if (preferWedges) {
1720*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->stencilTessellatedWedges(shape.fillType()), nullptr};
1721*c8dee2aaSAndroid Build Coastguard Worker         } else {
1722*c8dee2aaSAndroid Build Coastguard Worker             return {renderers->stencilTessellatedCurvesAndTris(shape.fillType()), nullptr};
1723*c8dee2aaSAndroid Build Coastguard Worker         }
1724*c8dee2aaSAndroid Build Coastguard Worker     }
1725*c8dee2aaSAndroid Build Coastguard Worker }
1726*c8dee2aaSAndroid Build Coastguard Worker 
lastDrawTask() const1727*c8dee2aaSAndroid Build Coastguard Worker sk_sp<Task> Device::lastDrawTask() const {
1728*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(this->isScratchDevice());
1729*c8dee2aaSAndroid Build Coastguard Worker     return fLastTask;
1730*c8dee2aaSAndroid Build Coastguard Worker }
1731*c8dee2aaSAndroid Build Coastguard Worker 
flushPendingWorkToRecorder()1732*c8dee2aaSAndroid Build Coastguard Worker void Device::flushPendingWorkToRecorder() {
1733*c8dee2aaSAndroid Build Coastguard Worker     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
1734*c8dee2aaSAndroid Build Coastguard Worker 
1735*c8dee2aaSAndroid Build Coastguard Worker     // If this is a scratch device being flushed, it should only be flushing into the expected
1736*c8dee2aaSAndroid Build Coastguard Worker     // next recording from when the Device was first created.
1737*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fRecorder);
1738*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fScopedRecordingID == 0 || fScopedRecordingID == fRecorder->priv().nextRecordingID());
1739*c8dee2aaSAndroid Build Coastguard Worker 
1740*c8dee2aaSAndroid Build Coastguard Worker     // TODO(b/330864257):  flushPendingWorkToRecorder() can be recursively called if this Device
1741*c8dee2aaSAndroid Build Coastguard Worker     // recorded a picture shader draw and during a flush (triggered by snap or automatically from
1742*c8dee2aaSAndroid Build Coastguard Worker     // reaching limits), the picture shader will be rendered to a new device. If that picture drawn
1743*c8dee2aaSAndroid Build Coastguard Worker     // to the temporary device fills up an atlas it can trigger the global
1744*c8dee2aaSAndroid Build Coastguard Worker     // recorder->flushTrackedDevices(), which will then encounter this device that is already in
1745*c8dee2aaSAndroid Build Coastguard Worker     // the midst of flushing. To avoid crashing we only actually flush the first time this is called
1746*c8dee2aaSAndroid Build Coastguard Worker     // and set a bit to early-out on any recursive calls.
1747*c8dee2aaSAndroid Build Coastguard Worker     // This is not an ideal solution since the temporary Device's flush-the-world may have reset
1748*c8dee2aaSAndroid Build Coastguard Worker     // atlas entries that the current Device's flushed draws will reference. But at this stage it's
1749*c8dee2aaSAndroid Build Coastguard Worker     // not possible to split the already recorded draws into a before-list and an after-list that
1750*c8dee2aaSAndroid Build Coastguard Worker     // can reference the old and new contents of the atlas. While avoiding the crash, this may cause
1751*c8dee2aaSAndroid Build Coastguard Worker     // incorrect accesses to a shared atlas. Once paint data is extracted at draw time, picture
1752*c8dee2aaSAndroid Build Coastguard Worker     // shaders will be resolved outside of flushes and then this will be fixed automatically.
1753*c8dee2aaSAndroid Build Coastguard Worker     if (fIsFlushing) {
1754*c8dee2aaSAndroid Build Coastguard Worker         return;
1755*c8dee2aaSAndroid Build Coastguard Worker     } else {
1756*c8dee2aaSAndroid Build Coastguard Worker         fIsFlushing = true;
1757*c8dee2aaSAndroid Build Coastguard Worker     }
1758*c8dee2aaSAndroid Build Coastguard Worker 
1759*c8dee2aaSAndroid Build Coastguard Worker     this->internalFlush();
1760*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<Task> drawTask = fDC->snapDrawTask(fRecorder);
1761*c8dee2aaSAndroid Build Coastguard Worker     if (this->isScratchDevice()) {
1762*c8dee2aaSAndroid Build Coastguard Worker         // TODO(b/323887221): Once shared atlas resources are less brittle, scratch devices won't
1763*c8dee2aaSAndroid Build Coastguard Worker         // flush to the recorder at all and will only store the snapped task here.
1764*c8dee2aaSAndroid Build Coastguard Worker         fLastTask = drawTask;
1765*c8dee2aaSAndroid Build Coastguard Worker     } else {
1766*c8dee2aaSAndroid Build Coastguard Worker         // Non-scratch devices do not need to point back to the last snapped task since they are
1767*c8dee2aaSAndroid Build Coastguard Worker         // always added to the root task list.
1768*c8dee2aaSAndroid Build Coastguard Worker         // TODO: It is currently possible for scratch devices to be flushed and instantiated before
1769*c8dee2aaSAndroid Build Coastguard Worker         // their work is finished, meaning they will produce additional tasks to be included in
1770*c8dee2aaSAndroid Build Coastguard Worker         // a follow-up Recording: https://chat.google.com/room/AAAA2HlH94I/YU0XdFqX2Uw.
1771*c8dee2aaSAndroid Build Coastguard Worker         // However, in this case they no longer appear scratch because the first Recording
1772*c8dee2aaSAndroid Build Coastguard Worker         // instantiated the targets. When scratch devices are not actually registered with the
1773*c8dee2aaSAndroid Build Coastguard Worker         // Recorder and are only included when they are drawn (e.g. restored), we should be able to
1774*c8dee2aaSAndroid Build Coastguard Worker         // assert that `fLastTask` is null.
1775*c8dee2aaSAndroid Build Coastguard Worker         fLastTask = nullptr;
1776*c8dee2aaSAndroid Build Coastguard Worker     }
1777*c8dee2aaSAndroid Build Coastguard Worker 
1778*c8dee2aaSAndroid Build Coastguard Worker     if (drawTask) {
1779*c8dee2aaSAndroid Build Coastguard Worker         fRecorder->priv().add(std::move(drawTask));
1780*c8dee2aaSAndroid Build Coastguard Worker 
1781*c8dee2aaSAndroid Build Coastguard Worker         // TODO(b/297344089): This always regenerates mipmaps on the draw target when it's drawn to.
1782*c8dee2aaSAndroid Build Coastguard Worker         // This could be wasteful if we draw to a target multiple times before reading from it with
1783*c8dee2aaSAndroid Build Coastguard Worker         // downscaling.
1784*c8dee2aaSAndroid Build Coastguard Worker         if (fDC->target()->mipmapped() == Mipmapped::kYes) {
1785*c8dee2aaSAndroid Build Coastguard Worker             if (!GenerateMipmaps(fRecorder, fDC->refTarget(), fDC->colorInfo())) {
1786*c8dee2aaSAndroid Build Coastguard Worker                 SKGPU_LOG_W("Device::flushPendingWorkToRecorder: Failed to generate mipmaps");
1787*c8dee2aaSAndroid Build Coastguard Worker             }
1788*c8dee2aaSAndroid Build Coastguard Worker         }
1789*c8dee2aaSAndroid Build Coastguard Worker     }
1790*c8dee2aaSAndroid Build Coastguard Worker 
1791*c8dee2aaSAndroid Build Coastguard Worker     fIsFlushing = false;
1792*c8dee2aaSAndroid Build Coastguard Worker }
1793*c8dee2aaSAndroid Build Coastguard Worker 
internalFlush()1794*c8dee2aaSAndroid Build Coastguard Worker void Device::internalFlush() {
1795*c8dee2aaSAndroid Build Coastguard Worker     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
1796*c8dee2aaSAndroid Build Coastguard Worker     ASSERT_SINGLE_OWNER
1797*c8dee2aaSAndroid Build Coastguard Worker 
1798*c8dee2aaSAndroid Build Coastguard Worker     // Push any pending uploads from the atlas provider that pending draws reference.
1799*c8dee2aaSAndroid Build Coastguard Worker     fRecorder->priv().atlasProvider()->recordUploads(fDC.get());
1800*c8dee2aaSAndroid Build Coastguard Worker 
1801*c8dee2aaSAndroid Build Coastguard Worker     // Clip shapes are depth-only draws, but aren't recorded in the DrawContext until a flush in
1802*c8dee2aaSAndroid Build Coastguard Worker     // order to determine the Z values for each element.
1803*c8dee2aaSAndroid Build Coastguard Worker     fClip.recordDeferredClipDraws();
1804*c8dee2aaSAndroid Build Coastguard Worker 
1805*c8dee2aaSAndroid Build Coastguard Worker     // Flush all pending items to the internal task list and reset Device tracking state
1806*c8dee2aaSAndroid Build Coastguard Worker     fDC->flush(fRecorder);
1807*c8dee2aaSAndroid Build Coastguard Worker 
1808*c8dee2aaSAndroid Build Coastguard Worker     fColorDepthBoundsManager->reset();
1809*c8dee2aaSAndroid Build Coastguard Worker     fDisjointStencilSet->reset();
1810*c8dee2aaSAndroid Build Coastguard Worker     fCurrentDepth = DrawOrder::kClearDepth;
1811*c8dee2aaSAndroid Build Coastguard Worker 
1812*c8dee2aaSAndroid Build Coastguard Worker      // Any cleanup in the AtlasProvider
1813*c8dee2aaSAndroid Build Coastguard Worker     fRecorder->priv().atlasProvider()->compact(/*forceCompact=*/false);
1814*c8dee2aaSAndroid Build Coastguard Worker }
1815*c8dee2aaSAndroid Build Coastguard Worker 
needsFlushBeforeDraw(int numNewRenderSteps,DstReadRequirement dstReadReq) const1816*c8dee2aaSAndroid Build Coastguard Worker bool Device::needsFlushBeforeDraw(int numNewRenderSteps, DstReadRequirement dstReadReq) const {
1817*c8dee2aaSAndroid Build Coastguard Worker     // Must also account for the elements in the clip stack that might need to be recorded.
1818*c8dee2aaSAndroid Build Coastguard Worker     numNewRenderSteps += fClip.maxDeferredClipDraws() * Renderer::kMaxRenderSteps;
1819*c8dee2aaSAndroid Build Coastguard Worker     return // Need flush if we don't have room to record into the current list.
1820*c8dee2aaSAndroid Build Coastguard Worker            (DrawList::kMaxRenderSteps - fDC->pendingRenderSteps()) < numNewRenderSteps ||
1821*c8dee2aaSAndroid Build Coastguard Worker            // Need flush if this draw needs to copy the dst surface for reading.
1822*c8dee2aaSAndroid Build Coastguard Worker            dstReadReq == DstReadRequirement::kTextureCopy;
1823*c8dee2aaSAndroid Build Coastguard Worker }
1824*c8dee2aaSAndroid Build Coastguard Worker 
drawSpecial(SkSpecialImage * special,const SkMatrix & localToDevice,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)1825*c8dee2aaSAndroid Build Coastguard Worker void Device::drawSpecial(SkSpecialImage* special,
1826*c8dee2aaSAndroid Build Coastguard Worker                          const SkMatrix& localToDevice,
1827*c8dee2aaSAndroid Build Coastguard Worker                          const SkSamplingOptions& sampling,
1828*c8dee2aaSAndroid Build Coastguard Worker                          const SkPaint& paint,
1829*c8dee2aaSAndroid Build Coastguard Worker                          SkCanvas::SrcRectConstraint constraint) {
1830*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!paint.getMaskFilter() && !paint.getImageFilter());
1831*c8dee2aaSAndroid Build Coastguard Worker 
1832*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkImage> img = special->asImage();
1833*c8dee2aaSAndroid Build Coastguard Worker     if (!img || !as_IB(img)->isGraphiteBacked()) {
1834*c8dee2aaSAndroid Build Coastguard Worker         SKGPU_LOG_W("Couldn't get Graphite-backed special image as image");
1835*c8dee2aaSAndroid Build Coastguard Worker         return;
1836*c8dee2aaSAndroid Build Coastguard Worker     }
1837*c8dee2aaSAndroid Build Coastguard Worker 
1838*c8dee2aaSAndroid Build Coastguard Worker     SkPaint paintWithShader(paint);
1839*c8dee2aaSAndroid Build Coastguard Worker     SkRect dst = SkModifyPaintAndDstForDrawImageRect(
1840*c8dee2aaSAndroid Build Coastguard Worker             img.get(),
1841*c8dee2aaSAndroid Build Coastguard Worker             sampling,
1842*c8dee2aaSAndroid Build Coastguard Worker             /*src=*/SkRect::Make(special->subset()),
1843*c8dee2aaSAndroid Build Coastguard Worker             /*dst=*/SkRect::MakeIWH(special->width(), special->height()),
1844*c8dee2aaSAndroid Build Coastguard Worker             /*strictSrcSubset=*/constraint == SkCanvas::kStrict_SrcRectConstraint,
1845*c8dee2aaSAndroid Build Coastguard Worker             &paintWithShader);
1846*c8dee2aaSAndroid Build Coastguard Worker     if (dst.isEmpty()) {
1847*c8dee2aaSAndroid Build Coastguard Worker         return;
1848*c8dee2aaSAndroid Build Coastguard Worker     }
1849*c8dee2aaSAndroid Build Coastguard Worker 
1850*c8dee2aaSAndroid Build Coastguard Worker     // The image filtering and layer code paths often rely on the paint being non-AA to avoid
1851*c8dee2aaSAndroid Build Coastguard Worker     // coverage operations. To stay consistent with the other backends, we use an edge AA "quad"
1852*c8dee2aaSAndroid Build Coastguard Worker     // whose flags match the paint's AA request.
1853*c8dee2aaSAndroid Build Coastguard Worker     EdgeAAQuad::Flags aaFlags = paint.isAntiAlias() ? EdgeAAQuad::Flags::kAll
1854*c8dee2aaSAndroid Build Coastguard Worker                                                     : EdgeAAQuad::Flags::kNone;
1855*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(Transform(SkM44(localToDevice)),
1856*c8dee2aaSAndroid Build Coastguard Worker                        Geometry(EdgeAAQuad(dst, aaFlags)),
1857*c8dee2aaSAndroid Build Coastguard Worker                        paintWithShader,
1858*c8dee2aaSAndroid Build Coastguard Worker                        DefaultFillStyle(),
1859*c8dee2aaSAndroid Build Coastguard Worker                        DrawFlags::kIgnorePathEffect);
1860*c8dee2aaSAndroid Build Coastguard Worker }
1861*c8dee2aaSAndroid Build Coastguard Worker 
drawCoverageMask(const SkSpecialImage * mask,const SkMatrix & localToDevice,const SkSamplingOptions & sampling,const SkPaint & paint)1862*c8dee2aaSAndroid Build Coastguard Worker void Device::drawCoverageMask(const SkSpecialImage* mask,
1863*c8dee2aaSAndroid Build Coastguard Worker                               const SkMatrix& localToDevice,
1864*c8dee2aaSAndroid Build Coastguard Worker                               const SkSamplingOptions& sampling,
1865*c8dee2aaSAndroid Build Coastguard Worker                               const SkPaint& paint) {
1866*c8dee2aaSAndroid Build Coastguard Worker     CoverageMaskShape::MaskInfo maskInfo{/*fTextureOrigin=*/{SkTo<uint16_t>(mask->subset().fLeft),
1867*c8dee2aaSAndroid Build Coastguard Worker                                                              SkTo<uint16_t>(mask->subset().fTop)},
1868*c8dee2aaSAndroid Build Coastguard Worker                                          /*fMaskSize=*/{SkTo<uint16_t>(mask->width()),
1869*c8dee2aaSAndroid Build Coastguard Worker                                                         SkTo<uint16_t>(mask->height())}};
1870*c8dee2aaSAndroid Build Coastguard Worker 
1871*c8dee2aaSAndroid Build Coastguard Worker     auto maskProxyView = AsView(mask->asImage());
1872*c8dee2aaSAndroid Build Coastguard Worker     if (!maskProxyView) {
1873*c8dee2aaSAndroid Build Coastguard Worker         SKGPU_LOG_W("Couldn't get Graphite-backed special image as texture proxy view");
1874*c8dee2aaSAndroid Build Coastguard Worker         return;
1875*c8dee2aaSAndroid Build Coastguard Worker     }
1876*c8dee2aaSAndroid Build Coastguard Worker 
1877*c8dee2aaSAndroid Build Coastguard Worker     // Every other "Image" draw reaches the underlying texture via AddToKey/NotifyInUse, which
1878*c8dee2aaSAndroid Build Coastguard Worker     // handles notifying the image and either flushing the linked surface or attaching draw tasks
1879*c8dee2aaSAndroid Build Coastguard Worker     // from a scratch device to the current draw context. In this case, 'mask' is very likely to
1880*c8dee2aaSAndroid Build Coastguard Worker     // be linked to a scratch device, but we must perform the same notifyInUse manually here because
1881*c8dee2aaSAndroid Build Coastguard Worker     // the texture is consumed by the RenderStep and not part of the PaintParams.
1882*c8dee2aaSAndroid Build Coastguard Worker     static_cast<Image_Base*>(mask->asImage().get())->notifyInUse(fRecorder, fDC.get());
1883*c8dee2aaSAndroid Build Coastguard Worker 
1884*c8dee2aaSAndroid Build Coastguard Worker     // 'mask' logically has 0 coverage outside of its pixels, which is equivalent to kDecal tiling.
1885*c8dee2aaSAndroid Build Coastguard Worker     // However, since we draw geometry tightly fitting 'mask', we can use the better-supported
1886*c8dee2aaSAndroid Build Coastguard Worker     // kClamp tiling and behave effectively the same way.
1887*c8dee2aaSAndroid Build Coastguard Worker     TextureDataBlock::SampledTexture sampledMask{maskProxyView.refProxy(),
1888*c8dee2aaSAndroid Build Coastguard Worker                                                  {SkFilterMode::kLinear, SkTileMode::kClamp}};
1889*c8dee2aaSAndroid Build Coastguard Worker     // Ensure this is kept alive; normally textures are kept alive by the PipelineDataGatherer for
1890*c8dee2aaSAndroid Build Coastguard Worker     // image shaders, or by the PathAtlas. This is a unique circumstance.
1891*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: CoverageMaskRenderStep controls the final sampling options; this texture data block
1892*c8dee2aaSAndroid Build Coastguard Worker     // serves only to keep the mask alive so the sampling passed to add() doesn't matter.
1893*c8dee2aaSAndroid Build Coastguard Worker     fRecorder->priv().textureDataCache()->insert(TextureDataBlock(sampledMask));
1894*c8dee2aaSAndroid Build Coastguard Worker 
1895*c8dee2aaSAndroid Build Coastguard Worker     // CoverageMaskShape() wraps a Shape when it's used as a PathAtlas, but in this case the
1896*c8dee2aaSAndroid Build Coastguard Worker     // original shape has been long lost, so just use a Rect that bounds the image.
1897*c8dee2aaSAndroid Build Coastguard Worker     CoverageMaskShape maskShape{Shape{Rect::WH((float)mask->width(), (float)mask->height())},
1898*c8dee2aaSAndroid Build Coastguard Worker                                 maskProxyView.proxy(),
1899*c8dee2aaSAndroid Build Coastguard Worker                                 // Use the active local-to-device transform for this since it
1900*c8dee2aaSAndroid Build Coastguard Worker                                 // determines the local coords for evaluating the skpaint, whereas
1901*c8dee2aaSAndroid Build Coastguard Worker                                 // the provided 'localToDevice' just places the coverage mask.
1902*c8dee2aaSAndroid Build Coastguard Worker                                 this->localToDeviceTransform().inverse(),
1903*c8dee2aaSAndroid Build Coastguard Worker                                 maskInfo};
1904*c8dee2aaSAndroid Build Coastguard Worker 
1905*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(Transform(SkM44(localToDevice)),
1906*c8dee2aaSAndroid Build Coastguard Worker                        Geometry(maskShape),
1907*c8dee2aaSAndroid Build Coastguard Worker                        paint,
1908*c8dee2aaSAndroid Build Coastguard Worker                        DefaultFillStyle(),
1909*c8dee2aaSAndroid Build Coastguard Worker                        DrawFlags::kIgnorePathEffect);
1910*c8dee2aaSAndroid Build Coastguard Worker }
1911*c8dee2aaSAndroid Build Coastguard Worker 
makeSpecial(const SkBitmap &)1912*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap&) {
1913*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
1914*c8dee2aaSAndroid Build Coastguard Worker }
1915*c8dee2aaSAndroid Build Coastguard Worker 
makeSpecial(const SkImage *)1916*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSpecialImage> Device::makeSpecial(const SkImage*) {
1917*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
1918*c8dee2aaSAndroid Build Coastguard Worker }
1919*c8dee2aaSAndroid Build Coastguard Worker 
snapSpecial(const SkIRect & subset,bool forceCopy)1920*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) {
1921*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: snapSpecial() can be called even after the device has been marked immutable (null
1922*c8dee2aaSAndroid Build Coastguard Worker     // recorder), but in those cases it should not be a copy and just returns the image view.
1923*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<Image> deviceImage;
1924*c8dee2aaSAndroid Build Coastguard Worker     SkIRect finalSubset;
1925*c8dee2aaSAndroid Build Coastguard Worker     if (forceCopy || !this->readSurfaceView() || this->readSurfaceView().proxy()->isFullyLazy()) {
1926*c8dee2aaSAndroid Build Coastguard Worker         deviceImage = this->makeImageCopy(
1927*c8dee2aaSAndroid Build Coastguard Worker                 subset, Budgeted::kYes, Mipmapped::kNo, SkBackingFit::kApprox);
1928*c8dee2aaSAndroid Build Coastguard Worker         finalSubset = SkIRect::MakeSize(subset.size());
1929*c8dee2aaSAndroid Build Coastguard Worker     } else {
1930*c8dee2aaSAndroid Build Coastguard Worker         // TODO(b/323886870): For now snapSpecial() force adds the pending work to the recorder's
1931*c8dee2aaSAndroid Build Coastguard Worker         // root task list. Once shared atlas management is solved and DrawTasks can be nested in a
1932*c8dee2aaSAndroid Build Coastguard Worker         // graph then this can go away in favor of auto-flushing through the image's linked device.
1933*c8dee2aaSAndroid Build Coastguard Worker         if (fRecorder) {
1934*c8dee2aaSAndroid Build Coastguard Worker             this->flushPendingWorkToRecorder();
1935*c8dee2aaSAndroid Build Coastguard Worker         }
1936*c8dee2aaSAndroid Build Coastguard Worker         deviceImage = Image::WrapDevice(sk_ref_sp(this));
1937*c8dee2aaSAndroid Build Coastguard Worker         finalSubset = subset;
1938*c8dee2aaSAndroid Build Coastguard Worker     }
1939*c8dee2aaSAndroid Build Coastguard Worker 
1940*c8dee2aaSAndroid Build Coastguard Worker     if (!deviceImage) {
1941*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
1942*c8dee2aaSAndroid Build Coastguard Worker     }
1943*c8dee2aaSAndroid Build Coastguard Worker 
1944*c8dee2aaSAndroid Build Coastguard Worker     // For non-copying "snapSpecial", the semantics are returning an image view of the surface data,
1945*c8dee2aaSAndroid Build Coastguard Worker     // and relying on higher-level draw and restore logic for the contents to make sense.
1946*c8dee2aaSAndroid Build Coastguard Worker     return SkSpecialImages::MakeGraphite(
1947*c8dee2aaSAndroid Build Coastguard Worker             fRecorder, finalSubset, std::move(deviceImage), this->surfaceProps());
1948*c8dee2aaSAndroid Build Coastguard Worker }
1949*c8dee2aaSAndroid Build Coastguard Worker 
createImageFilteringBackend(const SkSurfaceProps & surfaceProps,SkColorType colorType) const1950*c8dee2aaSAndroid Build Coastguard Worker sk_sp<skif::Backend> Device::createImageFilteringBackend(const SkSurfaceProps& surfaceProps,
1951*c8dee2aaSAndroid Build Coastguard Worker                                                          SkColorType colorType) const {
1952*c8dee2aaSAndroid Build Coastguard Worker     return skif::MakeGraphiteBackend(fRecorder, surfaceProps, colorType);
1953*c8dee2aaSAndroid Build Coastguard Worker }
1954*c8dee2aaSAndroid Build Coastguard Worker 
target()1955*c8dee2aaSAndroid Build Coastguard Worker TextureProxy* Device::target() { return fDC->target(); }
1956*c8dee2aaSAndroid Build Coastguard Worker 
readSurfaceView() const1957*c8dee2aaSAndroid Build Coastguard Worker TextureProxyView Device::readSurfaceView() const { return fDC->readSurfaceView(); }
1958*c8dee2aaSAndroid Build Coastguard Worker 
isScratchDevice() const1959*c8dee2aaSAndroid Build Coastguard Worker bool Device::isScratchDevice() const {
1960*c8dee2aaSAndroid Build Coastguard Worker     // Scratch device status is inferred from whether or not the Device's target is instantiated.
1961*c8dee2aaSAndroid Build Coastguard Worker     // By default devices start out un-instantiated unless they are wrapping an existing backend
1962*c8dee2aaSAndroid Build Coastguard Worker     // texture (definitely not a scratch scenario), or Surface explicitly instantiates the target
1963*c8dee2aaSAndroid Build Coastguard Worker     // before returning to the client (not a scratch scenario).
1964*c8dee2aaSAndroid Build Coastguard Worker     //
1965*c8dee2aaSAndroid Build Coastguard Worker     // Scratch device targets are instantiated during the prepareResources() phase of
1966*c8dee2aaSAndroid Build Coastguard Worker     // Recorder::snap(). Truly scratch devices that have gone out of scope as intended will have
1967*c8dee2aaSAndroid Build Coastguard Worker     // already been destroyed at this point. Scratch devices that become longer-lived (linked to
1968*c8dee2aaSAndroid Build Coastguard Worker     // a client-owned object) automatically transition to non-scratch usage.
1969*c8dee2aaSAndroid Build Coastguard Worker     return !fDC->target()->isInstantiated() && !fDC->target()->isLazy();
1970*c8dee2aaSAndroid Build Coastguard Worker }
1971*c8dee2aaSAndroid Build Coastguard Worker 
convertGlyphRunListToSlug(const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1972*c8dee2aaSAndroid Build Coastguard Worker sk_sp<sktext::gpu::Slug> Device::convertGlyphRunListToSlug(const sktext::GlyphRunList& glyphRunList,
1973*c8dee2aaSAndroid Build Coastguard Worker                                                            const SkPaint& paint) {
1974*c8dee2aaSAndroid Build Coastguard Worker     return sktext::gpu::SlugImpl::Make(this->localToDevice(),
1975*c8dee2aaSAndroid Build Coastguard Worker                                        glyphRunList,
1976*c8dee2aaSAndroid Build Coastguard Worker                                        paint,
1977*c8dee2aaSAndroid Build Coastguard Worker                                        this->strikeDeviceInfo(),
1978*c8dee2aaSAndroid Build Coastguard Worker                                        SkStrikeCache::GlobalStrikeCache());
1979*c8dee2aaSAndroid Build Coastguard Worker }
1980*c8dee2aaSAndroid Build Coastguard Worker 
drawSlug(SkCanvas * canvas,const sktext::gpu::Slug * slug,const SkPaint & paint)1981*c8dee2aaSAndroid Build Coastguard Worker void Device::drawSlug(SkCanvas* canvas, const sktext::gpu::Slug* slug, const SkPaint& paint) {
1982*c8dee2aaSAndroid Build Coastguard Worker     auto slugImpl = static_cast<const sktext::gpu::SlugImpl*>(slug);
1983*c8dee2aaSAndroid Build Coastguard Worker     slugImpl->subRuns()->draw(canvas, slugImpl->origin(), paint, slugImpl, this->atlasDelegate());
1984*c8dee2aaSAndroid Build Coastguard Worker }
1985*c8dee2aaSAndroid Build Coastguard Worker 
drawBlurredRRect(const SkRRect & rrect,const SkPaint & paint,float deviceSigma)1986*c8dee2aaSAndroid Build Coastguard Worker bool Device::drawBlurredRRect(const SkRRect& rrect, const SkPaint& paint, float deviceSigma) {
1987*c8dee2aaSAndroid Build Coastguard Worker     SkStrokeRec style(paint);
1988*c8dee2aaSAndroid Build Coastguard Worker     if (skgpu::BlurIsEffectivelyIdentity(deviceSigma)) {
1989*c8dee2aaSAndroid Build Coastguard Worker         this->drawGeometry(this->localToDeviceTransform(),
1990*c8dee2aaSAndroid Build Coastguard Worker                            Geometry(rrect.isRect() ? Shape(rrect.rect()) : Shape(rrect)),
1991*c8dee2aaSAndroid Build Coastguard Worker                            paint,
1992*c8dee2aaSAndroid Build Coastguard Worker                            style);
1993*c8dee2aaSAndroid Build Coastguard Worker         return true;
1994*c8dee2aaSAndroid Build Coastguard Worker     }
1995*c8dee2aaSAndroid Build Coastguard Worker 
1996*c8dee2aaSAndroid Build Coastguard Worker     std::optional<AnalyticBlurMask> analyticBlur = AnalyticBlurMask::Make(
1997*c8dee2aaSAndroid Build Coastguard Worker             this->recorder(), this->localToDeviceTransform(), deviceSigma, rrect);
1998*c8dee2aaSAndroid Build Coastguard Worker     if (!analyticBlur) {
1999*c8dee2aaSAndroid Build Coastguard Worker         return false;
2000*c8dee2aaSAndroid Build Coastguard Worker     }
2001*c8dee2aaSAndroid Build Coastguard Worker 
2002*c8dee2aaSAndroid Build Coastguard Worker     this->drawGeometry(this->localToDeviceTransform(), Geometry(*analyticBlur), paint, style);
2003*c8dee2aaSAndroid Build Coastguard Worker     return true;
2004*c8dee2aaSAndroid Build Coastguard Worker }
2005*c8dee2aaSAndroid Build Coastguard Worker 
2006*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
2007