1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
6 
7 #include "base/bind.h"
8 #include "base/metrics/histogram_functions.h"
9 #include "base/stl_util.h"
10 #include "base/trace_event/memory_dump_manager.h"
11 #include "base/trace_event/process_memory_dump.h"
12 #include "build/build_config.h"
13 #include "cc/paint/decode_stashing_image_provider.h"
14 #include "cc/paint/display_item_list.h"
15 #include "cc/tiles/software_image_decode_cache.h"
16 #include "components/viz/common/resources/resource_format_utils.h"
17 #include "gpu/GLES2/gl2extchromium.h"
18 #include "gpu/command_buffer/client/raster_interface.h"
19 #include "gpu/command_buffer/common/capabilities.h"
20 #include "gpu/config/gpu_driver_bug_workaround_type.h"
21 #include "gpu/config/gpu_feature_info.h"
22 #include "skia/buildflags.h"
23 #include "third_party/blink/public/common/features.h"
24 #include "third_party/blink/public/platform/platform.h"
25 #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
26 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
27 #include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
28 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
29 #include "third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h"
30 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
31 #include "third_party/skia/include/core/SkSurface.h"
32 #include "third_party/skia/include/gpu/GrDirectContext.h"
33 
34 namespace blink {
35 
36 class FlushForImageListener {
37   // With deferred rendering it's possible for a drawImage operation on a canvas
38   // to trigger a copy-on-write if another canvas has a read reference to it.
39   // This can cause serious regressions due to extra allocations:
40   // crbug.com/1030108. FlushForImageListener keeps a list of all active 2d
41   // contexts on a thread and notifies them when one is attempting copy-on
42   // write. If the notified context has a read reference to the canvas
43   // attempting a copy-on-write it then flushes so as to make the copy-on-write
44   // unnecessary.
45  public:
46   static FlushForImageListener* GetFlushForImageListener();
AddObserver(CanvasResourceProvider * observer)47   void AddObserver(CanvasResourceProvider* observer) {
48     observers_.AddObserver(observer);
49   }
50 
RemoveObserver(CanvasResourceProvider * observer)51   void RemoveObserver(CanvasResourceProvider* observer) {
52     observers_.RemoveObserver(observer);
53   }
54 
NotifyFlushForImage(cc::PaintImage::ContentId content_id)55   void NotifyFlushForImage(cc::PaintImage::ContentId content_id) {
56     for (CanvasResourceProvider& obs : observers_)
57       obs.OnFlushForImage(content_id);
58   }
59 
60  private:
61   friend class WTF::ThreadSpecific<FlushForImageListener>;
62   base::ObserverList<CanvasResourceProvider> observers_;
63 };
64 
GetFlushForImageListener()65 static FlushForImageListener* GetFlushForImageListener() {
66   DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<FlushForImageListener>,
67                                   flush_for_image_listener, ());
68   return flush_for_image_listener;
69 }
70 
71 namespace {
72 
IsGMBAllowed(IntSize size,const CanvasColorParams & color_params,const gpu::Capabilities & caps)73 bool IsGMBAllowed(IntSize size,
74                   const CanvasColorParams& color_params,
75                   const gpu::Capabilities& caps) {
76   return gpu::IsImageSizeValidForGpuMemoryBufferFormat(
77              gfx::Size(size), color_params.GetBufferFormat()) &&
78          gpu::IsImageFromGpuMemoryBufferFormatSupported(
79              color_params.GetBufferFormat(), caps);
80 }
81 
82 }  // namespace
83 
84 class CanvasResourceProvider::CanvasImageProvider : public cc::ImageProvider {
85  public:
86   CanvasImageProvider(cc::ImageDecodeCache* cache_n32,
87                       cc::ImageDecodeCache* cache_f16,
88                       const gfx::ColorSpace& target_color_space,
89                       SkColorType target_color_type,
90                       cc::PlaybackImageProvider::RasterMode raster_mode);
91   ~CanvasImageProvider() override = default;
92 
93   // cc::ImageProvider implementation.
94   cc::ImageProvider::ScopedResult GetRasterContent(
95       const cc::DrawImage&) override;
96 
97  private:
98   cc::PlaybackImageProvider::RasterMode raster_mode_;
99   base::Optional<cc::PlaybackImageProvider> playback_image_provider_n32_;
100   base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_;
101 
102   base::WeakPtrFactory<CanvasImageProvider> weak_factory_{this};
103 
104   DISALLOW_COPY_AND_ASSIGN(CanvasImageProvider);
105 };
106 
107 // * Renders to a Skia RAM-backed bitmap.
108 // * Mailboxing is not supported : cannot be directly composited.
109 class CanvasResourceProviderBitmap : public CanvasResourceProvider {
110  public:
CanvasResourceProviderBitmap(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)111   CanvasResourceProviderBitmap(
112       const IntSize& size,
113       SkFilterQuality filter_quality,
114       const CanvasColorParams& color_params,
115       base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
116       : CanvasResourceProvider(kBitmap,
117                                size,
118                                filter_quality,
119                                color_params,
120                                true /*is_origin_top_left*/,
121                                nullptr /*context_provider_wrapper*/,
122                                std::move(resource_dispatcher)) {}
123 
124   ~CanvasResourceProviderBitmap() override = default;
125 
IsValid() const126   bool IsValid() const final { return GetSkSurface(); }
IsAccelerated() const127   bool IsAccelerated() const final { return false; }
SupportsDirectCompositing() const128   bool SupportsDirectCompositing() const override { return false; }
129 
130  private:
ProduceCanvasResource()131   scoped_refptr<CanvasResource> ProduceCanvasResource() override {
132     return nullptr;  // Does not support direct compositing
133   }
134 
Snapshot(const ImageOrientation & orientation)135   scoped_refptr<StaticBitmapImage> Snapshot(
136       const ImageOrientation& orientation) override {
137     TRACE_EVENT0("blink", "CanvasResourceProviderBitmap::Snapshot");
138     return SnapshotInternal(orientation);
139   }
140 
CreateSkSurface() const141   sk_sp<SkSurface> CreateSkSurface() const override {
142     TRACE_EVENT0("blink", "CanvasResourceProviderBitmap::CreateSkSurface");
143 
144     SkImageInfo info = SkImageInfo::Make(
145         Size().Width(), Size().Height(), ColorParams().GetSkColorType(),
146         kPremul_SkAlphaType, ColorParams().GetSkColorSpace());
147     return SkSurface::MakeRaster(info, ColorParams().GetSkSurfaceProps());
148   }
149 };
150 
151 // * Renders to a shared memory bitmap.
152 // * Uses SharedBitmaps to pass frames directly to the compositor.
153 class CanvasResourceProviderSharedBitmap : public CanvasResourceProviderBitmap {
154  public:
CanvasResourceProviderSharedBitmap(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)155   CanvasResourceProviderSharedBitmap(
156       const IntSize& size,
157       SkFilterQuality filter_quality,
158       const CanvasColorParams& color_params,
159       base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
160       : CanvasResourceProviderBitmap(size,
161                                      filter_quality,
162                                      color_params,
163                                      std::move(resource_dispatcher)) {
164     DCHECK(ResourceDispatcher());
165     type_ = kSharedBitmap;
166   }
167   ~CanvasResourceProviderSharedBitmap() override = default;
SupportsDirectCompositing() const168   bool SupportsDirectCompositing() const override { return true; }
169 
170  private:
CreateResource()171   scoped_refptr<CanvasResource> CreateResource() final {
172     CanvasColorParams color_params = ColorParams();
173     if (!IsBitmapFormatSupported(color_params.TransferableResourceFormat())) {
174       // If the rendering format is not supported, downgrate to 8-bits.
175       // TODO(junov): Should we try 12-12-12-12 and 10-10-10-2?
176       color_params.SetCanvasPixelFormat(
177           CanvasColorParams::GetNativeCanvasPixelFormat());
178     }
179 
180     return CanvasResourceSharedBitmap::Create(Size(), color_params,
181                                               CreateWeakPtr(), FilterQuality());
182   }
183 
ProduceCanvasResource()184   scoped_refptr<CanvasResource> ProduceCanvasResource() final {
185     DCHECK(GetSkSurface());
186     scoped_refptr<CanvasResource> output_resource = NewOrRecycledResource();
187     if (!output_resource)
188       return nullptr;
189 
190     auto paint_image = MakeImageSnapshot();
191     if (!paint_image)
192       return nullptr;
193     DCHECK(!paint_image.IsTextureBacked());
194 
195     output_resource->TakeSkImage(paint_image.GetSwSkImage());
196 
197     return output_resource;
198   }
199 };
200 
201 // * Renders to a SharedImage, which manages memory internally.
202 // * Layers are overlay candidates.
203 class CanvasResourceProviderSharedImage : public CanvasResourceProvider {
204  public:
CanvasResourceProviderSharedImage(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,bool is_origin_top_left,bool is_accelerated,bool skia_use_dawn,uint32_t shared_image_usage_flags)205   CanvasResourceProviderSharedImage(
206       const IntSize& size,
207       SkFilterQuality filter_quality,
208       const CanvasColorParams& color_params,
209       base::WeakPtr<WebGraphicsContext3DProviderWrapper>
210           context_provider_wrapper,
211       bool is_origin_top_left,
212       bool is_accelerated,
213       bool skia_use_dawn,
214       uint32_t shared_image_usage_flags)
215       : CanvasResourceProvider(
216             skia_use_dawn ? kSkiaDawnSharedImage : kSharedImage,
217             size,
218             filter_quality,
219             // TODO(khushalsagar): The software path seems to be assuming N32
220             // somewhere in the later pipeline but for offscreen canvas only.
221             CanvasColorParams(color_params.ColorSpace(),
222                               is_accelerated && color_params.PixelFormat() !=
223                                                     CanvasPixelFormat::kF16
224                                   ? CanvasPixelFormat::kRGBA8
225                                   : color_params.PixelFormat(),
226                               color_params.GetOpacityMode()),
227             is_origin_top_left,
228             std::move(context_provider_wrapper),
229             nullptr /* resource_dispatcher */),
230         is_accelerated_(is_accelerated),
231         shared_image_usage_flags_(shared_image_usage_flags),
232         use_oop_rasterization_(is_accelerated && ContextProviderWrapper()
233                                                      ->ContextProvider()
234                                                      ->GetCapabilities()
235                                                      .supports_oop_raster) {
236     resource_ = NewOrRecycledResource();
237     GetFlushForImageListener()->AddObserver(this);
238 
239     if (resource_)
240       EnsureWriteAccess();
241   }
242 
~CanvasResourceProviderSharedImage()243   ~CanvasResourceProviderSharedImage() override {
244     GetFlushForImageListener()->RemoveObserver(this);
245   }
246 
IsAccelerated() const247   bool IsAccelerated() const final { return is_accelerated_; }
SupportsDirectCompositing() const248   bool SupportsDirectCompositing() const override { return true; }
IsValid() const249   bool IsValid() const final {
250     if (use_oop_rasterization_)
251       return !IsGpuContextLost();
252     else
253       return GetSkSurface() && !IsGpuContextLost();
254   }
255 
SupportsSingleBuffering() const256   bool SupportsSingleBuffering() const override {
257     return shared_image_usage_flags_ &
258            gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
259   }
GetBackingMailboxForOverwrite(MailboxSyncMode sync_mode)260   gpu::Mailbox GetBackingMailboxForOverwrite(
261       MailboxSyncMode sync_mode) override {
262     DCHECK(is_accelerated_);
263 
264     if (IsGpuContextLost())
265       return gpu::Mailbox();
266 
267     WillDrawInternal(false);
268     return resource_->GetOrCreateGpuMailbox(sync_mode);
269   }
270 
GetBackingTextureTarget() const271   GLenum GetBackingTextureTarget() const override {
272     return resource()->TextureTarget();
273   }
274 
WritePixels(const SkImageInfo & orig_info,const void * pixels,size_t row_bytes,int x,int y)275   bool WritePixels(const SkImageInfo& orig_info,
276                    const void* pixels,
277                    size_t row_bytes,
278                    int x,
279                    int y) override {
280     if (!use_oop_rasterization_) {
281       return CanvasResourceProvider::WritePixels(orig_info, pixels, row_bytes,
282                                                  x, y);
283     }
284 
285     TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::WritePixels");
286     if (IsGpuContextLost())
287       return false;
288 
289     WillDrawInternal(true);
290     RasterInterface()->WritePixels(
291         GetBackingMailboxForOverwrite(kOrderingBarrier), x, y,
292         GetBackingTextureTarget(), row_bytes, orig_info, pixels);
293     return true;
294   }
295 
CreateResource()296   scoped_refptr<CanvasResource> CreateResource() final {
297     TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::CreateResource");
298     if (IsGpuContextLost())
299       return nullptr;
300 
301 #if BUILDFLAG(SKIA_USE_DAWN)
302     if (type_ == kSkiaDawnSharedImage) {
303       return CanvasResourceSkiaDawnSharedImage::Create(
304           Size(), ContextProviderWrapper(), CreateWeakPtr(), FilterQuality(),
305           ColorParams(), IsOriginTopLeft(), shared_image_usage_flags_);
306     }
307 #endif
308 
309     return CanvasResourceRasterSharedImage::Create(
310         Size(), ContextProviderWrapper(), CreateWeakPtr(), FilterQuality(),
311         ColorParams(), IsOriginTopLeft(), is_accelerated_,
312         shared_image_usage_flags_);
313   }
314 
UseOopRasterization()315   bool UseOopRasterization() final { return use_oop_rasterization_; }
316 
NotifyTexParamsModified(const CanvasResource * resource)317   void NotifyTexParamsModified(const CanvasResource* resource) override {
318     if (!is_accelerated_ || use_oop_rasterization_)
319       return;
320 
321     if (resource_.get() == resource) {
322       DCHECK(!current_resource_has_write_access_);
323       // Note that the call below is guarenteed to not issue any GPU work for
324       // the backend texture since we ensure that all skia work on the resource
325       // is issued before releasing write access.
326       surface_->getBackendTexture(SkSurface::kFlushRead_BackendHandleAccess)
327           .glTextureParametersModified();
328     }
329   }
330 
RestoreBackBuffer(const cc::PaintImage & image)331   void RestoreBackBuffer(const cc::PaintImage& image) override {
332     if (!use_oop_rasterization_) {
333       CanvasResourceProvider::RestoreBackBuffer(image);
334       return;
335     }
336 
337     DCHECK_EQ(image.height(), Size().Height());
338     DCHECK_EQ(image.width(), Size().Width());
339 
340     auto sk_image = image.GetSwSkImage();
341     DCHECK(sk_image);
342     SkPixmap map;
343     // We know this SkImage is software backed because it's guaranteed by
344     // PaintImage::GetSwSkImage above
345     sk_image->peekPixels(&map);
346     WritePixels(map.info(), map.addr(), map.rowBytes(), /*x=*/0, /*y=*/0);
347   }
348 
349  protected:
ProduceCanvasResource()350   scoped_refptr<CanvasResource> ProduceCanvasResource() override {
351     TRACE_EVENT0("blink",
352                  "CanvasResourceProviderSharedImage::ProduceCanvasResource");
353     if (IsGpuContextLost())
354       return nullptr;
355 
356     FlushCanvas();
357     // Its important to end read access and ref the resource before the WillDraw
358     // call below. Since it relies on resource ref-count to trigger
359     // copy-on-write and asserts that we only have write access when the
360     // provider has the only ref to the resource, to ensure there are no other
361     // readers.
362     EndWriteAccess();
363     scoped_refptr<CanvasResource> resource = resource_;
364     resource->SetFilterQuality(FilterQuality());
365     if (ContextProviderWrapper()
366             ->ContextProvider()
367             ->GetCapabilities()
368             .disable_2d_canvas_copy_on_write) {
369       // A readback operation may alter the texture parameters, which may affect
370       // the compositor's behavior. Therefore, we must trigger copy-on-write
371       // even though we are not technically writing to the texture, only to its
372       // parameters. This issue is Android-WebView specific: crbug.com/585250.
373       WillDraw();
374     }
375 
376     return resource;
377   }
378 
Snapshot(const ImageOrientation & orientation)379   scoped_refptr<StaticBitmapImage> Snapshot(
380       const ImageOrientation& orientation) override {
381     TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::Snapshot");
382     if (!IsValid())
383       return nullptr;
384 
385     // We don't need to EndWriteAccess here since that's required to make the
386     // rendering results visible on the GpuMemoryBuffer while we return cpu
387     // memory, rendererd to by skia, here.
388     if (!is_accelerated_)
389       return SnapshotInternal(orientation);
390 
391     if (!cached_snapshot_) {
392       FlushCanvas();
393       EndWriteAccess();
394       cached_snapshot_ = resource_->Bitmap();
395     }
396 
397     DCHECK(cached_snapshot_);
398     DCHECK(!current_resource_has_write_access_);
399     return cached_snapshot_;
400   }
401 
WillDrawInternal(bool write_to_local_texture)402   void WillDrawInternal(bool write_to_local_texture) {
403     DCHECK(resource_);
404 
405     if (IsGpuContextLost())
406       return;
407 
408     // Because the cached snapshot is about to be cleared we'll record its
409     // content_id to be used by the FlushForImageListener.
410     // ShouldReplaceTargetBuffer needs this ID in order to let other contexts
411     // know to flush to avoid unnecessary copy-on-writes.
412     PaintImage::ContentId content_id = PaintImage::kInvalidContentId;
413     if (cached_snapshot_) {
414       content_id =
415           cached_snapshot_->PaintImageForCurrentFrame().GetContentIdForFrame(
416               0u);
417     }
418     // Since the resource will be updated, the cached snapshot is no longer
419     // valid. Note that it is important to release this reference here to not
420     // trigger copy-on-write below from the resource ref in the snapshot.
421     // Note that this is valid for single buffered mode also, since while the
422     // resource/mailbox remains the same, the snapshot needs an updated sync
423     // token for these writes.
424     cached_snapshot_.reset();
425 
426     // We don't need to do copy-on-write for the resource here since writes to
427     // the GMB are deferred until it needs to be dispatched to the display
428     // compositor via ProduceCanvasResource.
429     if (is_accelerated_ && ShouldReplaceTargetBuffer(content_id)) {
430       DCHECK(!current_resource_has_write_access_)
431           << "Write access must be released before sharing the resource";
432 
433       auto old_resource = std::move(resource_);
434       auto* old_resource_shared_image =
435           static_cast<CanvasResourceSharedImage*>(old_resource.get());
436       resource_ = NewOrRecycledResource();
437       DCHECK(resource_);
438 
439       auto* raster_interface = RasterInterface();
440       if (raster_interface) {
441         if (!use_oop_rasterization_)
442           TearDownSkSurface();
443 
444         if (mode_ == SkSurface::kRetain_ContentChangeMode) {
445           auto old_mailbox = old_resource_shared_image->GetOrCreateGpuMailbox(
446               kOrderingBarrier);
447           auto mailbox = resource()->GetOrCreateGpuMailbox(kOrderingBarrier);
448 
449           raster_interface->CopySubTexture(
450               old_mailbox, mailbox, GetBackingTextureTarget(), 0, 0, 0, 0,
451               Size().Width(), Size().Height(), false /* unpack_flip_y */,
452               false /* unpack_premultiply_alpha */);
453         }
454 
455         // In non-OOPR mode we need to update the client side SkSurface with the
456         // copied texture. Recreating SkSurface here matches the GPU process
457         // behaviour that will happen in OOPR mode.
458         if (!use_oop_rasterization_)
459           GetSkSurface();
460       } else {
461         EnsureWriteAccess();
462         if (surface_) {
463           // Take read access to the outgoing resource for the skia copy below.
464           if (!old_resource_shared_image->HasReadAccess()) {
465             old_resource_shared_image->BeginReadAccess();
466           }
467           surface_->replaceBackendTexture(CreateGrTextureForResource(),
468                                           GetGrSurfaceOrigin(), mode_);
469           if (!old_resource_shared_image->HasReadAccess()) {
470             old_resource_shared_image->EndReadAccess();
471           }
472         }
473       }
474       UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.ContentChangeMode",
475                             mode_ == SkSurface::kRetain_ContentChangeMode);
476       mode_ = SkSurface::kRetain_ContentChangeMode;
477     }
478 
479     if (write_to_local_texture)
480       EnsureWriteAccess();
481     else
482       EndWriteAccess();
483 
484     resource()->WillDraw();
485   }
486 
WillDraw()487   void WillDraw() override { WillDrawInternal(true); }
488 
RasterRecord(sk_sp<cc::PaintRecord> last_recording)489   void RasterRecord(sk_sp<cc::PaintRecord> last_recording) override {
490     if (!use_oop_rasterization_) {
491       CanvasResourceProvider::RasterRecord(std::move(last_recording));
492       return;
493     }
494     WillDrawInternal(true);
495     gpu::raster::RasterInterface* ri = RasterInterface();
496     SkColor background_color = ColorParams().GetOpacityMode() == kOpaque
497                                    ? SK_ColorBLACK
498                                    : SK_ColorTRANSPARENT;
499 
500     auto list = base::MakeRefCounted<cc::DisplayItemList>(
501         cc::DisplayItemList::kTopLevelDisplayItemList);
502 
503     list->StartPaint();
504     list->push<cc::DrawRecordOp>(std::move(last_recording));
505     list->EndPaintOfUnpaired(gfx::Rect(Size().Width(), Size().Height()));
506     list->Finalize();
507 
508     gfx::Size size(Size().Width(), Size().Height());
509     size_t max_op_size_hint =
510         gpu::raster::RasterInterface::kDefaultMaxOpSizeHint;
511     bool use_lcd = false;
512     gfx::Rect full_raster_rect(Size().Width(), Size().Height());
513     gfx::Rect playback_rect(Size().Width(), Size().Height());
514     gfx::Vector2dF post_translate(0.f, 0.f);
515 
516     ri->BeginRasterCHROMIUM(
517         background_color, 0 /* msaa_sample_count */, use_lcd,
518         ColorParams().GetStorageGfxColorSpace(),
519         resource()->GetOrCreateGpuMailbox(kUnverifiedSyncToken).name);
520 
521     ri->RasterCHROMIUM(list.get(), GetOrCreateCanvasImageProvider(), size,
522                        full_raster_rect, playback_rect, post_translate,
523                        1.f /* post_scale */, false /* requires_clear */,
524                        &max_op_size_hint);
525 
526     ri->EndRasterCHROMIUM();
527   }
528 
ShouldReplaceTargetBuffer(PaintImage::ContentId content_id=PaintImage::kInvalidContentId)529   bool ShouldReplaceTargetBuffer(
530       PaintImage::ContentId content_id = PaintImage::kInvalidContentId) {
531     // If the canvas is single buffered, concurrent read/writes to the resource
532     // are allowed. Note that we ignore the resource lost case as well since
533     // that only indicates that we did not get a sync token for read/write
534     // synchronization which is not a requirement for single buffered canvas.
535     if (IsSingleBuffered())
536       return false;
537 
538     // If the resource was lost, we can not use it for writes again.
539     if (resource()->IsLost())
540       return true;
541 
542     // We have the only ref to the resource which implies there are no active
543     // readers.
544     if (resource_->HasOneRef())
545       return false;
546 
547     // Its possible to have deferred work in skia which uses this resource. Try
548     // flushing once to see if that releases the read refs. We can avoid a copy
549     // by queuing this work before writing to this resource.
550     if (is_accelerated_) {
551       // Another context may have a read reference to this resource. Flush the
552       // deferred queue in that context so that we don't need to copy.
553       GetFlushForImageListener()->NotifyFlushForImage(content_id);
554 
555       if (!use_oop_rasterization_)
556         surface_->flushAndSubmit();
557     }
558 
559     return !resource_->HasOneRef();
560   }
561 
CreateSkSurface() const562   sk_sp<SkSurface> CreateSkSurface() const override {
563     TRACE_EVENT0("blink", "CanvasResourceProviderSharedImage::CreateSkSurface");
564     if (IsGpuContextLost() || !resource_)
565       return nullptr;
566 
567     if (is_accelerated_) {
568       return SkSurface::MakeFromBackendTexture(
569           GetGrContext(), CreateGrTextureForResource(), GetGrSurfaceOrigin(),
570           0 /* msaa_sample_count */, ColorParams().GetSkColorType(),
571           ColorParams().GetSkColorSpace(), ColorParams().GetSkSurfaceProps());
572     }
573 
574     // For software raster path, we render into cpu memory managed internally
575     // by SkSurface and copy the rendered results to the GMB before dispatching
576     // it to the display compositor.
577     return SkSurface::MakeRaster(resource_->CreateSkImageInfo(),
578                                  ColorParams().GetSkSurfaceProps());
579   }
580 
CreateGrTextureForResource() const581   GrBackendTexture CreateGrTextureForResource() const {
582     DCHECK(is_accelerated_);
583 
584     return resource()->CreateGrTexture();
585   }
586 
FlushGrContext()587   void FlushGrContext() {
588     DCHECK(is_accelerated_);
589 
590     // The resource may have been imported and used in skia. Make sure any
591     // operations using this resource are flushed to the underlying context.
592     // Note that its not sufficient to flush the SkSurface here since it will
593     // only perform a GrContext flush if that SkSurface has any pending ops. And
594     // this resource may be written to or read from skia without using the
595     // SkSurface here.
596     if (IsGpuContextLost())
597       return;
598     GetGrContext()->flushAndSubmit();
599   }
600 
EnsureWriteAccess()601   void EnsureWriteAccess() {
602     DCHECK(resource_);
603     // In software mode, we don't need write access to the resource during
604     // drawing since it is executed on cpu memory managed by skia. We ensure
605     // exclusive access to the resource when the results are copied onto the
606     // GMB in EndWriteAccess.
607     DCHECK(resource_->HasOneRef() || IsSingleBuffered() || !is_accelerated_)
608         << "Write access requires exclusive access to the resource";
609     DCHECK(!resource()->is_cross_thread())
610         << "Write access is only allowed on the owning thread";
611 
612     if (current_resource_has_write_access_ || IsGpuContextLost() ||
613         use_oop_rasterization_)
614       return;
615 
616     if (is_accelerated_) {
617       resource()->BeginWriteAccess();
618     }
619 
620     // For the non-accelerated path, we don't need a texture for writes since
621     // its on the CPU, but we set this bit to know whether the GMB needs to be
622     // updated.
623     current_resource_has_write_access_ = true;
624   }
625 
EndWriteAccess()626   void EndWriteAccess() {
627     DCHECK(!resource()->is_cross_thread());
628 
629     if (!current_resource_has_write_access_ || IsGpuContextLost())
630       return;
631 
632     DCHECK(!use_oop_rasterization_);
633 
634     if (is_accelerated_) {
635       // We reset |mode_| here since the draw commands which overwrite the
636       // complete canvas must have been flushed at this point without triggering
637       // copy-on-write.
638       mode_ = SkSurface::kRetain_ContentChangeMode;
639       // Issue any skia work using this resource before releasing write access.
640       FlushGrContext();
641       resource()->EndWriteAccess();
642     } else {
643       if (ShouldReplaceTargetBuffer())
644         resource_ = NewOrRecycledResource();
645       resource()->CopyRenderingResultsToGpuMemoryBuffer(
646           surface_->makeImageSnapshot());
647     }
648 
649     current_resource_has_write_access_ = false;
650   }
651 
resource()652   CanvasResourceSharedImage* resource() {
653     return static_cast<CanvasResourceSharedImage*>(resource_.get());
654   }
resource() const655   const CanvasResourceSharedImage* resource() const {
656     return static_cast<const CanvasResourceSharedImage*>(resource_.get());
657   }
658 
659   const bool is_accelerated_;
660   const uint32_t shared_image_usage_flags_;
661   bool current_resource_has_write_access_ = false;
662   const bool use_oop_rasterization_;
663   scoped_refptr<CanvasResource> resource_;
664   scoped_refptr<StaticBitmapImage> cached_snapshot_;
665 };
666 
667 // This class does nothing except answering to ProduceCanvasResource() by piping
668 // it to NewOrRecycledResource().  This ResourceProvider is meant to be used
669 // with an imported external CanvasResource, and all drawing and lifetime logic
670 // must be kept at a higher level.
671 class CanvasResourceProviderPassThrough final : public CanvasResourceProvider {
672  public:
CanvasResourceProviderPassThrough(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,bool is_origin_top_left)673   CanvasResourceProviderPassThrough(
674       const IntSize& size,
675       SkFilterQuality filter_quality,
676       const CanvasColorParams& color_params,
677       base::WeakPtr<WebGraphicsContext3DProviderWrapper>
678           context_provider_wrapper,
679       base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
680       bool is_origin_top_left)
681       : CanvasResourceProvider(kPassThrough,
682                                size,
683                                filter_quality,
684                                color_params,
685                                is_origin_top_left,
686                                std::move(context_provider_wrapper),
687                                std::move(resource_dispatcher)) {}
688 
689   ~CanvasResourceProviderPassThrough() override = default;
IsValid() const690   bool IsValid() const final { return true; }
IsAccelerated() const691   bool IsAccelerated() const final { return true; }
SupportsDirectCompositing() const692   bool SupportsDirectCompositing() const override { return true; }
SupportsSingleBuffering() const693   bool SupportsSingleBuffering() const override { return true; }
694 
695  private:
CreateResource()696   scoped_refptr<CanvasResource> CreateResource() final {
697     // This class has no CanvasResource to provide: this must be imported via
698     // ImportResource() and kept in the parent class.
699     NOTREACHED();
700     return nullptr;
701   }
702 
ProduceCanvasResource()703   scoped_refptr<CanvasResource> ProduceCanvasResource() final {
704     return NewOrRecycledResource();
705   }
706 
CreateSkSurface() const707   sk_sp<SkSurface> CreateSkSurface() const override {
708     NOTREACHED();
709     return nullptr;
710   }
711 
Snapshot(const ImageOrientation &)712   scoped_refptr<StaticBitmapImage> Snapshot(const ImageOrientation&) override {
713     auto resource = GetImportedResource();
714     if (IsGpuContextLost() || !resource)
715       return nullptr;
716     return resource->Bitmap();
717   }
718 };
719 
720 // * Renders to back buffer of a shared image swap chain.
721 // * Presents swap chain and exports front buffer mailbox to compositor to
722 //   support low latency mode.
723 // * Layers are overlay candidates.
724 class CanvasResourceProviderSwapChain final : public CanvasResourceProvider {
725  public:
CanvasResourceProviderSwapChain(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)726   CanvasResourceProviderSwapChain(
727       const IntSize& size,
728       SkFilterQuality filter_quality,
729       const CanvasColorParams& color_params,
730       base::WeakPtr<WebGraphicsContext3DProviderWrapper>
731           context_provider_wrapper,
732       base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
733       : CanvasResourceProvider(kSwapChain,
734                                size,
735                                filter_quality,
736                                color_params,
737                                true /*is_origin_top_left*/,
738                                std::move(context_provider_wrapper),
739                                std::move(resource_dispatcher)) {
740     resource_ = CanvasResourceSwapChain::Create(
741         Size(), ColorParams(), ContextProviderWrapper(), CreateWeakPtr(),
742         FilterQuality());
743     // CanvasResourceProviderSwapChain can only operate in a single buffered
744     // mode so enable it as soon as possible.
745     TryEnableSingleBuffering();
746     DCHECK(IsSingleBuffered());
747   }
748   ~CanvasResourceProviderSwapChain() override = default;
749 
IsValid() const750   bool IsValid() const final { return GetSkSurface() && !IsGpuContextLost(); }
IsAccelerated() const751   bool IsAccelerated() const final { return true; }
SupportsDirectCompositing() const752   bool SupportsDirectCompositing() const override { return true; }
SupportsSingleBuffering() const753   bool SupportsSingleBuffering() const override { return true; }
754 
755  private:
WillDraw()756   void WillDraw() override {
757     needs_present_ = true;
758     needs_flush_ = true;
759   }
760 
CreateResource()761   scoped_refptr<CanvasResource> CreateResource() final {
762     TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::CreateResource");
763     return resource_;
764   }
765 
ProduceCanvasResource()766   scoped_refptr<CanvasResource> ProduceCanvasResource() override {
767     DCHECK(IsSingleBuffered());
768     TRACE_EVENT0("blink",
769                  "CanvasResourceProviderSwapChain::ProduceCanvasResource");
770     if (!IsValid())
771       return nullptr;
772 
773     FlushIfNeeded();
774 
775     if (needs_present_) {
776       resource_->PresentSwapChain();
777       needs_present_ = false;
778     }
779     return resource_;
780   }
781 
Snapshot(const ImageOrientation &)782   scoped_refptr<StaticBitmapImage> Snapshot(const ImageOrientation&) override {
783     TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::Snapshot");
784 
785     if (!IsValid())
786       return nullptr;
787 
788     FlushIfNeeded();
789 
790     return resource_->Bitmap();
791   }
792 
CreateSkSurface() const793   sk_sp<SkSurface> CreateSkSurface() const override {
794     TRACE_EVENT0("blink", "CanvasResourceProviderSwapChain::CreateSkSurface");
795     if (IsGpuContextLost() || !resource_)
796       return nullptr;
797 
798     GrGLTextureInfo texture_info = {};
799     texture_info.fID = resource_->GetBackBufferTextureId();
800     texture_info.fTarget = resource_->TextureTarget();
801     texture_info.fFormat = ColorParams().GLSizedInternalFormat();
802 
803     auto backend_texture = GrBackendTexture(Size().Width(), Size().Height(),
804                                             GrMipMapped::kNo, texture_info);
805 
806     return SkSurface::MakeFromBackendTexture(
807         GetGrContext(), backend_texture, kTopLeft_GrSurfaceOrigin,
808         0 /* msaa_sample_count */, ColorParams().GetSkColorType(),
809         ColorParams().GetSkColorSpace(), ColorParams().GetSkSurfaceProps());
810   }
811 
FlushIfNeeded()812   void FlushIfNeeded() {
813     if (needs_flush_) {
814       // This only flushes recorded draw ops.
815       FlushCanvas();
816       // Call flushAndSubmit() explicitly so that any non-draw-op rendering by
817       // Skia is flushed to GL.  This is needed specifically for WritePixels().
818       GetGrContext()->flushAndSubmit();
819       needs_flush_ = false;
820     }
821   }
822 
823   bool needs_present_ = false;
824   bool needs_flush_ = false;
825   scoped_refptr<CanvasResourceSwapChain> resource_;
826 };
827 
828 namespace {
829 
830 enum class CanvasResourceType {
831   kDirect3DPassThrough,
832   kDirect2DSwapChain,
833   kSharedImage,
834   kSharedBitmap,
835   kBitmap,
836   kSkiaDawnSharedImage,
837 };
838 
839 }  // unnamed namespace
840 
841 std::unique_ptr<CanvasResourceProvider>
CreateBitmapProvider(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,ShouldInitialize should_initialize)842 CanvasResourceProvider::CreateBitmapProvider(
843     const IntSize& size,
844     SkFilterQuality filter_quality,
845     const CanvasColorParams& color_params,
846     ShouldInitialize should_initialize) {
847   auto provider = std::make_unique<CanvasResourceProviderBitmap>(
848       size, filter_quality, color_params, nullptr /*resource_dispatcher*/);
849   if (provider->IsValid()) {
850     if (should_initialize ==
851         CanvasResourceProvider::ShouldInitialize::kCallClear)
852       provider->Clear();
853     return provider;
854   }
855 
856   return nullptr;
857 }
858 
859 std::unique_ptr<CanvasResourceProvider>
CreateSharedBitmapProvider(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,ShouldInitialize should_initialize,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)860 CanvasResourceProvider::CreateSharedBitmapProvider(
861     const IntSize& size,
862     SkFilterQuality filter_quality,
863     const CanvasColorParams& color_params,
864     ShouldInitialize should_initialize,
865     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher) {
866   // SharedBitmapProvider has to have a valid resource_dispatecher to be able to
867   // be created.
868   if (!resource_dispatcher)
869     return nullptr;
870 
871   auto provider = std::make_unique<CanvasResourceProviderSharedBitmap>(
872       size, filter_quality, color_params, std::move(resource_dispatcher));
873   if (provider->IsValid()) {
874     if (should_initialize ==
875         CanvasResourceProvider::ShouldInitialize::kCallClear)
876       provider->Clear();
877     return provider;
878   }
879 
880   return nullptr;
881 }
882 
883 std::unique_ptr<CanvasResourceProvider>
CreateSharedImageProvider(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,ShouldInitialize should_initialize,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,RasterMode raster_mode,bool is_origin_top_left,uint32_t shared_image_usage_flags)884 CanvasResourceProvider::CreateSharedImageProvider(
885     const IntSize& size,
886     SkFilterQuality filter_quality,
887     const CanvasColorParams& color_params,
888     ShouldInitialize should_initialize,
889     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
890     RasterMode raster_mode,
891     bool is_origin_top_left,
892     uint32_t shared_image_usage_flags) {
893   // IsGpuCompositingEnabled can re-create the context if it has been lost, do
894   // this up front so that we can fail early and not expose ourselves to
895   // use after free bugs (crbug.com/1126424)
896   const bool is_gpu_compositing_enabled =
897       SharedGpuContext::IsGpuCompositingEnabled();
898 
899   // If the context is lost we don't want to re-create it here, the resulting
900   // resource provider would be invalid anyway
901   if (!context_provider_wrapper ||
902       context_provider_wrapper->ContextProvider()->IsContextLost())
903     return nullptr;
904 
905   const auto& capabilities =
906       context_provider_wrapper->ContextProvider()->GetCapabilities();
907   bool skia_use_dawn =
908       raster_mode == RasterMode::kGPU &&
909       base::FeatureList::IsEnabled(blink::features::kDawn2dCanvas);
910   // TODO(senorblanco): once Dawn reports maximum texture size, Dawn Canvas
911   // should respect it.  http://crbug.com/1082760
912   if (!skia_use_dawn && (size.Width() > capabilities.max_texture_size ||
913                          size.Height() > capabilities.max_texture_size)) {
914     return nullptr;
915   }
916 
917   const bool is_gpu_memory_buffer_image_allowed =
918       is_gpu_compositing_enabled &&
919       IsGMBAllowed(size, color_params, capabilities) &&
920       Platform::Current()->GetGpuMemoryBufferManager();
921 
922   if (raster_mode == RasterMode::kCPU && !is_gpu_memory_buffer_image_allowed)
923     return nullptr;
924 
925   // If we cannot use overlay, we have to remove the scanout flag and the
926   // concurrent read write flag.
927   if (!is_gpu_memory_buffer_image_allowed ||
928       !capabilities.texture_storage_image) {
929     shared_image_usage_flags &= ~gpu::SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE;
930     shared_image_usage_flags &= ~gpu::SHARED_IMAGE_USAGE_SCANOUT;
931   }
932 
933   auto provider = std::make_unique<CanvasResourceProviderSharedImage>(
934       size, filter_quality, color_params, context_provider_wrapper,
935       is_origin_top_left, raster_mode == RasterMode::kGPU, skia_use_dawn,
936       shared_image_usage_flags);
937   if (provider->IsValid()) {
938     if (should_initialize ==
939         CanvasResourceProvider::ShouldInitialize::kCallClear)
940       provider->Clear();
941     return provider;
942   }
943 
944   return nullptr;
945 }
946 
947 std::unique_ptr<CanvasResourceProvider>
CreatePassThroughProvider(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,bool is_origin_top_left)948 CanvasResourceProvider::CreatePassThroughProvider(
949     const IntSize& size,
950     SkFilterQuality filter_quality,
951     const CanvasColorParams& color_params,
952     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
953     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
954     bool is_origin_top_left) {
955   // SharedGpuContext::IsGpuCompositingEnabled can potentially replace the
956   // context_provider_wrapper, so it's important to call that first as it can
957   // invalidate the weak pointer.
958   if (!SharedGpuContext::IsGpuCompositingEnabled() || !context_provider_wrapper)
959     return nullptr;
960 
961   const auto& capabilities =
962       context_provider_wrapper->ContextProvider()->GetCapabilities();
963   if (size.Width() > capabilities.max_texture_size ||
964       size.Height() > capabilities.max_texture_size) {
965     return nullptr;
966   }
967 
968   // Either swap_chain or gpu memory buffer should be enabled for this be used
969   if (!capabilities.shared_image_swap_chain &&
970       (!IsGMBAllowed(size, color_params, capabilities) ||
971        !Platform::Current()->GetGpuMemoryBufferManager()))
972     return nullptr;
973 
974   auto provider = std::make_unique<CanvasResourceProviderPassThrough>(
975       size, filter_quality, color_params, context_provider_wrapper,
976       resource_dispatcher, is_origin_top_left);
977   if (provider->IsValid()) {
978     // All the other type of resources are doing a clear here. As a
979     // CanvasResourceProvider of type PassThrough is used to delegate the
980     // internal parts of the resource and provider to other classes, we should
981     // not attempt to do a clear here. clear is not needed here.
982     return provider;
983   }
984 
985   return nullptr;
986 }
987 
988 std::unique_ptr<CanvasResourceProvider>
CreateSwapChainProvider(const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,ShouldInitialize should_initialize,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,bool is_origin_top_left)989 CanvasResourceProvider::CreateSwapChainProvider(
990     const IntSize& size,
991     SkFilterQuality filter_quality,
992     const CanvasColorParams& color_params,
993     ShouldInitialize should_initialize,
994     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
995     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
996     bool is_origin_top_left) {
997   DCHECK(is_origin_top_left);
998   // SharedGpuContext::IsGpuCompositingEnabled can potentially replace the
999   // context_provider_wrapper, so it's important to call that first as it can
1000   // invalidate the weak pointer.
1001   if (!SharedGpuContext::IsGpuCompositingEnabled() || !context_provider_wrapper)
1002     return nullptr;
1003 
1004   const auto& capabilities =
1005       context_provider_wrapper->ContextProvider()->GetCapabilities();
1006   if (size.Width() > capabilities.max_texture_size ||
1007       size.Height() > capabilities.max_texture_size ||
1008       !capabilities.shared_image_swap_chain) {
1009     return nullptr;
1010   }
1011 
1012   auto provider = std::make_unique<CanvasResourceProviderSwapChain>(
1013       size, filter_quality, color_params, context_provider_wrapper,
1014       resource_dispatcher);
1015   if (provider->IsValid()) {
1016     if (should_initialize ==
1017         CanvasResourceProvider::ShouldInitialize::kCallClear)
1018       provider->Clear();
1019     return provider;
1020   }
1021 
1022   return nullptr;
1023 }
1024 
CanvasImageProvider(cc::ImageDecodeCache * cache_n32,cc::ImageDecodeCache * cache_f16,const gfx::ColorSpace & target_color_space,SkColorType canvas_color_type,cc::PlaybackImageProvider::RasterMode raster_mode)1025 CanvasResourceProvider::CanvasImageProvider::CanvasImageProvider(
1026     cc::ImageDecodeCache* cache_n32,
1027     cc::ImageDecodeCache* cache_f16,
1028     const gfx::ColorSpace& target_color_space,
1029     SkColorType canvas_color_type,
1030     cc::PlaybackImageProvider::RasterMode raster_mode)
1031     : raster_mode_(raster_mode) {
1032   base::Optional<cc::PlaybackImageProvider::Settings> settings =
1033       cc::PlaybackImageProvider::Settings();
1034   settings->raster_mode = raster_mode_;
1035 
1036   playback_image_provider_n32_.emplace(cache_n32, target_color_space,
1037                                        std::move(settings));
1038   // If the image provider may require to decode to half float instead of
1039   // uint8, create a f16 PlaybackImageProvider with the passed cache.
1040   if (canvas_color_type == kRGBA_F16_SkColorType) {
1041     DCHECK(cache_f16);
1042     settings = cc::PlaybackImageProvider::Settings();
1043     settings->raster_mode = raster_mode_;
1044     playback_image_provider_f16_.emplace(cache_f16, target_color_space,
1045                                          std::move(settings));
1046   }
1047 }
1048 
1049 cc::ImageProvider::ScopedResult
GetRasterContent(const cc::DrawImage & draw_image)1050 CanvasResourceProvider::CanvasImageProvider::GetRasterContent(
1051     const cc::DrawImage& draw_image) {
1052   // TODO(xidachen): Ensure this function works for paint worklet generated
1053   // images.
1054   // If we like to decode high bit depth image source to half float backed
1055   // image, we need to sniff the image bit depth here to avoid double decoding.
1056   ImageProvider::ScopedResult scoped_decoded_image;
1057   if (playback_image_provider_f16_ &&
1058       draw_image.paint_image().is_high_bit_depth()) {
1059     DCHECK(playback_image_provider_f16_);
1060     return playback_image_provider_f16_->GetRasterContent(draw_image);
1061   }
1062   return playback_image_provider_n32_->GetRasterContent(draw_image);
1063 }
1064 
CanvasResourceProvider(const ResourceProviderType & type,const IntSize & size,SkFilterQuality filter_quality,const CanvasColorParams & color_params,bool is_origin_top_left,base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)1065 CanvasResourceProvider::CanvasResourceProvider(
1066     const ResourceProviderType& type,
1067     const IntSize& size,
1068     SkFilterQuality filter_quality,
1069     const CanvasColorParams& color_params,
1070     bool is_origin_top_left,
1071     base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper,
1072     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher)
1073     : type_(type),
1074       context_provider_wrapper_(std::move(context_provider_wrapper)),
1075       resource_dispatcher_(resource_dispatcher),
1076       size_(size),
1077       filter_quality_(filter_quality),
1078       color_params_(color_params),
1079       is_origin_top_left_(is_origin_top_left),
1080       snapshot_paint_image_id_(cc::PaintImage::GetNextId()),
1081       identifiability_paint_op_digest_(size_) {
1082   if (context_provider_wrapper_)
1083     context_provider_wrapper_->AddObserver(this);
1084   CanvasMemoryDumpProvider::Instance()->RegisterClient(this);
1085 }
1086 
~CanvasResourceProvider()1087 CanvasResourceProvider::~CanvasResourceProvider() {
1088   UMA_HISTOGRAM_EXACT_LINEAR("Blink.Canvas.MaximumInflightResources",
1089                              max_inflight_resources_, 20);
1090   if (context_provider_wrapper_)
1091     context_provider_wrapper_->RemoveObserver(this);
1092   CanvasMemoryDumpProvider::Instance()->UnregisterClient(this);
1093 }
1094 
GetSkSurface() const1095 SkSurface* CanvasResourceProvider::GetSkSurface() const {
1096   if (!surface_)
1097     surface_ = CreateSkSurface();
1098   return surface_.get();
1099 }
1100 
EnsureSkiaCanvas()1101 void CanvasResourceProvider::EnsureSkiaCanvas() {
1102   WillDraw();
1103 
1104   if (skia_canvas_)
1105     return;
1106 
1107   cc::SkiaPaintCanvas::ContextFlushes context_flushes;
1108   if (IsAccelerated() && ContextProviderWrapper() &&
1109       !ContextProviderWrapper()
1110            ->ContextProvider()
1111            ->GetGpuFeatureInfo()
1112            .IsWorkaroundEnabled(gpu::DISABLE_2D_CANVAS_AUTO_FLUSH)) {
1113     context_flushes.enable = true;
1114     context_flushes.max_draws_before_flush = kMaxDrawsBeforeContextFlush;
1115   }
1116   skia_canvas_ = std::make_unique<cc::SkiaPaintCanvas>(
1117       GetSkSurface()->getCanvas(), GetOrCreateCanvasImageProvider(),
1118       context_flushes);
1119 }
1120 
1121 CanvasResourceProvider::CanvasImageProvider*
GetOrCreateCanvasImageProvider()1122 CanvasResourceProvider::GetOrCreateCanvasImageProvider() {
1123   if (!canvas_image_provider_) {
1124     // Create an ImageDecodeCache for half float images only if the canvas is
1125     // using half float back storage.
1126     cc::ImageDecodeCache* cache_f16 = nullptr;
1127     if (ColorParams().GetSkColorType() == kRGBA_F16_SkColorType)
1128       cache_f16 = ImageDecodeCacheF16();
1129 
1130     auto raster_mode = cc::PlaybackImageProvider::RasterMode::kSoftware;
1131     if (UseHardwareDecodeCache()) {
1132       raster_mode = UseOopRasterization()
1133                         ? cc::PlaybackImageProvider::RasterMode::kOop
1134                         : cc::PlaybackImageProvider::RasterMode::kGpu;
1135     }
1136     canvas_image_provider_ = std::make_unique<CanvasImageProvider>(
1137         ImageDecodeCacheRGBA8(), cache_f16, gfx::ColorSpace::CreateSRGB(),
1138         color_params_.GetSkColorType(), raster_mode);
1139   }
1140   return canvas_image_provider_.get();
1141 }
1142 
Canvas()1143 cc::PaintCanvas* CanvasResourceProvider::Canvas() {
1144   if (!recorder_) {
1145     // A raw pointer is safe here because the callback is only used by the
1146     // |recorder_|.
1147     recorder_ = std::make_unique<MemoryManagedPaintRecorder>(WTF::BindRepeating(
1148         &CanvasResourceProvider::SetNeedsFlush, WTF::Unretained(this)));
1149 
1150     return recorder_->beginRecording(Size().Width(), Size().Height());
1151   }
1152   return recorder_->getRecordingCanvas();
1153 }
1154 
OnContextDestroyed()1155 void CanvasResourceProvider::OnContextDestroyed() {
1156   if (skia_canvas_)
1157     skia_canvas_->reset_image_provider();
1158   canvas_image_provider_.reset();
1159 }
1160 
OnFlushForImage(PaintImage::ContentId content_id)1161 void CanvasResourceProvider::OnFlushForImage(PaintImage::ContentId content_id) {
1162   if (Canvas()) {
1163     MemoryManagedPaintCanvas* canvas =
1164         static_cast<MemoryManagedPaintCanvas*>(Canvas());
1165     if (canvas->IsCachingImage(content_id))
1166       this->FlushCanvas();
1167   }
1168 }
1169 
SnapshotInternal(const ImageOrientation & orientation)1170 scoped_refptr<StaticBitmapImage> CanvasResourceProvider::SnapshotInternal(
1171     const ImageOrientation& orientation) {
1172   if (!IsValid())
1173     return nullptr;
1174 
1175   auto paint_image = MakeImageSnapshot();
1176   DCHECK(!paint_image.IsTextureBacked());
1177   return UnacceleratedStaticBitmapImage::Create(std::move(paint_image),
1178                                                 orientation);
1179 }
1180 
MakeImageSnapshot()1181 cc::PaintImage CanvasResourceProvider::MakeImageSnapshot() {
1182   FlushCanvas();
1183   auto sk_image = GetSkSurface()->makeImageSnapshot();
1184   if (!sk_image)
1185     return cc::PaintImage();
1186 
1187   auto last_snapshot_sk_image_id = snapshot_sk_image_id_;
1188   snapshot_sk_image_id_ = sk_image->uniqueID();
1189 
1190   // Ensure that a new PaintImage::ContentId is used only when the underlying
1191   // SkImage changes. This is necessary to ensure that the same image results
1192   // in a cache hit in cc's ImageDecodeCache.
1193   if (snapshot_paint_image_content_id_ == PaintImage::kInvalidContentId ||
1194       last_snapshot_sk_image_id != snapshot_sk_image_id_) {
1195     snapshot_paint_image_content_id_ = PaintImage::GetNextContentId();
1196   }
1197 
1198   return PaintImageBuilder::WithDefault()
1199       .set_id(snapshot_paint_image_id_)
1200       .set_image(std::move(sk_image), snapshot_paint_image_content_id_)
1201       .TakePaintImage();
1202 }
1203 
ContextGL() const1204 gpu::gles2::GLES2Interface* CanvasResourceProvider::ContextGL() const {
1205   if (!context_provider_wrapper_)
1206     return nullptr;
1207   return context_provider_wrapper_->ContextProvider()->ContextGL();
1208 }
1209 
RasterInterface() const1210 gpu::raster::RasterInterface* CanvasResourceProvider::RasterInterface() const {
1211   if (!context_provider_wrapper_)
1212     return nullptr;
1213   return context_provider_wrapper_->ContextProvider()->RasterInterface();
1214 }
1215 
GetGrContext() const1216 GrDirectContext* CanvasResourceProvider::GetGrContext() const {
1217   if (!context_provider_wrapper_)
1218     return nullptr;
1219   return context_provider_wrapper_->ContextProvider()->GetGrContext();
1220 }
1221 
FlushCanvas()1222 sk_sp<cc::PaintRecord> CanvasResourceProvider::FlushCanvas() {
1223   if (!HasRecordedDrawOps())
1224     return nullptr;
1225   // Get PaintOp count before finishRecordingAsPicture() adds more, as these
1226   // additional ops don't correspond to canvas context operations.
1227   const size_t initial_paint_ops = recorder_->num_paint_ops();
1228   sk_sp<cc::PaintRecord> last_recording = recorder_->finishRecordingAsPicture();
1229   RasterRecord(last_recording);
1230   needs_flush_ = false;
1231   cc::PaintCanvas* canvas =
1232       recorder_->beginRecording(Size().Width(), Size().Height());
1233   if (restore_clip_stack_callback_)
1234     restore_clip_stack_callback_.Run(canvas);
1235   identifiability_paint_op_digest_.MaybeUpdateDigest(last_recording,
1236                                                      initial_paint_ops);
1237   // restore_clip_stack_callback_ also adds PaintOps -- these need to be skipped
1238   // during identifiability digest calculation.
1239   identifiability_paint_op_digest_.SetPrefixSkipCount(
1240       recorder_->num_paint_ops());
1241   return last_recording;
1242 }
1243 
RasterRecord(sk_sp<cc::PaintRecord> last_recording)1244 void CanvasResourceProvider::RasterRecord(
1245     sk_sp<cc::PaintRecord> last_recording) {
1246   EnsureSkiaCanvas();
1247   skia_canvas_->drawPicture(std::move(last_recording));
1248   GetSkSurface()->flushAndSubmit();
1249 }
1250 
IsGpuContextLost() const1251 bool CanvasResourceProvider::IsGpuContextLost() const {
1252   if (type_ == kSkiaDawnSharedImage) {
1253     return false;
1254   }
1255   auto* raster_interface = RasterInterface();
1256   return !raster_interface ||
1257          raster_interface->GetGraphicsResetStatusKHR() != GL_NO_ERROR;
1258 }
1259 
WritePixels(const SkImageInfo & orig_info,const void * pixels,size_t row_bytes,int x,int y)1260 bool CanvasResourceProvider::WritePixels(const SkImageInfo& orig_info,
1261                                          const void* pixels,
1262                                          size_t row_bytes,
1263                                          int x,
1264                                          int y) {
1265   TRACE_EVENT0("blink", "CanvasResourceProvider::WritePixels");
1266 
1267   DCHECK(IsValid());
1268   DCHECK(!HasRecordedDrawOps());
1269 
1270   EnsureSkiaCanvas();
1271 
1272   return GetSkSurface()->getCanvas()->writePixels(orig_info, pixels, row_bytes,
1273                                                   x, y);
1274 }
1275 
Clear()1276 void CanvasResourceProvider::Clear() {
1277   // Clear the background transparent or opaque, as required. This should only
1278   // be called when a new resource provider is created to ensure that we're
1279   // not leaking data or displaying bad pixels (in the case of kOpaque
1280   // canvases). Instead of adding these commands to our deferred queue, we'll
1281   // send them directly through to Skia so that they're not replayed for
1282   // printing operations. See crbug.com/1003114
1283   DCHECK(IsValid());
1284   if (color_params_.GetOpacityMode() == kOpaque)
1285     Canvas()->clear(SK_ColorBLACK);
1286   else
1287     Canvas()->clear(SK_ColorTRANSPARENT);
1288 
1289   FlushCanvas();
1290 }
1291 
ContentUniqueID() const1292 uint32_t CanvasResourceProvider::ContentUniqueID() const {
1293   return GetSkSurface()->generationID();
1294 }
1295 
CreateResource()1296 scoped_refptr<CanvasResource> CanvasResourceProvider::CreateResource() {
1297   // Needs to be implemented in subclasses that use resource recycling.
1298   NOTREACHED();
1299   return nullptr;
1300 }
1301 
ImageDecodeCacheRGBA8()1302 cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCacheRGBA8() {
1303   if (UseHardwareDecodeCache()) {
1304     return context_provider_wrapper_->ContextProvider()->ImageDecodeCache(
1305         kN32_SkColorType);
1306   }
1307 
1308   return &Image::SharedCCDecodeCache(kN32_SkColorType);
1309 }
1310 
ImageDecodeCacheF16()1311 cc::ImageDecodeCache* CanvasResourceProvider::ImageDecodeCacheF16() {
1312   if (UseHardwareDecodeCache()) {
1313     return context_provider_wrapper_->ContextProvider()->ImageDecodeCache(
1314         kRGBA_F16_SkColorType);
1315   }
1316   return &Image::SharedCCDecodeCache(kRGBA_F16_SkColorType);
1317 }
1318 
RecycleResource(scoped_refptr<CanvasResource> resource)1319 void CanvasResourceProvider::RecycleResource(
1320     scoped_refptr<CanvasResource> resource) {
1321   // We don't want to keep an arbitrary large number of canvases.
1322   if (canvas_resources_.size() >
1323       static_cast<unsigned int>(kMaxRecycledCanvasResources))
1324     return;
1325 
1326   // Need to check HasOneRef() because if there are outstanding references to
1327   // the resource, it cannot be safely recycled.
1328   if (resource->HasOneRef() && resource_recycling_enabled_ &&
1329       !is_single_buffered_) {
1330     canvas_resources_.push_back(std::move(resource));
1331   }
1332 }
1333 
SetResourceRecyclingEnabled(bool value)1334 void CanvasResourceProvider::SetResourceRecyclingEnabled(bool value) {
1335   resource_recycling_enabled_ = value;
1336   if (!resource_recycling_enabled_)
1337     ClearRecycledResources();
1338 }
1339 
ClearRecycledResources()1340 void CanvasResourceProvider::ClearRecycledResources() {
1341   canvas_resources_.clear();
1342 }
1343 
OnDestroyResource()1344 void CanvasResourceProvider::OnDestroyResource() {
1345   --num_inflight_resources_;
1346 }
1347 
1348 const IdentifiabilityPaintOpDigest&
GetIdentifiablityPaintOpDigest()1349 CanvasResourceProvider::GetIdentifiablityPaintOpDigest() {
1350   FlushCanvas();
1351   return identifiability_paint_op_digest_;
1352 }
1353 
NewOrRecycledResource()1354 scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
1355   if (canvas_resources_.IsEmpty()) {
1356     canvas_resources_.push_back(CreateResource());
1357     ++num_inflight_resources_;
1358     if (num_inflight_resources_ > max_inflight_resources_)
1359       max_inflight_resources_ = num_inflight_resources_;
1360   }
1361 
1362   if (IsSingleBuffered()) {
1363     DCHECK_EQ(canvas_resources_.size(), 1u);
1364     return canvas_resources_.back();
1365   }
1366 
1367   scoped_refptr<CanvasResource> resource = std::move(canvas_resources_.back());
1368   canvas_resources_.pop_back();
1369   return resource;
1370 }
1371 
TryEnableSingleBuffering()1372 void CanvasResourceProvider::TryEnableSingleBuffering() {
1373   if (IsSingleBuffered() || !SupportsSingleBuffering())
1374     return;
1375   is_single_buffered_ = true;
1376   ClearRecycledResources();
1377 }
1378 
ImportResource(scoped_refptr<CanvasResource> resource)1379 bool CanvasResourceProvider::ImportResource(
1380     scoped_refptr<CanvasResource> resource) {
1381   if (!IsSingleBuffered() || !SupportsSingleBuffering())
1382     return false;
1383   canvas_resources_.clear();
1384   canvas_resources_.push_back(std::move(resource));
1385   return true;
1386 }
1387 
GetImportedResource() const1388 scoped_refptr<CanvasResource> CanvasResourceProvider::GetImportedResource()
1389     const {
1390   if (!IsSingleBuffered() || !SupportsSingleBuffering())
1391     return nullptr;
1392   DCHECK_LE(canvas_resources_.size(), 1u);
1393   if (canvas_resources_.IsEmpty())
1394     return nullptr;
1395   return canvas_resources_.back();
1396 }
1397 
SkipQueuedDrawCommands()1398 void CanvasResourceProvider::SkipQueuedDrawCommands() {
1399   // Note that this function only gets called when canvas needs a full repaint,
1400   // so always update the |mode_| to discard the old copy of canvas content.
1401   mode_ = SkSurface::kDiscard_ContentChangeMode;
1402 
1403   if (!HasRecordedDrawOps())
1404     return;
1405   recorder_->finishRecordingAsPicture();
1406   cc::PaintCanvas* canvas =
1407       recorder_->beginRecording(Size().Width(), Size().Height());
1408   if (restore_clip_stack_callback_)
1409     restore_clip_stack_callback_.Run(canvas);
1410 }
1411 
SetRestoreClipStackCallback(RestoreMatrixClipStackCb callback)1412 void CanvasResourceProvider::SetRestoreClipStackCallback(
1413     RestoreMatrixClipStackCb callback) {
1414   DCHECK(restore_clip_stack_callback_.is_null() || callback.is_null());
1415   restore_clip_stack_callback_ = std::move(callback);
1416 }
1417 
RestoreBackBuffer(const cc::PaintImage & image)1418 void CanvasResourceProvider::RestoreBackBuffer(const cc::PaintImage& image) {
1419   DCHECK_EQ(image.height(), Size().Height());
1420   DCHECK_EQ(image.width(), Size().Width());
1421   EnsureSkiaCanvas();
1422   cc::PaintFlags copy_paint;
1423   copy_paint.setBlendMode(SkBlendMode::kSrc);
1424   skia_canvas_->drawImage(image, 0, 0, &copy_paint);
1425 }
1426 
HasRecordedDrawOps() const1427 bool CanvasResourceProvider::HasRecordedDrawOps() const {
1428   return recorder_ && recorder_->ListHasDrawOps();
1429 }
1430 
TearDownSkSurface()1431 void CanvasResourceProvider::TearDownSkSurface() {
1432   skia_canvas_ = nullptr;
1433   surface_ = nullptr;
1434 }
1435 
ComputeSurfaceSize() const1436 size_t CanvasResourceProvider::ComputeSurfaceSize() const {
1437   if (!surface_)
1438     return 0;
1439 
1440   SkImageInfo info = surface_->imageInfo();
1441   return info.computeByteSize(info.minRowBytes());
1442 }
1443 
OnMemoryDump(base::trace_event::ProcessMemoryDump * pmd)1444 void CanvasResourceProvider::OnMemoryDump(
1445     base::trace_event::ProcessMemoryDump* pmd) {
1446   if (!surface_)
1447     return;
1448 
1449   std::string dump_name =
1450       base::StringPrintf("canvas/ResourceProvider/SkSurface/0x%" PRIXPTR,
1451                          reinterpret_cast<uintptr_t>(surface_.get()));
1452   auto* dump = pmd->CreateAllocatorDump(dump_name);
1453 
1454   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
1455                   base::trace_event::MemoryAllocatorDump::kUnitsBytes,
1456                   ComputeSurfaceSize());
1457   dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount,
1458                   base::trace_event::MemoryAllocatorDump::kUnitsObjects, 1);
1459 
1460   // SkiaMemoryDumpProvider reports only sk_glyph_cache and sk_resource_cache.
1461   // So the SkSurface is suballocation of malloc, not SkiaDumpProvider.
1462   if (const char* system_allocator_name =
1463           base::trace_event::MemoryDumpManager::GetInstance()
1464               ->system_allocator_pool_name()) {
1465     pmd->AddSuballocation(dump->guid(), system_allocator_name);
1466   }
1467 }
1468 
GetSize() const1469 size_t CanvasResourceProvider::GetSize() const {
1470   return ComputeSurfaceSize();
1471 }
1472 
1473 }  // namespace blink
1474