• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/Device.h"
9 
10 #include "include/gpu/graphite/Recorder.h"
11 #include "include/gpu/graphite/Recording.h"
12 #include "src/gpu/AtlasTypes.h"
13 #include "src/gpu/graphite/Buffer.h"
14 #include "src/gpu/graphite/Caps.h"
15 #include "src/gpu/graphite/CommandBuffer.h"
16 #include "src/gpu/graphite/ContextPriv.h"
17 #include "src/gpu/graphite/CopyTask.h"
18 #include "src/gpu/graphite/DrawContext.h"
19 #include "src/gpu/graphite/DrawList.h"
20 #include "src/gpu/graphite/DrawParams.h"
21 #include "src/gpu/graphite/ImageUtils.h"
22 #include "src/gpu/graphite/Image_Graphite.h"
23 #include "src/gpu/graphite/Log.h"
24 #include "src/gpu/graphite/RecorderPriv.h"
25 #include "src/gpu/graphite/Renderer.h"
26 #include "src/gpu/graphite/RendererProvider.h"
27 #include "src/gpu/graphite/SharedContext.h"
28 #include "src/gpu/graphite/TextureProxy.h"
29 #include "src/gpu/graphite/TextureUtils.h"
30 #include "src/gpu/graphite/geom/BoundsManager.h"
31 #include "src/gpu/graphite/geom/Geometry.h"
32 #include "src/gpu/graphite/geom/IntersectionTree.h"
33 #include "src/gpu/graphite/geom/Shape.h"
34 #include "src/gpu/graphite/geom/Transform_graphite.h"
35 #include "src/gpu/graphite/text/AtlasManager.h"
36 
37 #include "include/core/SkColorSpace.h"
38 #include "include/core/SkPath.h"
39 #include "include/core/SkPathEffect.h"
40 #include "include/core/SkStrokeRec.h"
41 
42 #include "src/core/SkBlenderBase.h"
43 #include "src/core/SkColorSpacePriv.h"
44 #include "src/core/SkConvertPixels.h"
45 #include "src/core/SkImageInfoPriv.h"
46 #include "src/core/SkMatrixPriv.h"
47 #include "src/core/SkPaintPriv.h"
48 #include "src/core/SkRRectPriv.h"
49 #include "src/core/SkSpecialImage.h"
50 #include "src/core/SkTraceEvent.h"
51 #include "src/core/SkVerticesPriv.h"
52 #include "src/shaders/SkImageShader.h"
53 #include "src/text/gpu/SubRunContainer.h"
54 #include "src/text/gpu/TextBlobRedrawCoordinator.h"
55 
56 #include <unordered_map>
57 #include <vector>
58 
59 using RescaleGamma       = SkImage::RescaleGamma;
60 using RescaleMode        = SkImage::RescaleMode;
61 using ReadPixelsCallback = SkImage::ReadPixelsCallback;
62 using ReadPixelsContext  = SkImage::ReadPixelsContext;
63 
64 namespace skgpu::graphite {
65 
66 namespace {
67 
68 static const SkStrokeRec kFillStyle(SkStrokeRec::kFill_InitStyle);
69 
paint_depends_on_dst(SkColor4f color,const SkShader * shader,const SkColorFilter * colorFilter,const SkBlender * blender)70 bool paint_depends_on_dst(SkColor4f color,
71                           const SkShader* shader,
72                           const SkColorFilter* colorFilter,
73                           const SkBlender* blender) {
74     std::optional<SkBlendMode> bm = blender ? as_BB(blender)->asBlendMode() : SkBlendMode::kSrcOver;
75     if (!bm.has_value()) {
76         return true;
77     }
78     if (bm.value() == SkBlendMode::kSrc || bm.value() == SkBlendMode::kClear) {
79         // src and clear blending never depends on dst
80         return false;
81     }
82     if (bm.value() == SkBlendMode::kSrcOver) {
83         // src-over does not depend on dst if src is opaque (a = 1)
84         return !color.isOpaque() ||
85                (shader && !shader->isOpaque()) ||
86                (colorFilter && !colorFilter->isAlphaUnchanged());
87     }
88     // TODO: Are their other modes that don't depend on dst that can be trivially detected?
89     return true;
90 }
91 
paint_depends_on_dst(const PaintParams & paintParams)92 bool paint_depends_on_dst(const PaintParams& paintParams) {
93     return paint_depends_on_dst(paintParams.color(), paintParams.shader(),
94                                 paintParams.colorFilter(), paintParams.finalBlender());
95 }
96 
paint_depends_on_dst(const SkPaint & paint)97 bool paint_depends_on_dst(const SkPaint& paint) {
98     // CAUTION: getMaskFilter is intentionally ignored here.
99     SkASSERT(!paint.getImageFilter());  // no paints in SkDevice should have an image filter
100     return paint_depends_on_dst(paint.getColor4f(), paint.getShader(),
101                                 paint.getColorFilter(), paint.getBlender());
102 }
103 
104 /** 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)105 std::optional<SkColor4f> extract_paint_color(const SkPaint& paint,
106                                              const SkColorInfo& dstColorInfo) {
107     SkASSERT(!paint_depends_on_dst(paint));
108     if (paint.getShader()) {
109         return std::nullopt;
110     }
111 
112     SkColor4f dstPaintColor = PaintParams::Color4fPrepForDst(paint.getColor4f(), dstColorInfo);
113 
114     if (SkColorFilter* filter = paint.getColorFilter()) {
115         SkColorSpace* dstCS = dstColorInfo.colorSpace();
116         return filter->filterColor4f(dstPaintColor, dstCS, dstCS);
117     }
118     return dstPaintColor;
119 }
120 
rect_to_pixelbounds(const Rect & r)121 SkIRect rect_to_pixelbounds(const Rect& r) {
122     return r.makeRoundOut().asSkIRect();
123 }
124 
125 // TODO: this doesn't support the SrcRectConstraint option.
create_img_shader_paint(sk_sp<SkImage> image,const SkRect & subset,const SkSamplingOptions & sampling,const SkMatrix * localMatrix,SkPaint * paint)126 bool create_img_shader_paint(sk_sp<SkImage> image,
127                              const SkRect& subset,
128                              const SkSamplingOptions& sampling,
129                              const SkMatrix* localMatrix,
130                              SkPaint* paint) {
131     bool imageIsAlphaOnly = SkColorTypeIsAlphaOnly(image->colorType());
132 
133     sk_sp<SkShader> imgShader = SkImageShader::MakeSubset(std::move(image), subset,
134                                                           SkTileMode::kClamp, SkTileMode::kClamp,
135                                                           sampling, localMatrix);
136     if (!imgShader) {
137         SKGPU_LOG_W("Couldn't create subset image shader");
138         return false;
139     }
140     if (imageIsAlphaOnly && paint->getShader()) {
141         // Compose the image shader with the paint's shader. Alpha images+shaders should output the
142         // texture's alpha multiplied by the shader's color. DstIn (d*sa) will achieve this with
143         // the source image and dst shader (MakeBlend takes dst first, src second).
144         imgShader = SkShaders::Blend(SkBlendMode::kDstIn, paint->refShader(), std::move(imgShader));
145     }
146 
147     paint->setStyle(SkPaint::kFill_Style);
148     paint->setShader(std::move(imgShader));
149     paint->setPathEffect(nullptr);  // neither drawSpecial nor drawImageRect support path effects
150     return true;
151 }
152 
is_simple_shape(const Shape & shape,SkStrokeRec::Style type)153 bool is_simple_shape(const Shape& shape, SkStrokeRec::Style type) {
154     // We send regular filled and hairline [round] rectangles and quadrilaterals, and stroked
155     // [r]rects with circular corners to a single Renderer that does not trigger MSAA.
156     return !shape.inverted() && type != SkStrokeRec::kStrokeAndFill_Style &&
157             (shape.isRect() /* || shape.isQuadrilateral()*/ ||
158              (shape.isRRect() && (type != SkStrokeRec::kStroke_Style ||
159                                   SkRRectPriv::AllCornersCircular(shape.rrect()))));
160 }
161 
162 } // anonymous namespace
163 
164 /**
165  * IntersectionTreeSet controls multiple IntersectionTrees to organize all add rectangles into
166  * disjoint sets. For a given CompressedPaintersOrder and bounds, it returns the smallest
167  * DisjointStencilIndex that guarantees the bounds are disjoint from all other draws that use the
168  * same painters order and stencil index.
169  */
170 class Device::IntersectionTreeSet {
171 public:
172     IntersectionTreeSet() = default;
173 
add(CompressedPaintersOrder drawOrder,Rect rect)174     DisjointStencilIndex add(CompressedPaintersOrder drawOrder, Rect rect) {
175         auto& trees = fTrees[drawOrder];
176         DisjointStencilIndex stencil = DrawOrder::kUnassigned.next();
177         for (auto&& tree : trees) {
178             if (tree->add(rect)) {
179                 return stencil;
180             }
181             stencil = stencil.next(); // advance to the next tree's index
182         }
183 
184         // If here, no existing intersection tree can hold the rect so add a new one
185         IntersectionTree* newTree = this->makeTree();
186         SkAssertResult(newTree->add(rect));
187         trees.push_back(newTree);
188         return stencil;
189     }
190 
reset()191     void reset() {
192         fTrees.clear();
193         fTreeStore.reset();
194     }
195 
196 private:
197     struct Hash {
operator ()skgpu::graphite::Device::IntersectionTreeSet::Hash198         size_t operator()(const CompressedPaintersOrder& o) const noexcept { return o.bits(); }
199     };
200 
makeTree()201     IntersectionTree* makeTree() {
202         return fTreeStore.make<IntersectionTree>();
203     }
204 
205     // Each compressed painters order defines a barrier around draws so each order's set of draws
206     // are independent, even if they may intersect. Within each order, the list of trees holds the
207     // IntersectionTrees representing each disjoint set.
208     // TODO: This organization of trees is logically convenient but may need to be optimized based
209     // on real world data (e.g. how sparse is the map, how long is each vector of trees,...)
210     std::unordered_map<CompressedPaintersOrder, std::vector<IntersectionTree*>, Hash> fTrees;
211     SkSTArenaAllocWithReset<4 * sizeof(IntersectionTree)> fTreeStore;
212 };
213 
Make(Recorder * recorder,const SkImageInfo & ii,skgpu::Budgeted budgeted,Mipmapped mipmapped,const SkSurfaceProps & props,bool addInitialClear)214 sk_sp<Device> Device::Make(Recorder* recorder,
215                            const SkImageInfo& ii,
216                            skgpu::Budgeted budgeted,
217                            Mipmapped mipmapped,
218                            const SkSurfaceProps& props,
219                            bool addInitialClear) {
220     if (!recorder) {
221         return nullptr;
222     }
223 
224     sk_sp<TextureProxy> target = TextureProxy::Make(recorder->priv().caps(),
225                                                     ii.dimensions(),
226                                                     ii.colorType(),
227                                                     mipmapped,
228                                                     Protected::kNo,
229                                                     Renderable::kYes,
230                                                     budgeted);
231     if (!target) {
232         return nullptr;
233     }
234 
235     return Make(recorder, std::move(target), ii.colorInfo(), props, addInitialClear);
236 }
237 
Make(Recorder * recorder,sk_sp<TextureProxy> target,const SkColorInfo & colorInfo,const SkSurfaceProps & props,bool addInitialClear)238 sk_sp<Device> Device::Make(Recorder* recorder,
239                            sk_sp<TextureProxy> target,
240                            const SkColorInfo& colorInfo,
241                            const SkSurfaceProps& props,
242                            bool addInitialClear) {
243     return Make(recorder, target, target->dimensions(), colorInfo, props, addInitialClear);
244 }
245 
Make(Recorder * recorder,sk_sp<TextureProxy> target,SkISize deviceSize,const SkColorInfo & colorInfo,const SkSurfaceProps & props,bool addInitialClear)246 sk_sp<Device> Device::Make(Recorder* recorder,
247                            sk_sp<TextureProxy> target,
248                            SkISize deviceSize,
249                            const SkColorInfo& colorInfo,
250                            const SkSurfaceProps& props,
251                            bool addInitialClear) {
252     if (!recorder) {
253         return nullptr;
254     }
255     if (colorInfo.alphaType() != kPremul_SkAlphaType) {
256         return nullptr;
257     }
258 
259     sk_sp<DrawContext> dc = DrawContext::Make(std::move(target), deviceSize, colorInfo, props);
260     if (!dc) {
261         return nullptr;
262     }
263 
264     return sk_sp<Device>(new Device(recorder, std::move(dc), addInitialClear));
265 }
266 
267 // These default tuning numbers for the HybridBoundsManager were chosen from looking at performance
268 // and accuracy curves produced by the BoundsManagerBench for random draw bounding boxes. This
269 // config will use brute force for the first 64 draw calls to the Device and then switch to a grid
270 // that is dynamically sized to produce cells that are 16x16, which seemed to be in the sweet spot
271 // for maintaining good performance without becoming too inaccurate.
272 // TODO: These could be exposed as context options or surface options, and we may want to have
273 // different strategies in place for a base device vs. a layer's device.
274 static constexpr int kGridCellSize = 16;
275 static constexpr int kMaxBruteForceN = 64;
276 
Device(Recorder * recorder,sk_sp<DrawContext> dc,bool addInitialClear)277 Device::Device(Recorder* recorder, sk_sp<DrawContext> dc, bool addInitialClear)
278         : SkBaseDevice(dc->imageInfo(), dc->surfaceProps())
279         , fRecorder(recorder)
280         , fDC(std::move(dc))
281         , fClip(this)
282         , fColorDepthBoundsManager(
283                     std::make_unique<HybridBoundsManager>(fDC->imageInfo().dimensions(),
284                                                           kGridCellSize,
285                                                           kMaxBruteForceN))
286         , fDisjointStencilSet(std::make_unique<IntersectionTreeSet>())
287         , fCachedLocalToDevice(SkM44())
288         , fCurrentDepth(DrawOrder::kClearDepth)
289         , fSDFTControl(recorder->priv().caps()->getSDFTControl(false))
290         , fDrawsOverlap(false) {
291     SkASSERT(SkToBool(fDC) && SkToBool(fRecorder));
292     fRecorder->registerDevice(this);
293 
294     if (addInitialClear) {
295         fDC->clear(SkColors::kTransparent);
296     }
297 }
298 
~Device()299 Device::~Device() {
300     if (fRecorder) {
301         this->flushPendingWorkToRecorder();
302         fRecorder->deregisterDevice(this);
303     }
304 }
305 
abandonRecorder()306 void Device::abandonRecorder() {
307     fRecorder = nullptr;
308 }
309 
localToDeviceTransform()310 const Transform& Device::localToDeviceTransform() {
311     if (this->checkLocalToDeviceDirty()) {
312         fCachedLocalToDevice = Transform{this->localToDevice44()};
313     }
314     return fCachedLocalToDevice;
315 }
316 
strikeDeviceInfo() const317 SkStrikeDeviceInfo Device::strikeDeviceInfo() const {
318     return {this->surfaceProps(), this->scalerContextFlags(), &fSDFTControl};
319 }
320 
onCreateDevice(const CreateInfo & info,const SkPaint *)321 SkBaseDevice* Device::onCreateDevice(const CreateInfo& info, const SkPaint*) {
322     // TODO: Inspect the paint and create info to determine if there's anything that has to be
323     // modified to support inline subpasses.
324     // TODO: onCreateDevice really should return sk_sp<SkBaseDevice>...
325     SkSurfaceProps props(this->surfaceProps().flags(), info.fPixelGeometry);
326 
327     // Skia's convention is to only clear a device if it is non-opaque.
328     bool addInitialClear = !info.fInfo.isOpaque();
329 
330     return Make(fRecorder,
331                 info.fInfo,
332                 skgpu::Budgeted::kYes,
333                 Mipmapped::kNo,
334                 props,
335                 addInitialClear)
336             .release();
337 }
338 
makeSurface(const SkImageInfo & ii,const SkSurfaceProps & props)339 sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& ii, const SkSurfaceProps& props) {
340     return SkSurface::MakeGraphite(fRecorder, ii, Mipmapped::kNo, &props);
341 }
342 
createCopy(const SkIRect * subset,Mipmapped mipmapped)343 TextureProxyView Device::createCopy(const SkIRect* subset, Mipmapped mipmapped) {
344     this->flushPendingWorkToRecorder();
345 
346     TextureProxyView srcView = this->readSurfaceView();
347     if (!srcView) {
348         return {};
349     }
350 
351     SkIRect srcRect = subset ? *subset : SkIRect::MakeSize(this->imageInfo().dimensions());
352     return TextureProxyView::Copy(this->recorder(),
353                                   this->imageInfo().colorInfo(),
354                                   srcView,
355                                   srcRect,
356                                   mipmapped);
357 }
358 
Copy(Recorder * recorder,const SkColorInfo & srcColorInfo,const TextureProxyView & srcView,SkIRect srcRect,Mipmapped mipmapped)359 TextureProxyView TextureProxyView::Copy(Recorder* recorder,
360                                         const SkColorInfo& srcColorInfo,
361                                         const TextureProxyView& srcView,
362                                         SkIRect srcRect,
363                                         Mipmapped mipmapped) {
364     SkASSERT(SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(srcRect));
365 
366     sk_sp<TextureProxy> dest = TextureProxy::Make(recorder->priv().caps(),
367                                                   srcRect.size(),
368                                                   srcColorInfo.colorType(),
369                                                   mipmapped,
370                                                   srcView.proxy()->textureInfo().isProtected(),
371                                                   Renderable::kNo,
372                                                   skgpu::Budgeted::kNo);
373     if (!dest) {
374         return {};
375     }
376 
377     sk_sp<CopyTextureToTextureTask> copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(),
378                                                                               srcRect,
379                                                                               dest,
380                                                                               {0, 0});
381     if (!copyTask) {
382         return {};
383     }
384 
385     recorder->priv().add(std::move(copyTask));
386 
387     return { std::move(dest), srcView.swizzle() };
388 }
389 
onReadPixels(const SkPixmap & pm,int srcX,int srcY)390 bool Device::onReadPixels(const SkPixmap& pm, int srcX, int srcY) {
391 #if GRAPHITE_TEST_UTILS
392     if (Context* context = fRecorder->priv().context()) {
393         this->flushPendingWorkToRecorder();
394         // Add all previous commands generated to the command buffer.
395         // If the client snaps later they'll only get post-read commands in their Recording,
396         // but since they're doing a readPixels in the middle that shouldn't be unexpected.
397         std::unique_ptr<Recording> recording = fRecorder->snap();
398         if (!recording) {
399             return false;
400         }
401         InsertRecordingInfo info;
402         info.fRecording = recording.get();
403         if (!context->insertRecording(info)) {
404             return false;
405         }
406         return context->priv().readPixels(pm, fDC->target(), this->imageInfo(), srcX, srcY);
407     }
408 #endif
409     // We have no access to a context to do a read pixels here.
410     return false;
411 }
412 
asyncRescaleAndReadPixels(const SkImageInfo & info,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)413 void Device::asyncRescaleAndReadPixels(const SkImageInfo& info,
414                                        SkIRect srcRect,
415                                        RescaleGamma rescaleGamma,
416                                        RescaleMode rescaleMode,
417                                        ReadPixelsCallback callback,
418                                        ReadPixelsContext context) {
419     // Not supported for Graphite
420     callback(context, nullptr);
421 }
422 
asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,SkIRect srcRect,SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)423 void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
424                                              sk_sp<SkColorSpace> dstColorSpace,
425                                              SkIRect srcRect,
426                                              SkISize dstSize,
427                                              RescaleGamma rescaleGamma,
428                                              RescaleMode rescaleMode,
429                                              ReadPixelsCallback callback,
430                                              ReadPixelsContext context) {
431     // TODO: implement for Graphite
432     callback(context, nullptr);
433 }
434 
onWritePixels(const SkPixmap & src,int x,int y)435 bool Device::onWritePixels(const SkPixmap& src, int x, int y) {
436     // TODO: we may need to share this in a more central place to handle uploads
437     // to backend textures
438 
439     const TextureProxy* target = fDC->target();
440 
441     // TODO: add mipmap support for createBackendTexture
442 
443     if (src.colorType() == kUnknown_SkColorType) {
444         return false;
445     }
446 
447     // If one alpha type is unknown and the other isn't, it's too underspecified.
448     if ((src.alphaType() == kUnknown_SkAlphaType) !=
449         (this->imageInfo().alphaType() == kUnknown_SkAlphaType)) {
450         return false;
451     }
452 
453     // TODO: check for readOnly or framebufferOnly target and return false if so
454 
455     // TODO: canvas2DFastPath?
456     // TODO: check that surface supports writePixels
457     // TODO: handle writePixels as draw if needed (e.g., canvas2DFastPath || !supportsWritePixels)
458 
459     // TODO: check for flips and either handle here or pass info to UploadTask
460 
461     // Determine rect to copy
462     SkIRect dstRect = SkIRect::MakePtSize({x, y}, src.dimensions());
463     if (!target->isFullyLazy() && !dstRect.intersect(SkIRect::MakeSize(target->dimensions()))) {
464         return false;
465     }
466 
467     // Set up copy location
468     const void* addr = src.addr(dstRect.fLeft - x, dstRect.fTop - y);
469     std::vector<MipLevel> levels;
470     levels.push_back({addr, src.rowBytes()});
471 
472     this->flushPendingWorkToRecorder();
473 
474     return fDC->recordUpload(fRecorder, sk_ref_sp(target), src.info().colorInfo(),
475                              this->imageInfo().colorInfo(), levels, dstRect, nullptr);
476 }
477 
478 
479 ///////////////////////////////////////////////////////////////////////////////
480 
onClipIsAA() const481 bool Device::onClipIsAA() const {
482     // All clips are AA'ed unless it's wide-open, empty, or a device-rect with integer coordinates
483     ClipStack::ClipState type = fClip.clipState();
484     if (type == ClipStack::ClipState::kWideOpen || type == ClipStack::ClipState::kEmpty) {
485         return false;
486     } else if (type == ClipStack::ClipState::kDeviceRect) {
487         const ClipStack::Element rect = *fClip.begin();
488         SkASSERT(rect.fShape.isRect() && rect.fLocalToDevice.type() == Transform::Type::kIdentity);
489         return rect.fShape.rect() != rect.fShape.rect().makeRoundOut();
490     } else {
491         return true;
492     }
493 }
494 
onGetClipType() const495 SkBaseDevice::ClipType Device::onGetClipType() const {
496     ClipStack::ClipState state = fClip.clipState();
497     if (state == ClipStack::ClipState::kEmpty) {
498         return ClipType::kEmpty;
499     } else if (state == ClipStack::ClipState::kDeviceRect ||
500                state == ClipStack::ClipState::kWideOpen) {
501         return ClipType::kRect;
502     } else {
503         return ClipType::kComplex;
504     }
505 }
506 
onDevClipBounds() const507 SkIRect Device::onDevClipBounds() const {
508     return rect_to_pixelbounds(fClip.conservativeBounds());
509 }
510 
511 // TODO: This is easy enough to support, but do we still need this API in Skia at all?
onAsRgnClip(SkRegion * region) const512 void Device::onAsRgnClip(SkRegion* region) const {
513     SkIRect bounds = this->devClipBounds();
514     // Assume wide open and then perform intersect/difference operations reducing the region
515     region->setRect(bounds);
516     const SkRegion deviceBounds(bounds);
517     for (const ClipStack::Element& e : fClip) {
518         SkRegion tmp;
519         if (e.fShape.isRect() && e.fLocalToDevice.type() == Transform::Type::kIdentity) {
520             tmp.setRect(rect_to_pixelbounds(e.fShape.rect()));
521         } else {
522             SkPath tmpPath = e.fShape.asPath();
523             tmpPath.transform(e.fLocalToDevice);
524             tmp.setPath(tmpPath, deviceBounds);
525         }
526 
527         region->op(tmp, (SkRegion::Op) e.fOp);
528     }
529 }
530 
onClipRect(const SkRect & rect,SkClipOp op,bool aa)531 void Device::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
532     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
533     // TODO: Snap rect edges to pixel bounds if non-AA and axis-aligned?
534     fClip.clipShape(this->localToDeviceTransform(), Shape{rect}, op);
535 }
536 
onClipRRect(const SkRRect & rrect,SkClipOp op,bool aa)537 void Device::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
538     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
539     // TODO: Snap rrect edges to pixel bounds if non-AA and axis-aligned? Is that worth doing to
540     // seam with non-AA rects even if the curves themselves are AA'ed?
541     fClip.clipShape(this->localToDeviceTransform(), Shape{rrect}, op);
542 }
543 
onClipPath(const SkPath & path,SkClipOp op,bool aa)544 void Device::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
545     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
546     // TODO: Ensure all path inspection is handled here or in SkCanvas, and that non-AA rects as
547     // paths are routed appropriately.
548     // TODO: Must also detect paths that are lines so the clip stack can be set to empty
549     fClip.clipShape(this->localToDeviceTransform(), Shape{path}, op);
550 }
551 
onClipShader(sk_sp<SkShader> shader)552 void Device::onClipShader(sk_sp<SkShader> shader) {
553     fClip.clipShader(std::move(shader));
554 }
555 
556 // TODO: Is clipRegion() on the deprecation chopping block. If not it should be...
onClipRegion(const SkRegion & globalRgn,SkClipOp op)557 void Device::onClipRegion(const SkRegion& globalRgn, SkClipOp op) {
558     SkASSERT(op == SkClipOp::kIntersect || op == SkClipOp::kDifference);
559 
560     Transform globalToDevice{this->globalToDevice()};
561 
562     if (globalRgn.isEmpty()) {
563         fClip.clipShape(globalToDevice, Shape{}, op);
564     } else if (globalRgn.isRect()) {
565         // TODO: Region clips are non-AA so this should match non-AA onClipRect(), but we use a
566         // different transform so can't just call that instead.
567         fClip.clipShape(globalToDevice, Shape{SkRect::Make(globalRgn.getBounds())}, op);
568     } else {
569         // TODO: Can we just iterate the region and do non-AA rects for each chunk?
570         SkPath path;
571         globalRgn.getBoundaryPath(&path);
572         fClip.clipShape(globalToDevice, Shape{path}, op);
573     }
574 }
575 
onReplaceClip(const SkIRect & rect)576 void Device::onReplaceClip(const SkIRect& rect) {
577     // ReplaceClip() is currently not intended to be supported in Graphite since it's only used
578     // for emulating legacy clip ops in Android Framework, and apps/devices that require that
579     // should not use Graphite. However, if it needs to be supported, we could probably implement
580     // it by:
581     //  1. Flush all pending clip element depth draws.
582     //  2. Draw a fullscreen rect to the depth attachment using a Z value greater than what's
583     //     been used so far.
584     //  3. Make sure all future "unclipped" draws use this Z value instead of 0 so they aren't
585     //     sorted before the depth reset.
586     //  4. Make sure all prior elements are inactive so they can't affect subsequent draws.
587     //
588     // For now, just ignore it.
589 }
590 
591 ///////////////////////////////////////////////////////////////////////////////
592 
drawPaint(const SkPaint & paint)593 void Device::drawPaint(const SkPaint& paint) {
594     // We never want to do a fullscreen clear on a fully-lazy render target, because the device size
595     // may be smaller than the final surface we draw to, in which case we don't want to fill the
596     // entire final surface.
597     if (this->clipIsWideOpen() && !fDC->target()->isFullyLazy()) {
598         if (!paint_depends_on_dst(paint)) {
599             if (std::optional<SkColor4f> color = extract_paint_color(paint, fDC->colorInfo())) {
600                 // do fullscreen clear
601                 fDC->clear(*color);
602                 return;
603             }
604             // TODO(michaelludwig): this paint doesn't depend on the destination, so we can reset
605             // the DrawContext to use a discard load op. The drawPaint will cover anything else
606             // entirely. We still need shader evaluation to get per-pixel colors (since the paint
607             // couldn't be reduced to a solid color).
608         }
609     }
610 
611     const Transform& localToDevice = this->localToDeviceTransform();
612     if (!localToDevice.valid()) {
613         // TBD: This matches legacy behavior for drawPaint() that requires local coords, although
614         // v1 handles arbitrary transforms when the paint is solid color because it just fills the
615         // device bounds directly. In the new world it might be nice to have non-invertible
616         // transforms formalized (i.e. no drawing ever, handled at SkCanvas level possibly?)
617         return;
618     }
619     Rect localCoveringBounds = localToDevice.inverseMapRect(fClip.conservativeBounds());
620     this->drawGeometry(localToDevice, Geometry(Shape(localCoveringBounds)), paint, kFillStyle,
621                        DrawFlags::kIgnorePathEffect | DrawFlags::kIgnoreMaskFilter);
622 }
623 
drawRect(const SkRect & r,const SkPaint & paint)624 void Device::drawRect(const SkRect& r, const SkPaint& paint) {
625     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(r)),
626                        paint, SkStrokeRec(paint));
627 }
628 
drawVertices(const SkVertices * vertices,sk_sp<SkBlender> blender,const SkPaint & paint,bool skipColorXform)629 void Device::drawVertices(const SkVertices* vertices, sk_sp<SkBlender> blender,
630                           const SkPaint& paint, bool skipColorXform)  {
631   // TODO - Add GPU handling of skipColorXform once Graphite has its color system more fleshed out.
632   this->drawGeometry(this->localToDeviceTransform(),
633                      Geometry(sk_ref_sp(vertices)),
634                      paint,
635                      kFillStyle,
636                      DrawFlags::kIgnorePathEffect | DrawFlags::kIgnoreMaskFilter,
637                      std::move(blender),
638                      skipColorXform);
639 }
640 
drawOval(const SkRect & oval,const SkPaint & paint)641 void Device::drawOval(const SkRect& oval, const SkPaint& paint) {
642     // TODO: This has wasted effort from the SkCanvas level since it instead converts rrects that
643     // happen to be ovals into this, only for us to go right back to rrect.
644     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(SkRRect::MakeOval(oval))),
645                        paint, SkStrokeRec(paint));
646 }
647 
drawRRect(const SkRRect & rr,const SkPaint & paint)648 void Device::drawRRect(const SkRRect& rr, const SkPaint& paint) {
649     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(rr)),
650                        paint, SkStrokeRec(paint));
651 }
652 
drawPath(const SkPath & path,const SkPaint & paint,bool pathIsMutable)653 void Device::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
654     // TODO: If we do try to inspect the path, it should happen here and possibly after computing
655     // the path effect. Alternatively, all that should be handled in SkCanvas.
656     this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(path)),
657                        paint, SkStrokeRec(paint));
658 }
659 
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & paint)660 void Device::drawPoints(SkCanvas::PointMode mode, size_t count,
661                         const SkPoint* points, const SkPaint& paint) {
662     // TODO: I'm [ml] not sure either CPU or GPU backend really has a fast path for this that
663     // isn't captured by drawOval and drawLine, so could easily be moved into SkCanvas.
664     if (mode == SkCanvas::kPoints_PointMode) {
665         float radius = 0.5f * paint.getStrokeWidth();
666         for (size_t i = 0; i < count; ++i) {
667             SkRect pointRect = SkRect::MakeLTRB(points[i].fX - radius, points[i].fY - radius,
668                                                 points[i].fX + radius, points[i].fY + radius);
669             // drawOval/drawRect with a forced fill style
670             if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
671                 this->drawGeometry(this->localToDeviceTransform(),
672                                    Geometry(Shape(SkRRect::MakeOval(pointRect))),
673                                    paint, kFillStyle);
674             } else {
675                 this->drawGeometry(this->localToDeviceTransform(), Geometry(Shape(pointRect)),
676                                    paint, kFillStyle);
677             }
678         }
679     } else {
680         // Force the style to be a stroke, using the radius and cap from the paint
681         SkStrokeRec stroke(paint, SkPaint::kStroke_Style);
682         size_t inc = (mode == SkCanvas::kLines_PointMode) ? 2 : 1;
683         for (size_t i = 0; i < count-1; i += inc) {
684             this->drawGeometry(this->localToDeviceTransform(),
685                                Geometry(Shape(points[i], points[i + 1])),
686                                paint, stroke);
687         }
688     }
689 }
690 
drawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],SkCanvas::QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)691 void Device::drawEdgeAAQuad(const SkRect& rect,
692                             const SkPoint clip[4],
693                             SkCanvas::QuadAAFlags aaFlags,
694                             const SkColor4f& color,
695                             SkBlendMode mode) {
696     SkPaint solidColorPaint;
697     solidColorPaint.setColor4f(color, /*colorSpace=*/nullptr);
698     solidColorPaint.setBlendMode(mode);
699 
700     auto flags = SkEnumBitMask<EdgeAAQuad::Flags>(static_cast<EdgeAAQuad::Flags>(aaFlags));
701     EdgeAAQuad quad = clip ? EdgeAAQuad(clip, flags) : EdgeAAQuad(rect, flags);
702     this->drawGeometry(this->localToDeviceTransform(), Geometry(quad), solidColorPaint, kFillStyle,
703                        DrawFlags::kIgnoreMaskFilter | DrawFlags::kIgnorePathEffect);
704 }
705 
drawEdgeAAImageSet(const SkCanvas::ImageSetEntry[],int count,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions &,const SkPaint &,SkCanvas::SrcRectConstraint)706 void Device::drawEdgeAAImageSet(const SkCanvas::ImageSetEntry[], int count,
707                                 const SkPoint dstClips[], const SkMatrix preViewMatrices[],
708                                 const SkSamplingOptions&, const SkPaint&,
709                                 SkCanvas::SrcRectConstraint) {
710     // TODO: Implement this by merging the logic of drawImageRect and drawEdgeAAQuad.
711 }
712 
drawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint constraint)713 void Device::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
714                            const SkSamplingOptions& sampling, const SkPaint& paint,
715                            SkCanvas::SrcRectConstraint constraint) {
716     SkASSERT(dst.isFinite());
717     SkASSERT(dst.isSorted());
718 
719     // TODO: All of this logic should be handled in SkCanvas, since it's the same for every backend
720     SkRect tmpSrc, tmpDst = dst;
721     SkRect imgBounds = SkRect::Make(image->bounds());
722 
723     if (src) {
724         tmpSrc = *src;
725     } else {
726         tmpSrc = SkRect::Make(image->bounds());
727     }
728     SkMatrix matrix = SkMatrix::RectToRect(tmpSrc, dst);
729 
730     // clip the tmpSrc to the bounds of the image, and recompute the dest rect if
731     // needed (i.e., if the src was clipped). No check needed if src==null.
732     if (src) {
733         if (!imgBounds.contains(tmpSrc)) {
734             if (!tmpSrc.intersect(imgBounds)) {
735                 return; // nothing to draw
736             }
737             // recompute dst, based on the smaller tmpSrc
738             matrix.mapRect(&tmpDst, tmpSrc);
739             if (!tmpDst.isFinite()) {
740                 return;
741             }
742         }
743     }
744 
745     auto [ imageToDraw, newSampling ] = skgpu::graphite::GetGraphiteBacked(this->recorder(),
746                                                                            image, sampling);
747     if (!imageToDraw) {
748         SKGPU_LOG_W("Device::drawImageRect: Creation of Graphite-backed image failed");
749         return;
750     }
751 
752     SkPaint paintWithShader(paint);
753     if (!create_img_shader_paint(std::move(imageToDraw), tmpSrc, newSampling,
754                                  &matrix, &paintWithShader)) {
755         return;
756     }
757 
758     this->drawRect(tmpDst, paintWithShader);
759 }
760 
onDrawGlyphRunList(SkCanvas * canvas,const sktext::GlyphRunList & glyphRunList,const SkPaint & initialPaint,const SkPaint & drawingPaint)761 void Device::onDrawGlyphRunList(SkCanvas* canvas,
762                                 const sktext::GlyphRunList& glyphRunList,
763                                 const SkPaint& initialPaint,
764                                 const SkPaint& drawingPaint) {
765     fRecorder->priv().textBlobCache()->drawGlyphRunList(canvas,
766                                                         this->localToDevice(),
767                                                         glyphRunList,
768                                                         drawingPaint,
769                                                         this->strikeDeviceInfo(),
770                                                         this);
771 }
772 
drawAtlasSubRun(const sktext::gpu::AtlasSubRun * subRun,SkPoint drawOrigin,const SkPaint & paint,sk_sp<SkRefCnt> subRunStorage)773 void Device::drawAtlasSubRun(const sktext::gpu::AtlasSubRun* subRun,
774                              SkPoint drawOrigin,
775                              const SkPaint& paint,
776                              sk_sp<SkRefCnt> subRunStorage) {
777     const int subRunEnd = subRun->glyphCount();
778     for (int subRunCursor = 0; subRunCursor < subRunEnd;) {
779         // For the remainder of the run, add any atlas uploads to the Recorder's AtlasManager
780         auto[ok, glyphsRegenerated] = subRun->regenerateAtlas(subRunCursor, subRunEnd, fRecorder);
781         // There was a problem allocating the glyph in the atlas. Bail.
782         if (!ok) {
783             return;
784         }
785         if (glyphsRegenerated) {
786             auto [bounds, localToDevice] = subRun->boundsAndDeviceMatrix(
787                                                    this->localToDeviceTransform(), drawOrigin);
788             SkPaint subRunPaint = paint;
789             // For color emoji, only the paint alpha affects the final color
790             if (subRun->maskFormat() == skgpu::MaskFormat::kARGB) {
791                 subRunPaint.setColor(SK_ColorWHITE);
792                 subRunPaint.setAlphaf(paint.getAlphaf());
793             }
794             this->drawGeometry(localToDevice,
795                                Geometry(SubRunData(subRun, subRunStorage, bounds, subRunCursor,
796                                                    glyphsRegenerated, fRecorder)),
797                                subRunPaint,
798                                kFillStyle,
799                                DrawFlags::kIgnorePathEffect | DrawFlags::kIgnoreMaskFilter);
800         }
801         subRunCursor += glyphsRegenerated;
802 
803         if (subRunCursor < subRunEnd) {
804             // Flush if not all the glyphs are handled because the atlas is out of space.
805             // We flush every Device because the glyphs that are being flushed/referenced are not
806             // necessarily specific to this Device. This addresses both multiple SkSurfaces within
807             // a Recorder, and nested layers.
808             ATRACE_ANDROID_FRAMEWORK_ALWAYS("Atlas full");
809             fRecorder->priv().flushTrackedDevices();
810         }
811     }
812 }
813 
drawGeometry(const Transform & localToDevice,const Geometry & geometry,const SkPaint & paint,const SkStrokeRec & style,SkEnumBitMask<DrawFlags> flags,sk_sp<SkBlender> primitiveBlender,bool skipColorXform)814 void Device::drawGeometry(const Transform& localToDevice,
815                           const Geometry& geometry,
816                           const SkPaint& paint,
817                           const SkStrokeRec& style,
818                           SkEnumBitMask<DrawFlags> flags,
819                           sk_sp<SkBlender> primitiveBlender,
820                           bool skipColorXform) {
821     if (!localToDevice.valid()) {
822         // If the transform is not invertible or not finite then drawing isn't well defined.
823         SKGPU_LOG_W("Skipping draw with non-invertible/non-finite transform.");
824         return;
825     }
826 
827     // Heavy weight paint options like path effects, mask filters, and stroke-and-fill style are
828     // applied on the CPU by generating a new shape and recursing on drawShape() with updated flags
829     if (!(flags & DrawFlags::kIgnorePathEffect) && paint.getPathEffect()) {
830         // Apply the path effect before anything else, which if we are applying here, means that we
831         // are dealing with a Shape. drawVertices (and a SkVertices geometry) should pass in
832         // kIgnorePathEffect per SkCanvas spec. Text geometry also should pass in kIgnorePathEffect
833         // because the path effect is applied per glyph by the SkStrikeSpec already.
834         SkASSERT(geometry.isShape());
835 
836         // TODO: If asADash() returns true and the base path matches the dashing fast path, then
837         // that should be detected now as well. Maybe add dashPath to Device so canvas can handle it
838         SkStrokeRec newStyle = style;
839         newStyle.setResScale(localToDevice.maxScaleFactor());
840         SkPath dst;
841         if (paint.getPathEffect()->filterPath(&dst, geometry.shape().asPath(), &newStyle,
842                                               nullptr, localToDevice)) {
843             // Recurse using the path and new style, while disabling downstream path effect handling
844             this->drawGeometry(localToDevice, Geometry(Shape(dst)), paint, newStyle,
845                                flags | DrawFlags::kIgnorePathEffect, std::move(primitiveBlender),
846                                skipColorXform);
847             return;
848         } else {
849             SKGPU_LOG_W("Path effect failed to apply, drawing original path.");
850             this->drawGeometry(localToDevice, geometry, paint, style,
851                                flags | DrawFlags::kIgnorePathEffect, std::move(primitiveBlender),
852                                skipColorXform);
853             return;
854         }
855     }
856 
857     if (!(flags & DrawFlags::kIgnoreMaskFilter) && paint.getMaskFilter()) {
858         // TODO: Handle mask filters, ignored for the sprint.
859         // TODO: Could this be handled by SkCanvas by drawing a mask, blurring, and then sampling
860         // with a rect draw? What about fast paths for rrect blur masks...
861         this->drawGeometry(localToDevice, geometry, paint, style,
862                            flags | DrawFlags::kIgnoreMaskFilter, std::move(primitiveBlender),
863                            skipColorXform);
864         return;
865     }
866 
867     // TODO: The tessellating path renderers haven't implemented perspective yet, so transform to
868     // device space so we draw something approximately correct (barring local coord issues).
869     if (geometry.isShape() && localToDevice.type() == Transform::Type::kProjection &&
870         !is_simple_shape(geometry.shape(), style.getStyle())) {
871         SkPath devicePath = geometry.shape().asPath();
872         devicePath.transform(localToDevice.matrix().asM33());
873         this->drawGeometry(Transform::Identity(), Geometry(Shape(devicePath)), paint, style, flags,
874                            std::move(primitiveBlender), skipColorXform);
875         return;
876     }
877 
878     // TODO: Manually snap pixels for rects, rrects, and lines if paint is non-AA (ideally also
879     // consider snapping stroke width and/or adjusting geometry for hairlines). This pixel snapping
880     // math should be consistent with how non-AA clip [r]rects are handled.
881 
882     // If we got here, then path effects and mask filters should have been handled and the style
883     // should be fill or stroke/hairline. Stroke-and-fill is not handled by DrawContext, but is
884     // emulated here by drawing twice--one stroke and one fill--using the same depth value.
885     SkASSERT(!SkToBool(paint.getPathEffect()) || (flags & DrawFlags::kIgnorePathEffect));
886     SkASSERT(!SkToBool(paint.getMaskFilter()) || (flags & DrawFlags::kIgnoreMaskFilter));
887 
888     // Check if we have room to record into the current list before determining clipping and order
889     SkStrokeRec::Style styleType = style.getStyle();
890     if (this->needsFlushBeforeDraw(styleType == SkStrokeRec::kStrokeAndFill_Style ? 2 : 1)) {
891         this->flushPendingWorkToRecorder();
892     }
893 
894     DrawOrder order(fCurrentDepth.next());
895     auto [clip, clipOrder] = fClip.applyClipToDraw(
896             fColorDepthBoundsManager.get(), localToDevice, geometry, style, order.depth());
897     if (clip.drawBounds().isEmptyNegativeOrNaN()) {
898         // Clipped out, so don't record anything
899         return;
900     }
901     // Some Renderer decisions are based on estimated fill rate, which requires the clipped bounds.
902     // Since the fallbacks shouldn't change the bounds of the draw, it's okay to have evaluated the
903     // clip stack before calling ChooseRenderer.
904     const Renderer* renderer = this->chooseRenderer(geometry, clip, style, /*requireMSAA=*/false);
905     if (!renderer) {
906         SKGPU_LOG_W("Skipping draw with no supported renderer.");
907         return;
908     }
909 
910 #if defined(SK_DEBUG)
911     // Renderers and their component RenderSteps have flexibility in defining their
912     // DepthStencilSettings. However, the clipping and ordering managed between Device and ClipStack
913     // requires that only GREATER or GEQUAL depth tests are used for draws recorded through the
914     // client-facing, painters-order-oriented API. We assert here vs. in Renderer's constructor to
915     // allow internal-oriented Renderers that are never selected for a "regular" draw call to have
916     // more flexibility in their settings.
917     for (const RenderStep* step : renderer->steps()) {
918         auto dss = step->depthStencilSettings();
919         SkASSERT((!step->performsShading() || dss.fDepthTestEnabled) &&
920                  (!dss.fDepthTestEnabled ||
921                   dss.fDepthCompareOp == CompareOp::kGreater ||
922                   dss.fDepthCompareOp == CompareOp::kGEqual));
923     }
924 #endif
925 
926     // A draw's order always depends on the clips that must be drawn before it
927     order.dependsOnPaintersOrder(clipOrder);
928 
929     // A primitive blender should be ignored if there is no primitive color to blend against.
930     // Additionally, if a renderer emits a primitive color, then a null primitive blender should
931     // be interpreted as SrcOver blending mode.
932     if (!renderer->emitsPrimitiveColor()) {
933         primitiveBlender = nullptr;
934     } else if (!SkToBool(primitiveBlender)) {
935         primitiveBlender = SkBlender::Mode(SkBlendMode::kSrcOver);
936     }
937 
938     // If a draw is not opaque, it must be drawn after the most recent draw it intersects with in
939     // order to blend correctly. We always query the most recent draw (even when opaque) because it
940     // also lets Device easily track whether or not there are any overlapping draws.
941     PaintParams shading{paint, std::move(primitiveBlender), skipColorXform};
942     const bool dependsOnDst = renderer->emitsCoverage() || paint_depends_on_dst(shading);
943     CompressedPaintersOrder prevDraw =
944             fColorDepthBoundsManager->getMostRecentDraw(clip.drawBounds());
945     if (dependsOnDst) {
946         order.dependsOnPaintersOrder(prevDraw);
947     }
948 
949     // Now that the base paint order and draw bounds are finalized, if the Renderer relies on the
950     // stencil attachment, we compute a secondary sorting field to allow disjoint draws to reorder
951     // the RenderSteps across draws instead of in sequence for each draw.
952     if (renderer->depthStencilFlags() & DepthStencilFlags::kStencil) {
953         DisjointStencilIndex setIndex = fDisjointStencilSet->add(order.paintOrder(),
954                                                                  clip.drawBounds());
955         order.dependsOnStencil(setIndex);
956     }
957 
958     if (styleType == SkStrokeRec::kStroke_Style ||
959         styleType == SkStrokeRec::kHairline_Style ||
960         styleType == SkStrokeRec::kStrokeAndFill_Style) {
961         // For stroke-and-fill, 'renderer' is used for the fill and we always use the
962         // TessellatedStrokes renderer; for stroke and hairline, 'renderer' is used.
963         StrokeStyle stroke(style.getWidth(), style.getMiter(), style.getJoin(), style.getCap());
964         fDC->recordDraw(styleType == SkStrokeRec::kStrokeAndFill_Style
965                                ? fRecorder->priv().rendererProvider()->tessellatedStrokes()
966                                : renderer,
967                         localToDevice, geometry, clip, order, &shading, &stroke);
968     }
969     if (styleType == SkStrokeRec::kFill_Style ||
970         styleType == SkStrokeRec::kStrokeAndFill_Style) {
971         fDC->recordDraw(renderer, localToDevice, geometry, clip, order, &shading, nullptr);
972     }
973 
974     // TODO: If 'fullyOpaque' is true, it might be useful to store the draw bounds and Z in a
975     // special occluders list for filtering the DrawList/DrawPass when flushing.
976     // const bool fullyOpaque = !dependsOnDst &&
977     //                          clipOrder == DrawOrder::kNoIntersection &&
978     //                          shape.isRect() &&
979     //                          localToDevice.type() <= Transform::Type::kRectStaysRect;
980 
981     // Post-draw book keeping (bounds manager, depth tracking, etc.)
982     fColorDepthBoundsManager->recordDraw(clip.drawBounds(), order.paintOrder());
983     fCurrentDepth = order.depth();
984     fDrawsOverlap |= (prevDraw != DrawOrder::kNoIntersection);
985 }
986 
drawClipShape(const Transform & localToDevice,const Shape & shape,const Clip & clip,DrawOrder order)987 void Device::drawClipShape(const Transform& localToDevice,
988                            const Shape& shape,
989                            const Clip& clip,
990                            DrawOrder order) {
991     // This call represents one of the deferred clip shapes that's already pessimistically counted
992     // in needsFlushBeforeDraw(), so the DrawContext should have room to add it.
993     SkASSERT(fDC->pendingDrawCount() + 1 < DrawList::kMaxDraws);
994 
995     // A clip draw's state is almost fully defined by the ClipStack. The only thing we need
996     // to account for is selecting a Renderer and tracking the stencil buffer usage.
997     Geometry geometry{shape};
998     const Renderer* renderer = this->chooseRenderer(geometry, clip, kFillStyle,
999                                                     /*requireMSAA=*/true);
1000     if (!renderer) {
1001         SKGPU_LOG_W("Skipping clip with no supported path renderer.");
1002         return;
1003     } else if (renderer->depthStencilFlags() & DepthStencilFlags::kStencil) {
1004         DisjointStencilIndex setIndex = fDisjointStencilSet->add(order.paintOrder(),
1005                                                                  clip.drawBounds());
1006         order.dependsOnStencil(setIndex);
1007     }
1008     // Anti-aliased clipping requires the renderer to use MSAA to modify the depth per sample, so
1009     // analytic coverage renderers cannot be used.
1010     SkASSERT(!renderer->emitsCoverage() && renderer->requiresMSAA());
1011 
1012     // Clips draws are depth-only (null PaintParams), and filled (null StrokeStyle).
1013     // TODO: Remove this CPU-transform once perspective is supported for all path renderers
1014     if (localToDevice.type() == Transform::Type::kProjection) {
1015         SkPath devicePath = geometry.shape().asPath();
1016         devicePath.transform(localToDevice.matrix().asM33());
1017         fDC->recordDraw(renderer, Transform::Identity(), Geometry(Shape(devicePath)), clip, order,
1018                         nullptr, nullptr);
1019     } else {
1020         fDC->recordDraw(renderer, localToDevice, geometry, clip, order, nullptr, nullptr);
1021     }
1022     // This ensures that draws recorded after this clip shape has been popped off the stack will
1023     // be unaffected by the Z value the clip shape wrote to the depth attachment.
1024     if (order.depth() > fCurrentDepth) {
1025         fCurrentDepth = order.depth();
1026     }
1027 }
1028 
1029 // TODO: Currently all Renderers are always defined, but with config options and caps that may not
1030 // be the case, in which case chooseRenderer() will have to go through compatible choices.
chooseRenderer(const Geometry & geometry,const Clip & clip,const SkStrokeRec & style,bool requireMSAA) const1031 const Renderer* Device::chooseRenderer(const Geometry& geometry,
1032                                        const Clip& clip,
1033                                        const SkStrokeRec& style,
1034                                        bool requireMSAA) const {
1035     const RendererProvider* renderers = fRecorder->priv().rendererProvider();
1036     SkStrokeRec::Style type = style.getStyle();
1037 
1038     if (geometry.isSubRun()) {
1039         SkASSERT(!requireMSAA);
1040         return geometry.subRunData().subRun()->renderer(renderers);
1041     } else if (geometry.isVertices()) {
1042         SkVerticesPriv info(geometry.vertices()->priv());
1043         return renderers->vertices(info.mode(), info.hasColors(), info.hasTexCoords());
1044     } else if (geometry.isEdgeAAQuad()) {
1045         SkASSERT(!requireMSAA && style.isFillStyle());
1046         return renderers->analyticRRect(); // handled by the same system as rects and round rects
1047     } else if (!geometry.isShape()) {
1048         // We must account for new Geometry types with specific Renderers
1049         return nullptr;
1050     }
1051 
1052     const Shape& shape = geometry.shape();
1053     // We can't use this renderer if we require MSAA for an effect (i.e. clipping or stroke+fill).
1054     if (!requireMSAA && is_simple_shape(shape, type)) {
1055         return renderers->analyticRRect();
1056     }
1057 
1058     // If we got here, it requires tessellated path rendering or an MSAA technique applied to a
1059     // simple shape (so we interpret them as paths to reduce the number of pipelines we need).
1060 
1061     // TODO: All shapes that select a tessellating path renderer need to be "pre-chopped" if they
1062     // are large enough to exceed the fixed count tessellation limits. Fills are pre-chopped to the
1063     // viewport bounds, strokes and stroke-and-fills are pre-chopped to the viewport bounds outset
1064     // by the stroke radius (hence taking the whole style and not just its type).
1065 
1066     if (type == SkStrokeRec::kStroke_Style ||
1067         type == SkStrokeRec::kHairline_Style) {
1068         // Unlike in Ganesh, the HW stroke tessellator can work with arbitrary paints since the
1069         // depth test prevents double-blending when there is transparency, thus we can HW stroke
1070         // any path regardless of its paint.
1071         // TODO: We treat inverse-filled strokes as regular strokes. We could handle them by
1072         // stenciling first with the HW stroke tessellator and then covering their bounds, but
1073         // inverse-filled strokes are not well-specified in our public canvas behavior so we may be
1074         // able to remove it.
1075         return renderers->tessellatedStrokes();
1076     }
1077 
1078     // 'type' could be kStrokeAndFill, but in that case chooseRenderer() is meant to return the
1079     // fill renderer since tessellatedStrokes() will always be used for the stroke pass.
1080     if (shape.convex() && !shape.inverted()) {
1081         // TODO: Ganesh doesn't have a curve+middle-out triangles option for convex paths, but it
1082         // would be pretty trivial to spin up.
1083         return renderers->convexTessellatedWedges();
1084     } else {
1085         // TODO: Combine this heuristic with what is used in PathStencilCoverOp to choose between
1086         // wedges curves consistently in Graphite and Ganesh.
1087         const bool preferWedges = (shape.isPath() && shape.path().countVerbs() < 50) ||
1088                                    clip.drawBounds().area() <= (256 * 256);
1089 
1090         if (preferWedges) {
1091             return renderers->stencilTessellatedWedges(shape.fillType());
1092         } else {
1093             return renderers->stencilTessellatedCurvesAndTris(shape.fillType());
1094         }
1095     }
1096 }
1097 
flushPendingWorkToRecorder()1098 void Device::flushPendingWorkToRecorder() {
1099     SkASSERT(fRecorder);
1100 
1101     // TODO: we may need to further split this function up since device->device drawList and
1102     // DrawPass stealing will need to share some of the same logic w/o becoming a Task.
1103 
1104     // push any pending uploads from the atlasmanager
1105     auto atlasManager = fRecorder->priv().atlasManager();
1106     if (!fDC->recordTextUploads(atlasManager)) {
1107         SKGPU_LOG_E("AtlasManager uploads have failed -- may see invalid results.");
1108     }
1109 
1110     auto uploadTask = fDC->snapUploadTask(fRecorder);
1111     if (uploadTask) {
1112         fRecorder->priv().add(std::move(uploadTask));
1113     }
1114 
1115 #ifdef SK_ENABLE_PIET_GPU
1116     auto pietTask = fDC->snapPietRenderTask(fRecorder);
1117     if (pietTask) {
1118         fRecorder->priv().add(std::move(pietTask));
1119     }
1120 #endif
1121 
1122     fClip.recordDeferredClipDraws();
1123     auto drawTask = fDC->snapRenderPassTask(fRecorder);
1124     if (drawTask) {
1125         fRecorder->priv().add(std::move(drawTask));
1126     }
1127 
1128     // Reset accumulated state tracking since everything that it referred to has been moved into
1129     // an immutable DrawPass.
1130     fColorDepthBoundsManager->reset();
1131     fDisjointStencilSet->reset();
1132     fCurrentDepth = DrawOrder::kClearDepth;
1133     // NOTE: fDrawsOverlap is not reset here because that is a persistent property of everything
1134     // drawn into the Device, and not just the currently accumulating pass.
1135 }
1136 
needsFlushBeforeDraw(int numNewDraws) const1137 bool Device::needsFlushBeforeDraw(int numNewDraws) const {
1138     // Must also account for the elements in the clip stack that might need to be recorded.
1139     numNewDraws += fClip.maxDeferredClipDraws();
1140     return (DrawList::kMaxDraws - fDC->pendingDrawCount()) < numNewDraws;
1141 }
1142 
drawDevice(SkBaseDevice * device,const SkSamplingOptions & sampling,const SkPaint & paint)1143 void Device::drawDevice(SkBaseDevice* device,
1144                         const SkSamplingOptions& sampling,
1145                         const SkPaint& paint) {
1146     this->SkBaseDevice::drawDevice(device, sampling, paint);
1147 }
1148 
drawSpecial(SkSpecialImage * special,const SkMatrix & localToDevice,const SkSamplingOptions & sampling,const SkPaint & paint)1149 void Device::drawSpecial(SkSpecialImage* special,
1150                          const SkMatrix& localToDevice,
1151                          const SkSamplingOptions& sampling,
1152                          const SkPaint& paint) {
1153     SkASSERT(!paint.getMaskFilter() && !paint.getImageFilter());
1154 
1155     sk_sp<SkImage> img = special->asImage();
1156     if (!img) {
1157         SKGPU_LOG_W("Couldn't get Graphite-backed special image as image");
1158         return;
1159     }
1160 
1161     // TODO: remove this check once Graphite has image filter support.
1162     if (!img->isTextureBacked()) {
1163         return;
1164     }
1165 
1166     SkRect src = SkRect::Make(special->subset());
1167     SkRect dst = SkRect::MakeWH(special->width(), special->height());
1168     SkMatrix srcToDst = SkMatrix::RectToRect(src, dst);
1169     SkASSERT(srcToDst.isTranslate());
1170 
1171     SkPaint paintWithShader(paint);
1172     if (!create_img_shader_paint(std::move(img), src, sampling, &srcToDst, &paintWithShader)) {
1173         return;
1174     }
1175 
1176     this->drawGeometry(Transform(SkM44(localToDevice)),
1177                        Geometry(Shape(dst)),
1178                        paintWithShader,
1179                        kFillStyle,
1180                        DrawFlags::kIgnorePathEffect | DrawFlags::kIgnoreMaskFilter);
1181 }
1182 
makeSpecial(const SkBitmap &)1183 sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap&) {
1184     return nullptr;
1185 }
1186 
makeSpecial(const SkImage *)1187 sk_sp<SkSpecialImage> Device::makeSpecial(const SkImage*) {
1188     return nullptr;
1189 }
1190 
snapSpecial(const SkIRect & subset,bool forceCopy)1191 sk_sp<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) {
1192     this->flushPendingWorkToRecorder();
1193 
1194     SkIRect finalSubset = subset;
1195     TextureProxyView view = fDC->readSurfaceView(fRecorder->priv().caps());
1196     if (forceCopy || !view) {
1197         // TODO: fill this in. 'forceCopy' is only true for backdrop saveLayers. A non-readable
1198         // surface view could happen any time though.
1199         return nullptr;
1200     }
1201 
1202     return SkSpecialImage::MakeGraphite(fRecorder,
1203                                         finalSubset,
1204                                         kNeedNewImageUniqueID_SpecialImage,
1205                                         std::move(view),
1206                                         this->imageInfo().colorInfo(),
1207                                         this->surfaceProps());
1208 }
1209 
target()1210 TextureProxy* Device::target() { return fDC->target(); }
1211 
readSurfaceView() const1212 TextureProxyView Device::readSurfaceView() const {
1213     if (!fRecorder) {
1214         return {};
1215     }
1216     return fDC->readSurfaceView(fRecorder->priv().caps());
1217 }
1218 
1219 } // namespace skgpu::graphite
1220