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, ©_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