1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h"
27 
28 #include <memory>
29 #include <utility>
30 
31 #include "base/location.h"
32 #include "base/rand_util.h"
33 #include "base/single_thread_task_runner.h"
34 #include "base/timer/elapsed_timer.h"
35 #include "cc/layers/texture_layer.h"
36 #include "components/viz/common/resources/transferable_resource.h"
37 #include "gpu/command_buffer/client/raster_interface.h"
38 
39 #include "third_party/blink/public/common/features.h"
40 #include "third_party/blink/public/platform/platform.h"
41 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
42 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
43 #include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h"
44 #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
45 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
46 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
47 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
48 #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
49 #include "third_party/blink/renderer/platform/graphics/web_graphics_context_3d_provider_wrapper.h"
50 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
51 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
52 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
53 #include "third_party/skia/include/core/SkData.h"
54 #include "third_party/skia/include/core/SkSurface.h"
55 
56 namespace blink {
57 
Canvas2DLayerBridge(const IntSize & size,RasterMode raster_mode,const CanvasColorParams & color_params)58 Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size,
59                                          RasterMode raster_mode,
60                                          const CanvasColorParams& color_params)
61     : logger_(std::make_unique<Logger>()),
62       have_recorded_draw_commands_(false),
63       is_hidden_(false),
64       is_being_displayed_(false),
65       raster_mode_(raster_mode),
66       color_params_(color_params),
67       size_(size),
68       snapshot_state_(kInitialSnapshotState),
69       resource_host_(nullptr),
70       random_generator_((uint32_t)base::RandUint64()),
71       bernoulli_distribution_(kRasterMetricProbability),
72       last_recording_(nullptr) {
73   // Used by browser tests to detect the use of a Canvas2DLayerBridge.
74   TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation",
75                        TRACE_EVENT_SCOPE_GLOBAL);
76 }
77 
~Canvas2DLayerBridge()78 Canvas2DLayerBridge::~Canvas2DLayerBridge() {
79   ClearPendingRasterTimers();
80   if (IsHibernating())
81     logger_->ReportHibernationEvent(kHibernationEndedWithTeardown);
82   ResetResourceProvider();
83 
84   if (!layer_)
85     return;
86 
87   if (raster_mode_ == RasterMode::kGPU) {
88     layer_->ClearTexture();
89     // Orphaning the layer is required to trigger the recreation of a new layer
90     // in the case where destruction is caused by a canvas resize. Test:
91     // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
92     layer_->RemoveFromParent();
93   }
94   layer_->ClearClient();
95   layer_ = nullptr;
96 }
97 
SetCanvasResourceHost(CanvasResourceHost * host)98 void Canvas2DLayerBridge::SetCanvasResourceHost(CanvasResourceHost* host) {
99   resource_host_ = host;
100 }
101 
ResetResourceProvider()102 void Canvas2DLayerBridge::ResetResourceProvider() {
103   if (resource_host_)
104     resource_host_->ReplaceResourceProvider(nullptr);
105 }
106 
ShouldAccelerate() const107 bool Canvas2DLayerBridge::ShouldAccelerate() const {
108   bool use_gpu = raster_mode_ == RasterMode::kGPU;
109 
110   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
111       SharedGpuContext::ContextProviderWrapper();
112   if (use_gpu &&
113       (!context_provider_wrapper ||
114        context_provider_wrapper->ContextProvider()->IsContextLost())) {
115     use_gpu = false;
116   }
117   return use_gpu;
118 }
119 
IsAccelerated() const120 bool Canvas2DLayerBridge::IsAccelerated() const {
121   if (raster_mode_ == RasterMode::kCPU)
122     return false;
123   if (IsHibernating())
124     return false;
125   if (resource_host_ && resource_host_->ResourceProvider())
126     return resource_host_->ResourceProvider()->IsAccelerated();
127 
128   // Whether or not to accelerate is not yet resolved, the canvas cannot be
129   // accelerated if the gpu context is lost.
130   return ShouldAccelerate();
131 }
132 
HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,base::TimeTicks)133 static void HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,
134                              base::TimeTicks /*idleDeadline*/) {
135   if (bridge) {
136     bridge->Hibernate();
137   } else {
138     Canvas2DLayerBridge::Logger local_logger;
139     local_logger.ReportHibernationEvent(
140         Canvas2DLayerBridge::
141             kHibernationAbortedDueToDestructionWhileHibernatePending);
142   }
143 }
144 
HibernateWrapperForTesting(base::WeakPtr<Canvas2DLayerBridge> bridge)145 static void HibernateWrapperForTesting(
146     base::WeakPtr<Canvas2DLayerBridge> bridge) {
147   HibernateWrapper(std::move(bridge), base::TimeTicks());
148 }
149 
Hibernate()150 void Canvas2DLayerBridge::Hibernate() {
151   DCHECK(!IsHibernating());
152   DCHECK(hibernation_scheduled_);
153 
154   hibernation_scheduled_ = false;
155 
156   if (!resource_host_ || !resource_host_->ResourceProvider()) {
157     logger_->ReportHibernationEvent(kHibernationAbortedBecauseNoSurface);
158     return;
159   }
160 
161   if (!IsHidden()) {
162     logger_->ReportHibernationEvent(kHibernationAbortedDueToVisibilityChange);
163     return;
164   }
165 
166   if (!IsValid()) {
167     logger_->ReportHibernationEvent(kHibernationAbortedDueGpuContextLoss);
168     return;
169   }
170 
171   if (!IsAccelerated()) {
172     logger_->ReportHibernationEvent(
173         kHibernationAbortedDueToSwitchToUnacceleratedRendering);
174     return;
175   }
176 
177   TRACE_EVENT0("blink", "Canvas2DLayerBridge::hibernate");
178   // No HibernationEvent reported on success. This is on purppose to avoid
179   // non-complementary stats. Each HibernationScheduled event is paired with
180   // exactly one failure or exit event.
181   FlushRecording();
182   // The following checks that the flush succeeded, which should always be the
183   // case because flushRecording should only fail it it fails to allocate
184   // a surface, and we have an early exit at the top of this function for when
185   // 'this' does not already have a surface.
186   DCHECK(!have_recorded_draw_commands_);
187   SkPaint copy_paint;
188   copy_paint.setBlendMode(SkBlendMode::kSrc);
189   scoped_refptr<StaticBitmapImage> snapshot =
190       resource_host_->ResourceProvider()->Snapshot();
191   if (!snapshot) {
192     logger_->ReportHibernationEvent(kHibernationAbortedDueSnapshotFailure);
193     return;
194   }
195   hibernation_image_ = snapshot->PaintImageForCurrentFrame().GetSwSkImage();
196   ResetResourceProvider();
197   if (layer_)
198     layer_->ClearTexture();
199 
200   // shouldBeDirectComposited() may have changed.
201   if (resource_host_)
202     resource_host_->SetNeedsCompositingUpdate();
203   logger_->DidStartHibernating();
204 }
205 
ResourceProvider() const206 CanvasResourceProvider* Canvas2DLayerBridge::ResourceProvider() const {
207   return resource_host_ ? resource_host_->ResourceProvider() : nullptr;
208 }
209 
GetOrCreateResourceProvider()210 CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider() {
211   DCHECK(resource_host_);
212   CanvasResourceProvider* resource_provider = ResourceProvider();
213 
214   if (context_lost_) {
215     DCHECK(!resource_provider);
216     return nullptr;
217   }
218 
219   if (resource_provider && resource_provider->IsValid()) {
220 #if DCHECK_IS_ON()
221     // If resource provider is accelerated, a layer should already exist.
222     // unless this is a canvas in low latency mode.
223     // If this DCHECK fails, it probably means that
224     // CanvasRenderingContextHost::GetOrCreateCanvasResourceProvider() was
225     // called on a 2D context before this function.
226     if (IsAccelerated()) {
227       DCHECK(!!layer_ ||
228              (resource_host_ && resource_host_->LowLatencyEnabled()));
229     }
230 #endif
231     return resource_provider;
232   }
233 
234   // Restore() is tried at most four times in two seconds to recreate the
235   // ResourceProvider before the final attempt, in which a new
236   // Canvas2DLayerBridge is created along with its resource provider.
237 
238   bool want_acceleration = ShouldAccelerate();
239   RasterModeHint adjusted_hint = want_acceleration ? RasterModeHint::kPreferGPU
240                                                    : RasterModeHint::kPreferCPU;
241 
242   // Re-creation will happen through Restore().
243   // If the Canvas2DLayerBridge has just been created, possibly due to failed
244   // attempts of Restore(), the layer would not exist, therefore, it will not
245   // fall through this clause to try Restore() again
246   if (layer_ && !IsHibernating() &&
247       adjusted_hint == RasterModeHint::kPreferGPU) {
248     return nullptr;
249   }
250 
251   // We call GetOrCreateCanvasResourceProviderImpl directly here to prevent a
252   // circular callstack from HTMLCanvasElement.
253   resource_provider =
254       resource_host_->GetOrCreateCanvasResourceProviderImpl(adjusted_hint);
255   if (!resource_provider || !resource_provider->IsValid())
256     return nullptr;
257 
258   // Calling to DidDraw because GetOrCreateResourceProvider created a new
259   // provider and cleared it
260   // TODO crbug/1090081: Check possibility to move DidDraw inside Clear.
261   DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height()));
262 
263   if (IsAccelerated() && !layer_) {
264     layer_ = cc::TextureLayer::CreateForMailbox(this);
265     layer_->SetIsDrawable(true);
266     layer_->SetHitTestable(true);
267     layer_->SetContentsOpaque(ColorParams().GetOpacityMode() == kOpaque);
268     layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque);
269     layer_->SetNearestNeighbor(resource_host_->FilterQuality() ==
270                                kNone_SkFilterQuality);
271   }
272 
273   if (!IsHibernating())
274     return resource_provider;
275 
276   if (resource_provider->IsAccelerated()) {
277     logger_->ReportHibernationEvent(kHibernationEndedNormally);
278   } else {
279     if (IsHidden()) {
280       logger_->ReportHibernationEvent(
281           kHibernationEndedWithSwitchToBackgroundRendering);
282     } else {
283       logger_->ReportHibernationEvent(kHibernationEndedWithFallbackToSW);
284     }
285   }
286 
287   PaintImageBuilder builder = PaintImageBuilder::WithDefault();
288   builder.set_image(hibernation_image_, PaintImage::GetNextContentId());
289   builder.set_id(PaintImage::GetNextId());
290   resource_provider->RestoreBackBuffer(builder.TakePaintImage());
291   hibernation_image_.reset();
292 
293   if (resource_host_) {
294     // shouldBeDirectComposited() may have changed.
295     resource_host_->SetNeedsCompositingUpdate();
296   }
297   return resource_provider;
298 }
299 
GetPaintCanvas()300 cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() {
301   DCHECK(resource_host_);
302   // We avoid only using GetOrCreateResourceProvider() here to skip the
303   // IsValid/ContextLost checks since this is in hot code paths. The context
304   // does not need to be valid here since only the recording canvas is used.
305   if (!ResourceProvider() && !GetOrCreateResourceProvider())
306     return nullptr;
307   return ResourceProvider()->Canvas();
308 }
309 
SetFilterQuality(SkFilterQuality filter_quality)310 void Canvas2DLayerBridge::SetFilterQuality(SkFilterQuality filter_quality) {
311   if (CanvasResourceProvider* resource_provider = ResourceProvider())
312     resource_provider->SetFilterQuality(filter_quality);
313   if (layer_)
314     layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
315 }
316 
SetIsInHiddenPage(bool hidden)317 void Canvas2DLayerBridge::SetIsInHiddenPage(bool hidden) {
318   if (is_hidden_ == hidden)
319     return;
320 
321   is_hidden_ = hidden;
322   if (ResourceProvider())
323     ResourceProvider()->SetResourceRecyclingEnabled(!IsHidden());
324 
325   if (CANVAS2D_HIBERNATION_ENABLED && ResourceProvider() && IsAccelerated() &&
326       IsHidden() && !hibernation_scheduled_) {
327     if (layer_)
328       layer_->ClearTexture();
329     logger_->ReportHibernationEvent(kHibernationScheduled);
330     hibernation_scheduled_ = true;
331     if (dont_use_idle_scheduling_for_testing_) {
332       Thread::Current()->GetTaskRunner()->PostTask(
333           FROM_HERE, WTF::Bind(&HibernateWrapperForTesting,
334                                weak_ptr_factory_.GetWeakPtr()));
335     } else {
336       ThreadScheduler::Current()->PostIdleTask(
337           FROM_HERE,
338           WTF::Bind(&HibernateWrapper, weak_ptr_factory_.GetWeakPtr()));
339     }
340   }
341   if (!IsHidden() && IsHibernating())
342     GetOrCreateResourceProvider();  // Rude awakening
343 }
344 
SetIsBeingDisplayed(bool displayed)345 void Canvas2DLayerBridge::SetIsBeingDisplayed(bool displayed) {
346   is_being_displayed_ = displayed;
347   // If the canvas is no longer being displayed, stop using the rate
348   // limiter.
349   if (!is_being_displayed_) {
350     frames_since_last_commit_ = 0;
351     if (rate_limiter_) {
352       rate_limiter_->Reset();
353       rate_limiter_.reset(nullptr);
354     }
355   }
356 }
357 
DrawFullImage(const cc::PaintImage & image)358 void Canvas2DLayerBridge::DrawFullImage(const cc::PaintImage& image) {
359   GetPaintCanvas()->drawImage(image, 0, 0);
360 }
361 
WritePixels(const SkImageInfo & orig_info,const void * pixels,size_t row_bytes,int x,int y)362 bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info,
363                                       const void* pixels,
364                                       size_t row_bytes,
365                                       int x,
366                                       int y) {
367   if (!GetOrCreateResourceProvider())
368     return false;
369 
370   if (x <= 0 && y <= 0 && x + orig_info.width() >= size_.Width() &&
371       y + orig_info.height() >= size_.Height()) {
372     SkipQueuedDrawCommands();
373   } else {
374     FlushRecording();
375     if (!GetOrCreateResourceProvider())
376       return false;
377   }
378   have_recorded_draw_commands_ = false;
379 
380   bool wrote_pixels =
381       ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y);
382   if (wrote_pixels)
383     last_record_tainted_by_write_pixels_ = true;
384 
385   return wrote_pixels;
386 }
387 
SkipQueuedDrawCommands()388 void Canvas2DLayerBridge::SkipQueuedDrawCommands() {
389   ResourceProvider()->SkipQueuedDrawCommands();
390   have_recorded_draw_commands_ = false;
391 
392   if (rate_limiter_)
393     rate_limiter_->Reset();
394 }
395 
ClearPendingRasterTimers()396 void Canvas2DLayerBridge::ClearPendingRasterTimers() {
397   gpu::raster::RasterInterface* raster_interface = nullptr;
398   if (IsAccelerated() && SharedGpuContext::ContextProviderWrapper() &&
399       SharedGpuContext::ContextProviderWrapper()->ContextProvider()) {
400     raster_interface = SharedGpuContext::ContextProviderWrapper()
401                            ->ContextProvider()
402                            ->RasterInterface();
403   }
404 
405   if (raster_interface) {
406     while (!pending_raster_timers_.IsEmpty()) {
407       RasterTimer rt = pending_raster_timers_.TakeFirst();
408       raster_interface->DeleteQueriesEXT(1, &rt.gl_query_id);
409     }
410   } else {
411     pending_raster_timers_.clear();
412   }
413 }
414 
FinishRasterTimers(gpu::raster::RasterInterface * raster_interface)415 void Canvas2DLayerBridge::FinishRasterTimers(
416     gpu::raster::RasterInterface* raster_interface) {
417   // If the context was lost, then the old queries are not valid anymore
418   if (!CheckResourceProviderValid()) {
419     ClearPendingRasterTimers();
420     return;
421   }
422 
423   // Finish up any pending queries that are complete
424   while (!pending_raster_timers_.IsEmpty()) {
425     auto it = pending_raster_timers_.begin();
426     GLuint complete = 1;
427     raster_interface->GetQueryObjectuivEXT(
428         it->gl_query_id, GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT,
429         &complete);
430     if (!complete) {
431       break;
432     }
433 
434     GLuint raw_gpu_duration = 0u;
435     raster_interface->GetQueryObjectuivEXT(it->gl_query_id, GL_QUERY_RESULT_EXT,
436                                            &raw_gpu_duration);
437     base::TimeDelta gpu_duration_microseconds =
438         base::TimeDelta::FromMicroseconds(raw_gpu_duration);
439     base::TimeDelta total_time =
440         gpu_duration_microseconds + it->cpu_raster_duration;
441 
442     base::TimeDelta min = base::TimeDelta::FromMicroseconds(1);
443     base::TimeDelta max = base::TimeDelta::FromMilliseconds(100);
444     int num_buckets = 100;
445     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
446         "Blink.Canvas.RasterDuration.Accelerated.GPU",
447         gpu_duration_microseconds, min, max, num_buckets);
448     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
449         "Blink.Canvas.RasterDuration.Accelerated.CPU", it->cpu_raster_duration,
450         min, max, num_buckets);
451     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
452         "Blink.Canvas.RasterDuration.Accelerated.Total", total_time, min, max,
453         num_buckets);
454 
455     raster_interface->DeleteQueriesEXT(1, &it->gl_query_id);
456 
457     pending_raster_timers_.erase(it);
458   }
459 }
460 
FlushRecording()461 void Canvas2DLayerBridge::FlushRecording() {
462   if (!have_recorded_draw_commands_ || !GetOrCreateResourceProvider())
463     return;
464 
465   TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushRecording");
466 
467   gpu::raster::RasterInterface* raster_interface = nullptr;
468   if (IsAccelerated() && SharedGpuContext::ContextProviderWrapper() &&
469       SharedGpuContext::ContextProviderWrapper()->ContextProvider()) {
470     raster_interface = SharedGpuContext::ContextProviderWrapper()
471                            ->ContextProvider()
472                            ->RasterInterface();
473     FinishRasterTimers(raster_interface);
474   }
475 
476   // Sample one out of every kRasterMetricProbability frames to time
477   // If the canvas is accelerated, we also need access to the raster_interface
478 
479   // We are using @dont_use_idle_scheduling_for_testing_ temporarily to always
480   // measure while testing.
481   const bool will_measure = dont_use_idle_scheduling_for_testing_ ||
482                             bernoulli_distribution_(random_generator_);
483   const bool measure_raster_metric =
484       (raster_interface || !IsAccelerated()) && will_measure;
485 
486   RasterTimer rasterTimer;
487   base::Optional<base::ElapsedTimer> timer;
488   // Start Recording the raster duration
489   if (measure_raster_metric) {
490     if (IsAccelerated()) {
491       GLuint gl_id = 0u;
492       raster_interface->GenQueriesEXT(1, &gl_id);
493       raster_interface->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, gl_id);
494       rasterTimer.gl_query_id = gl_id;
495     }
496     timer.emplace();
497   }
498 
499   last_recording_ = ResourceProvider()->FlushCanvas();
500   last_record_tainted_by_write_pixels_ = false;
501   if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) {
502     last_recording_ = nullptr;
503     clear_frame_ = false;
504   }
505 
506   // Finish up the timing operation
507   if (measure_raster_metric) {
508     if (IsAccelerated()) {
509       rasterTimer.cpu_raster_duration = timer->Elapsed();
510       raster_interface->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
511       pending_raster_timers_.push_back(rasterTimer);
512     } else {
513       UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
514           "Blink.Canvas.RasterDuration.Unaccelerated", timer->Elapsed(),
515           base::TimeDelta::FromMicroseconds(1),
516           base::TimeDelta::FromMilliseconds(100), 100);
517     }
518   }
519   have_recorded_draw_commands_ = false;
520 }
521 
HasRateLimiterForTesting()522 bool Canvas2DLayerBridge::HasRateLimiterForTesting() {
523   return !!rate_limiter_;
524 }
525 
IsValid()526 bool Canvas2DLayerBridge::IsValid() {
527   return CheckResourceProviderValid();
528 }
529 
CheckResourceProviderValid()530 bool Canvas2DLayerBridge::CheckResourceProviderValid() {
531   if (IsHibernating())
532     return true;
533   if (!layer_ || raster_mode_ == RasterMode::kCPU)
534     return true;
535   if (context_lost_)
536     return false;
537   if (ResourceProvider() && IsAccelerated() &&
538       ResourceProvider()->IsGpuContextLost()) {
539     context_lost_ = true;
540     ClearPendingRasterTimers();
541     ResetResourceProvider();
542     if (resource_host_)
543       resource_host_->NotifyGpuContextLost();
544     return false;
545   }
546   return !!GetOrCreateResourceProvider();
547 }
548 
Restore()549 bool Canvas2DLayerBridge::Restore() {
550   DCHECK(context_lost_);
551   if (!IsAccelerated())
552     return false;
553   DCHECK(!ResourceProvider());
554 
555   layer_->ClearTexture();
556   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
557       SharedGpuContext::ContextProviderWrapper();
558 
559   if (!context_provider_wrapper->ContextProvider()->IsContextLost()) {
560     CanvasResourceProvider* resource_provider =
561         resource_host_->GetOrCreateCanvasResourceProviderImpl(
562             RasterModeHint::kPreferGPU);
563 
564     // The current paradigm does not support switching from accelerated to
565     // non-accelerated, which would be tricky due to changes to the layer tree,
566     // which can only happen at specific times during the document lifecycle.
567     // Therefore, we can only accept the restored surface if it is accelerated.
568     if (resource_provider && !IsAccelerated()) {
569       resource_host_->ReplaceResourceProvider(nullptr);
570       // FIXME: draw sad canvas picture into new buffer crbug.com/243842
571     } else {
572       context_lost_ = false;
573     }
574   }
575 
576   if (resource_host_)
577     resource_host_->UpdateMemoryUsage();
578 
579   return ResourceProvider();
580 }
581 
PrepareTransferableResource(cc::SharedBitmapIdRegistrar * bitmap_registrar,viz::TransferableResource * out_resource,std::unique_ptr<viz::SingleReleaseCallback> * out_release_callback)582 bool Canvas2DLayerBridge::PrepareTransferableResource(
583     cc::SharedBitmapIdRegistrar* bitmap_registrar,
584     viz::TransferableResource* out_resource,
585     std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
586   DCHECK(layer_);  // This explodes if FinalizeFrame() was not called.
587 
588   frames_since_last_commit_ = 0;
589   if (rate_limiter_)
590     rate_limiter_->Reset();
591 
592   // If hibernating but not hidden, we want to wake up from hibernation.
593   if (IsHibernating() && IsHidden())
594     return false;
595 
596   if (!IsValid())
597     return false;
598 
599   FlushRecording();
600 
601   // If the context is lost, we don't know if we should be producing GPU or
602   // software frames, until we get a new context, since the compositor will
603   // be trying to get a new context and may change modes.
604   if (!GetOrCreateResourceProvider())
605     return false;
606 
607   scoped_refptr<CanvasResource> frame =
608       ResourceProvider()->ProduceCanvasResource();
609   if (!frame || !frame->IsValid())
610     return false;
611 
612   // Note frame is kept alive via a reference kept in out_release_callback.
613   if (!frame->PrepareTransferableResource(out_resource, out_release_callback,
614                                           kUnverifiedSyncToken) ||
615       *out_resource == layer_->current_transferable_resource()) {
616     // If the resource did not change, the release will be handled correctly
617     // when the callback from the previous frame is dispatched. But run the
618     // |out_release_callback| to release the ref acquired above.
619     (*out_release_callback)->Run(gpu::SyncToken(), false /* is_lost */);
620     *out_release_callback = nullptr;
621     return false;
622   }
623 
624   return true;
625 }
626 
Layer()627 cc::Layer* Canvas2DLayerBridge::Layer() {
628   // Trigger lazy layer creation
629   GetOrCreateResourceProvider();
630   return layer_.get();
631 }
632 
DidDraw(const FloatRect &)633 void Canvas2DLayerBridge::DidDraw(const FloatRect& /* rect */) {
634   if (ResourceProvider() && ResourceProvider()->needs_flush())
635     FinalizeFrame();
636   have_recorded_draw_commands_ = true;
637 }
638 
FinalizeFrame()639 void Canvas2DLayerBridge::FinalizeFrame() {
640   TRACE_EVENT0("blink", "Canvas2DLayerBridge::FinalizeFrame");
641 
642   // Make sure surface is ready for painting: fix the rendering mode now
643   // because it will be too late during the paint invalidation phase.
644   if (!GetOrCreateResourceProvider())
645     return;
646 
647   FlushRecording();
648   if (is_being_displayed_) {
649     ++frames_since_last_commit_;
650     // Make sure the GPU is never more than two animation frames behind.
651     constexpr unsigned kMaxCanvasAnimationBacklog = 2;
652     if (frames_since_last_commit_ >=
653         static_cast<int>(kMaxCanvasAnimationBacklog)) {
654       if (IsAccelerated() && !rate_limiter_) {
655         rate_limiter_ = std::make_unique<SharedContextRateLimiter>(
656             kMaxCanvasAnimationBacklog);
657       }
658     }
659   }
660 
661   if (rate_limiter_)
662     rate_limiter_->Tick();
663 }
664 
DoPaintInvalidation(const FloatRect & dirty_rect)665 void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
666   if (layer_ && raster_mode_ == RasterMode::kGPU)
667     layer_->SetNeedsDisplayRect(EnclosingIntRect(dirty_rect));
668 }
669 
NewImageSnapshot()670 scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot() {
671   if (snapshot_state_ == kInitialSnapshotState)
672     snapshot_state_ = kDidAcquireSnapshot;
673   if (IsHibernating())
674     return UnacceleratedStaticBitmapImage::Create(hibernation_image_);
675   if (!IsValid())
676     return nullptr;
677   // GetOrCreateResourceProvider needs to be called before FlushRecording, to
678   // make sure "hint" is properly taken into account, as well as after
679   // FlushRecording, in case the playback crashed the GPU context.
680   if (!GetOrCreateResourceProvider())
681     return nullptr;
682   FlushRecording();
683   if (!GetOrCreateResourceProvider())
684     return nullptr;
685   return ResourceProvider()->Snapshot();
686 }
687 
WillOverwriteCanvas()688 void Canvas2DLayerBridge::WillOverwriteCanvas() {
689   SkipQueuedDrawCommands();
690 }
691 
ReportHibernationEvent(HibernationEvent event)692 void Canvas2DLayerBridge::Logger::ReportHibernationEvent(
693     HibernationEvent event) {
694   UMA_HISTOGRAM_ENUMERATION("Blink.Canvas.HibernationEvents", event);
695 }
696 
697 }  // namespace blink
698