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,AccelerationMode acceleration_mode,const CanvasColorParams & color_params)58 Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size,
59                                          AccelerationMode acceleration_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       software_rendering_while_hidden_(false),
66       acceleration_mode_(acceleration_mode),
67       color_params_(color_params),
68       size_(size),
69       snapshot_state_(kInitialSnapshotState),
70       resource_host_(nullptr),
71       random_generator_((uint32_t)base::RandUint64()),
72       bernoulli_distribution_(kRasterMetricProbability),
73       last_recording_(nullptr) {
74   // Used by browser tests to detect the use of a Canvas2DLayerBridge.
75   TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation",
76                        TRACE_EVENT_SCOPE_GLOBAL);
77 }
78 
~Canvas2DLayerBridge()79 Canvas2DLayerBridge::~Canvas2DLayerBridge() {
80   ClearPendingRasterTimers();
81   if (IsHibernating())
82     logger_->ReportHibernationEvent(kHibernationEndedWithTeardown);
83   ResetResourceProvider();
84 
85   if (!layer_)
86     return;
87 
88   if (acceleration_mode_ != kDisableAcceleration) {
89     layer_->ClearTexture();
90     // Orphaning the layer is required to trigger the recreation of a new layer
91     // in the case where destruction is caused by a canvas resize. Test:
92     // virtual/gpu/fast/canvas/canvas-resize-after-paint-without-layout.html
93     layer_->RemoveFromParent();
94   }
95   layer_->ClearClient();
96   layer_ = nullptr;
97 }
98 
SetCanvasResourceHost(CanvasResourceHost * host)99 void Canvas2DLayerBridge::SetCanvasResourceHost(CanvasResourceHost* host) {
100   resource_host_ = host;
101 
102   if (resource_host_ && GetOrCreateResourceProvider()) {
103     EnsureCleared();
104   }
105 }
106 
ResetResourceProvider()107 void Canvas2DLayerBridge::ResetResourceProvider() {
108   if (resource_host_)
109     resource_host_->ReplaceResourceProvider(nullptr);
110 }
111 
ShouldAccelerate(AccelerationHint hint) const112 bool Canvas2DLayerBridge::ShouldAccelerate(AccelerationHint hint) const {
113   bool accelerate;
114   if (software_rendering_while_hidden_) {
115     accelerate = false;
116   } else if (acceleration_mode_ == kForceAccelerationForTesting) {
117     accelerate = true;
118   } else if (acceleration_mode_ == kDisableAcceleration) {
119     accelerate = false;
120   } else if (acceleration_mode_ == kEnableAcceleration) {
121     accelerate = true;
122   } else {
123     accelerate = hint == kPreferAcceleration ||
124                  hint == kPreferAccelerationAfterVisibilityChange;
125   }
126 
127   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
128       SharedGpuContext::ContextProviderWrapper();
129   if (accelerate && (!context_provider_wrapper ||
130                      context_provider_wrapper->ContextProvider()
131                              ->RasterInterface()
132                              ->GetGraphicsResetStatusKHR() != GL_NO_ERROR)) {
133     accelerate = false;
134   }
135   return accelerate;
136 }
137 
IsAccelerated() const138 bool Canvas2DLayerBridge::IsAccelerated() const {
139   if (acceleration_mode_ == kDisableAcceleration)
140     return false;
141   if (IsHibernating())
142     return false;
143   if (software_rendering_while_hidden_)
144     return false;
145   if (resource_host_ && resource_host_->ResourceProvider())
146     return resource_host_->ResourceProvider()->IsAccelerated();
147 
148   // Whether or not to accelerate is not yet resolved. Determine whether
149   // immediate presentation of the canvas would result in the canvas being
150   // accelerated. Presentation is assumed to be a 'PreferAcceleration'
151   // operation.
152   return ShouldAccelerate(kPreferAcceleration);
153 }
154 
HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,base::TimeTicks)155 static void HibernateWrapper(base::WeakPtr<Canvas2DLayerBridge> bridge,
156                              base::TimeTicks /*idleDeadline*/) {
157   if (bridge) {
158     bridge->Hibernate();
159   } else {
160     Canvas2DLayerBridge::Logger local_logger;
161     local_logger.ReportHibernationEvent(
162         Canvas2DLayerBridge::
163             kHibernationAbortedDueToDestructionWhileHibernatePending);
164   }
165 }
166 
HibernateWrapperForTesting(base::WeakPtr<Canvas2DLayerBridge> bridge)167 static void HibernateWrapperForTesting(
168     base::WeakPtr<Canvas2DLayerBridge> bridge) {
169   HibernateWrapper(std::move(bridge), base::TimeTicks());
170 }
171 
Hibernate()172 void Canvas2DLayerBridge::Hibernate() {
173   DCHECK(!IsHibernating());
174   DCHECK(hibernation_scheduled_);
175 
176   hibernation_scheduled_ = false;
177 
178   if (!resource_host_ || !resource_host_->ResourceProvider()) {
179     logger_->ReportHibernationEvent(kHibernationAbortedBecauseNoSurface);
180     return;
181   }
182 
183   if (!IsHidden()) {
184     logger_->ReportHibernationEvent(kHibernationAbortedDueToVisibilityChange);
185     return;
186   }
187 
188   if (!IsValid()) {
189     logger_->ReportHibernationEvent(kHibernationAbortedDueGpuContextLoss);
190     return;
191   }
192 
193   if (!IsAccelerated()) {
194     logger_->ReportHibernationEvent(
195         kHibernationAbortedDueToSwitchToUnacceleratedRendering);
196     return;
197   }
198 
199   TRACE_EVENT0("blink", "Canvas2DLayerBridge::hibernate");
200   sk_sp<SkSurface> temp_hibernation_surface =
201       SkSurface::MakeRasterN32Premul(size_.Width(), size_.Height());
202   if (!temp_hibernation_surface) {
203     logger_->ReportHibernationEvent(kHibernationAbortedDueToAllocationFailure);
204     return;
205   }
206   // No HibernationEvent reported on success. This is on purppose to avoid
207   // non-complementary stats. Each HibernationScheduled event is paired with
208   // exactly one failure or exit event.
209   FlushRecording();
210   // The following checks that the flush succeeded, which should always be the
211   // case because flushRecording should only fail it it fails to allocate
212   // a surface, and we have an early exit at the top of this function for when
213   // 'this' does not already have a surface.
214   DCHECK(!have_recorded_draw_commands_);
215   SkPaint copy_paint;
216   copy_paint.setBlendMode(SkBlendMode::kSrc);
217   scoped_refptr<StaticBitmapImage> snapshot =
218       resource_host_->ResourceProvider()->Snapshot();
219   if (!snapshot) {
220     logger_->ReportHibernationEvent(kHibernationAbortedDueSnapshotFailure);
221     return;
222   }
223   temp_hibernation_surface->getCanvas()->drawImage(
224       snapshot->PaintImageForCurrentFrame().GetSkImage(), 0, 0, &copy_paint);
225   hibernation_image_ = temp_hibernation_surface->makeImageSnapshot();
226   ResetResourceProvider();
227   if (layer_)
228     layer_->ClearTexture();
229 
230   // shouldBeDirectComposited() may have changed.
231   if (resource_host_)
232     resource_host_->SetNeedsCompositingUpdate();
233   logger_->DidStartHibernating();
234 }
235 
ResourceProvider() const236 CanvasResourceProvider* Canvas2DLayerBridge::ResourceProvider() const {
237   return resource_host_ ? resource_host_->ResourceProvider() : nullptr;
238 }
239 
GetOrCreateResourceProvider(AccelerationHint hint)240 CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider(
241     AccelerationHint hint) {
242   DCHECK(resource_host_);
243   CanvasResourceProvider* resource_provider = ResourceProvider();
244 
245   if (context_lost_) {
246     DCHECK(!resource_provider);
247     return nullptr;
248   }
249 
250   if (resource_provider && resource_provider->IsValid()) {
251 #if DCHECK_IS_ON()
252     // If resource provider is accelerated, a layer should already exist.
253     // unless this is a canvas in low latency mode.
254     // If this DCHECK fails, it probably means that
255     // CanvasRenderingContextHost::GetOrCreateCanvasResourceProvider() was
256     // called on a 2D context before this function.
257     if (IsAccelerated()) {
258       DCHECK(!!layer_ ||
259              (resource_host_ && resource_host_->LowLatencyEnabled()));
260     }
261 #endif
262     return resource_provider;
263   }
264 
265   if (layer_ && !IsHibernating() && hint == kPreferAcceleration &&
266       acceleration_mode_ != kDisableAcceleration) {
267     return nullptr;  // re-creation will happen through restore()
268   }
269 
270   bool want_acceleration = ShouldAccelerate(hint);
271   if (CANVAS2D_BACKGROUND_RENDER_SWITCH_TO_CPU && IsHidden() &&
272       want_acceleration) {
273     want_acceleration = false;
274     software_rendering_while_hidden_ = true;
275   }
276   AccelerationHint adjusted_hint =
277       want_acceleration ? kPreferAcceleration : kPreferNoAcceleration;
278 
279   // We call GetOrCreateCanvasResourceProviderImpl directly here to prevent a
280   // circular callstack from HTMLCanvasElement.
281   resource_provider =
282       resource_host_->GetOrCreateCanvasResourceProviderImpl(adjusted_hint);
283   if (!resource_provider || !resource_provider->IsValid())
284     return nullptr;
285 
286   EnsureCleared();
287 
288   if (IsAccelerated() && !layer_) {
289     layer_ = cc::TextureLayer::CreateForMailbox(this);
290     layer_->SetIsDrawable(true);
291     layer_->SetHitTestable(true);
292     layer_->SetContentsOpaque(ColorParams().GetOpacityMode() == kOpaque);
293     layer_->SetBlendBackgroundColor(ColorParams().GetOpacityMode() != kOpaque);
294     layer_->SetNearestNeighbor(resource_host_->FilterQuality() ==
295                                kNone_SkFilterQuality);
296   }
297 
298   if (!IsHibernating())
299     return resource_provider;
300 
301   if (resource_provider->IsAccelerated()) {
302     logger_->ReportHibernationEvent(kHibernationEndedNormally);
303   } else {
304     if (IsHidden()) {
305       logger_->ReportHibernationEvent(
306           kHibernationEndedWithSwitchToBackgroundRendering);
307     } else {
308       logger_->ReportHibernationEvent(kHibernationEndedWithFallbackToSW);
309     }
310   }
311 
312   PaintImageBuilder builder = PaintImageBuilder::WithDefault();
313   builder.set_image(hibernation_image_, PaintImage::GetNextContentId());
314   builder.set_id(PaintImage::GetNextId());
315   resource_provider->RestoreBackBuffer(builder.TakePaintImage());
316   hibernation_image_.reset();
317 
318   if (resource_host_) {
319     // shouldBeDirectComposited() may have changed.
320     resource_host_->SetNeedsCompositingUpdate();
321   }
322   return resource_provider;
323 }
324 
GetPaintCanvas()325 cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() {
326   DCHECK(resource_host_);
327   // We avoid only using GetOrCreateResourceProvider() here to skip the
328   // IsValid/ContextLost checks since this is in hot code paths. The context
329   // does not need to be valid here since only the recording canvas is used.
330   if (!ResourceProvider() && !GetOrCreateResourceProvider())
331     return nullptr;
332   return ResourceProvider()->Canvas();
333 }
334 
UpdateFilterQuality()335 void Canvas2DLayerBridge::UpdateFilterQuality() {
336   SkFilterQuality filter_quality = resource_host_->FilterQuality();
337   if (GetOrCreateResourceProvider())
338     ResourceProvider()->SetFilterQuality(filter_quality);
339   if (layer_)
340     layer_->SetNearestNeighbor(filter_quality == kNone_SkFilterQuality);
341 }
342 
SetIsInHiddenPage(bool hidden)343 void Canvas2DLayerBridge::SetIsInHiddenPage(bool hidden) {
344   if (is_hidden_ == hidden)
345     return;
346 
347   is_hidden_ = hidden;
348   if (ResourceProvider())
349     ResourceProvider()->SetResourceRecyclingEnabled(!IsHidden());
350 
351   if (CANVAS2D_HIBERNATION_ENABLED && ResourceProvider() && IsAccelerated() &&
352       IsHidden() && !hibernation_scheduled_) {
353     if (layer_)
354       layer_->ClearTexture();
355     logger_->ReportHibernationEvent(kHibernationScheduled);
356     hibernation_scheduled_ = true;
357     if (dont_use_idle_scheduling_for_testing_) {
358       Thread::Current()->GetTaskRunner()->PostTask(
359           FROM_HERE, WTF::Bind(&HibernateWrapperForTesting,
360                                weak_ptr_factory_.GetWeakPtr()));
361     } else {
362       ThreadScheduler::Current()->PostIdleTask(
363           FROM_HERE,
364           WTF::Bind(&HibernateWrapper, weak_ptr_factory_.GetWeakPtr()));
365     }
366   }
367   if (!IsHidden() && software_rendering_while_hidden_) {
368     FlushRecording();
369     PaintFlags copy_paint;
370     copy_paint.setBlendMode(SkBlendMode::kSrc);
371 
372     std::unique_ptr<CanvasResourceProvider> old_resource_provider =
373         resource_host_->ReplaceResourceProvider(nullptr);
374 
375     software_rendering_while_hidden_ = false;
376     GetOrCreateResourceProvider(kPreferAccelerationAfterVisibilityChange);
377 
378     if (ResourceProvider()) {
379       if (old_resource_provider) {
380         cc::PaintImage snapshot =
381             old_resource_provider->Snapshot()->PaintImageForCurrentFrame();
382         ResourceProvider()->Canvas()->drawImage(snapshot, 0, 0, &copy_paint);
383       }
384     } else {
385       // New resource provider could not be created. Stay with old one.
386       resource_host_->ReplaceResourceProvider(std::move(old_resource_provider));
387     }
388   }
389   if (!IsHidden() && IsHibernating())
390     GetOrCreateResourceProvider();  // Rude awakening
391 }
392 
SetIsBeingDisplayed(bool displayed)393 void Canvas2DLayerBridge::SetIsBeingDisplayed(bool displayed) {
394   is_being_displayed_ = displayed;
395   // If the canvas is no longer being displayed, stop using the rate
396   // limiter.
397   if (!is_being_displayed_) {
398     frames_since_last_commit_ = 0;
399     if (rate_limiter_) {
400       rate_limiter_->Reset();
401       rate_limiter_.reset(nullptr);
402     }
403   }
404 }
405 
DrawFullImage(const cc::PaintImage & image)406 void Canvas2DLayerBridge::DrawFullImage(const cc::PaintImage& image) {
407   GetPaintCanvas()->drawImage(image, 0, 0);
408 }
409 
WritePixels(const SkImageInfo & orig_info,const void * pixels,size_t row_bytes,int x,int y)410 bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info,
411                                       const void* pixels,
412                                       size_t row_bytes,
413                                       int x,
414                                       int y) {
415   if (!GetOrCreateResourceProvider())
416     return false;
417 
418   if (x <= 0 && y <= 0 && x + orig_info.width() >= size_.Width() &&
419       y + orig_info.height() >= size_.Height()) {
420     SkipQueuedDrawCommands();
421   } else {
422     FlushRecording();
423     if (!GetOrCreateResourceProvider())
424       return false;
425   }
426 
427   last_record_tainted_by_write_pixels_ = true;
428   have_recorded_draw_commands_ = false;
429 
430   ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y);
431   return true;
432 }
433 
SkipQueuedDrawCommands()434 void Canvas2DLayerBridge::SkipQueuedDrawCommands() {
435   ResourceProvider()->SkipQueuedDrawCommands();
436   have_recorded_draw_commands_ = false;
437 
438   if (rate_limiter_)
439     rate_limiter_->Reset();
440 }
441 
EnsureCleared()442 void Canvas2DLayerBridge::EnsureCleared() {
443   if (cleared_)
444     return;
445   cleared_ = true;
446   ResourceProvider()->Clear();
447   DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height()));
448 }
449 
ClearPendingRasterTimers()450 void Canvas2DLayerBridge::ClearPendingRasterTimers() {
451   gpu::raster::RasterInterface* raster_interface = nullptr;
452   if (IsAccelerated() && SharedGpuContext::ContextProviderWrapper() &&
453       SharedGpuContext::ContextProviderWrapper()->ContextProvider()) {
454     raster_interface = SharedGpuContext::ContextProviderWrapper()
455                            ->ContextProvider()
456                            ->RasterInterface();
457   }
458 
459   if (raster_interface) {
460     while (!pending_raster_timers_.IsEmpty()) {
461       RasterTimer rt = pending_raster_timers_.TakeFirst();
462       raster_interface->DeleteQueriesEXT(1, &rt.gl_query_id);
463     }
464   } else {
465     pending_raster_timers_.clear();
466   }
467 }
468 
FinishRasterTimers(gpu::raster::RasterInterface * raster_interface)469 void Canvas2DLayerBridge::FinishRasterTimers(
470     gpu::raster::RasterInterface* raster_interface) {
471   // If the context was lost, then the old queries are not valid anymore
472   if (!CheckResourceProviderValid()) {
473     ClearPendingRasterTimers();
474     return;
475   }
476 
477   // Finish up any pending queries that are complete
478   while (!pending_raster_timers_.IsEmpty()) {
479     auto it = pending_raster_timers_.begin();
480     GLuint complete = 1;
481     raster_interface->GetQueryObjectuivEXT(
482         it->gl_query_id, GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT,
483         &complete);
484     if (!complete) {
485       break;
486     }
487 
488     GLuint raw_gpu_duration = 0u;
489     raster_interface->GetQueryObjectuivEXT(it->gl_query_id, GL_QUERY_RESULT_EXT,
490                                            &raw_gpu_duration);
491     base::TimeDelta gpu_duration_microseconds =
492         base::TimeDelta::FromMicroseconds(raw_gpu_duration);
493     base::TimeDelta total_time =
494         gpu_duration_microseconds + it->cpu_raster_duration;
495 
496     base::TimeDelta min = base::TimeDelta::FromMicroseconds(1);
497     base::TimeDelta max = base::TimeDelta::FromMilliseconds(100);
498     int num_buckets = 100;
499     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
500         "Blink.Canvas.RasterDuration.Accelerated.GPU",
501         gpu_duration_microseconds, min, max, num_buckets);
502     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
503         "Blink.Canvas.RasterDuration.Accelerated.CPU", it->cpu_raster_duration,
504         min, max, num_buckets);
505     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
506         "Blink.Canvas.RasterDuration.Accelerated.Total", total_time, min, max,
507         num_buckets);
508 
509     raster_interface->DeleteQueriesEXT(1, &it->gl_query_id);
510 
511     pending_raster_timers_.erase(it);
512   }
513 }
514 
FlushRecording()515 void Canvas2DLayerBridge::FlushRecording() {
516   if (!have_recorded_draw_commands_ || !GetOrCreateResourceProvider())
517     return;
518 
519   TRACE_EVENT0("cc", "Canvas2DLayerBridge::flushRecording");
520 
521   gpu::raster::RasterInterface* raster_interface = nullptr;
522   if (IsAccelerated() && SharedGpuContext::ContextProviderWrapper() &&
523       SharedGpuContext::ContextProviderWrapper()->ContextProvider()) {
524     raster_interface = SharedGpuContext::ContextProviderWrapper()
525                            ->ContextProvider()
526                            ->RasterInterface();
527     FinishRasterTimers(raster_interface);
528   }
529 
530   // Sample one out of every kRasterMetricProbability frames to time
531   // If the canvas is accelerated, we also need access to the raster_interface
532 
533   // We are using @dont_use_idle_scheduling_for_testing_ temporarily to always
534   // measure while testing.
535   const bool will_measure = dont_use_idle_scheduling_for_testing_ ||
536                             bernoulli_distribution_(random_generator_);
537   const bool measure_raster_metric =
538       (raster_interface || !IsAccelerated()) && will_measure;
539 
540   RasterTimer rasterTimer;
541   base::Optional<base::ElapsedTimer> timer;
542   // Start Recording the raster duration
543   if (measure_raster_metric) {
544     if (IsAccelerated()) {
545       GLuint gl_id = 0u;
546       raster_interface->GenQueriesEXT(1, &gl_id);
547       raster_interface->BeginQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM, gl_id);
548       rasterTimer.gl_query_id = gl_id;
549     }
550     timer.emplace();
551   }
552 
553   last_recording_ = ResourceProvider()->FlushCanvas();
554   last_record_tainted_by_write_pixels_ = false;
555   if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) {
556     last_recording_ = nullptr;
557     clear_frame_ = false;
558   }
559 
560   // Finish up the timing operation
561   if (measure_raster_metric) {
562     if (IsAccelerated()) {
563       rasterTimer.cpu_raster_duration = timer->Elapsed();
564       raster_interface->EndQueryEXT(GL_COMMANDS_ISSUED_CHROMIUM);
565       pending_raster_timers_.push_back(rasterTimer);
566     } else {
567       UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
568           "Blink.Canvas.RasterDuration.Unaccelerated", timer->Elapsed(),
569           base::TimeDelta::FromMicroseconds(1),
570           base::TimeDelta::FromMilliseconds(100), 100);
571     }
572   }
573 
574   // Rastering the recording would have locked images, since we've flushed
575   // all recorded ops, we should release all locked images as well.
576   // A new null check on the resource provider is necessary just in case
577   // the playback crashed the context.
578   if (GetOrCreateResourceProvider())
579     ResourceProvider()->ReleaseLockedImages();
580 
581   have_recorded_draw_commands_ = false;
582 }
583 
HasRateLimiterForTesting()584 bool Canvas2DLayerBridge::HasRateLimiterForTesting() {
585   return !!rate_limiter_;
586 }
587 
IsValid()588 bool Canvas2DLayerBridge::IsValid() {
589   return CheckResourceProviderValid();
590 }
591 
CheckResourceProviderValid()592 bool Canvas2DLayerBridge::CheckResourceProviderValid() {
593   if (IsHibernating())
594     return true;
595   if (!layer_ || acceleration_mode_ == kDisableAcceleration)
596     return true;
597   if (context_lost_)
598     return false;
599   if (ResourceProvider() && IsAccelerated() &&
600       ResourceProvider()->IsGpuContextLost()) {
601     context_lost_ = true;
602     ClearPendingRasterTimers();
603     ResetResourceProvider();
604     if (resource_host_)
605       resource_host_->NotifyGpuContextLost();
606     return false;
607   }
608   return !!GetOrCreateResourceProvider();
609 }
610 
Restore()611 bool Canvas2DLayerBridge::Restore() {
612   DCHECK(context_lost_);
613   if (!IsAccelerated())
614     return false;
615   DCHECK(!ResourceProvider());
616 
617   gpu::raster::RasterInterface* shared_raster_interface = nullptr;
618   layer_->ClearTexture();
619   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper =
620       SharedGpuContext::ContextProviderWrapper();
621   if (context_provider_wrapper) {
622     shared_raster_interface =
623         context_provider_wrapper->ContextProvider()->RasterInterface();
624   }
625 
626   if (shared_raster_interface &&
627       shared_raster_interface->GetGraphicsResetStatusKHR() == GL_NO_ERROR) {
628     CanvasResourceProvider* resource_provider =
629         resource_host_->GetOrCreateCanvasResourceProviderImpl(
630             kPreferAcceleration);
631 
632     // The current paradigm does not support switching from accelerated to
633     // non-accelerated, which would be tricky due to changes to the layer tree,
634     // which can only happen at specific times during the document lifecycle.
635     // Therefore, we can only accept the restored surface if it is accelerated.
636     if (resource_provider && !IsAccelerated()) {
637       resource_host_->ReplaceResourceProvider(nullptr);
638       // FIXME: draw sad canvas picture into new buffer crbug.com/243842
639     } else {
640       context_lost_ = false;
641     }
642   }
643 
644   if (resource_host_)
645     resource_host_->UpdateMemoryUsage();
646 
647   return ResourceProvider();
648 }
649 
PrepareTransferableResource(cc::SharedBitmapIdRegistrar * bitmap_registrar,viz::TransferableResource * out_resource,std::unique_ptr<viz::SingleReleaseCallback> * out_release_callback)650 bool Canvas2DLayerBridge::PrepareTransferableResource(
651     cc::SharedBitmapIdRegistrar* bitmap_registrar,
652     viz::TransferableResource* out_resource,
653     std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) {
654   DCHECK(layer_);  // This explodes if FinalizeFrame() was not called.
655 
656   frames_since_last_commit_ = 0;
657   if (rate_limiter_)
658     rate_limiter_->Reset();
659 
660   // If hibernating but not hidden, we want to wake up from hibernation.
661   if ((IsHibernating() || software_rendering_while_hidden_) && IsHidden())
662     return false;
663 
664   if (!IsValid())
665     return false;
666 
667   FlushRecording();
668 
669   // If the context is lost, we don't know if we should be producing GPU or
670   // software frames, until we get a new context, since the compositor will
671   // be trying to get a new context and may change modes.
672   if (!GetOrCreateResourceProvider())
673     return false;
674 
675   scoped_refptr<CanvasResource> frame =
676       ResourceProvider()->ProduceCanvasResource();
677   if (!frame || !frame->IsValid())
678     return false;
679 
680   // Note frame is kept alive via a reference kept in out_release_callback.
681   if (!frame->PrepareTransferableResource(out_resource, out_release_callback,
682                                           kUnverifiedSyncToken) ||
683       *out_resource == layer_->current_transferable_resource()) {
684     // If the resource did not change, the release will be handled correctly
685     // when the callback from the previous frame is dispatched. But run the
686     // |out_release_callback| to release the ref acquired above.
687     (*out_release_callback)->Run(gpu::SyncToken(), false /* is_lost */);
688     *out_release_callback = nullptr;
689     return false;
690   }
691 
692   return true;
693 }
694 
Layer()695 cc::Layer* Canvas2DLayerBridge::Layer() {
696   // Trigger lazy layer creation
697   GetOrCreateResourceProvider(kPreferAcceleration);
698   return layer_.get();
699 }
700 
DidDraw(const FloatRect &)701 void Canvas2DLayerBridge::DidDraw(const FloatRect& /* rect */) {
702   if (ResourceProvider() && ResourceProvider()->needs_flush())
703     FinalizeFrame();
704   have_recorded_draw_commands_ = true;
705 }
706 
FinalizeFrame()707 void Canvas2DLayerBridge::FinalizeFrame() {
708   TRACE_EVENT0("blink", "Canvas2DLayerBridge::FinalizeFrame");
709 
710   // Make sure surface is ready for painting: fix the rendering mode now
711   // because it will be too late during the paint invalidation phase.
712   if (!GetOrCreateResourceProvider(kPreferAcceleration))
713     return;
714 
715   FlushRecording();
716   if (is_being_displayed_) {
717     ++frames_since_last_commit_;
718     // Make sure the GPU is never more than two animation frames behind.
719     constexpr unsigned kMaxCanvasAnimationBacklog = 2;
720     if (frames_since_last_commit_ >=
721         static_cast<int>(kMaxCanvasAnimationBacklog)) {
722       if (IsAccelerated() && !rate_limiter_) {
723         rate_limiter_ = std::make_unique<SharedContextRateLimiter>(
724             kMaxCanvasAnimationBacklog);
725       }
726     }
727   }
728 
729   if (rate_limiter_)
730     rate_limiter_->Tick();
731 }
732 
DoPaintInvalidation(const FloatRect & dirty_rect)733 void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) {
734   if (layer_ && acceleration_mode_ != kDisableAcceleration)
735     layer_->SetNeedsDisplayRect(EnclosingIntRect(dirty_rect));
736 }
737 
NewImageSnapshot(AccelerationHint hint)738 scoped_refptr<StaticBitmapImage> Canvas2DLayerBridge::NewImageSnapshot(
739     AccelerationHint hint) {
740   if (snapshot_state_ == kInitialSnapshotState)
741     snapshot_state_ = kDidAcquireSnapshot;
742   if (IsHibernating())
743     return UnacceleratedStaticBitmapImage::Create(hibernation_image_);
744   if (!IsValid())
745     return nullptr;
746   // GetOrCreateResourceProvider needs to be called before FlushRecording, to
747   // make sure "hint" is properly taken into account, as well as after
748   // FlushRecording, in case the playback crashed the GPU context.
749   if (!GetOrCreateResourceProvider(hint))
750     return nullptr;
751   FlushRecording();
752   if (!GetOrCreateResourceProvider(hint))
753     return nullptr;
754   return ResourceProvider()->Snapshot();
755 }
756 
WillOverwriteCanvas()757 void Canvas2DLayerBridge::WillOverwriteCanvas() {
758   SkipQueuedDrawCommands();
759 }
760 
ReportHibernationEvent(HibernationEvent event)761 void Canvas2DLayerBridge::Logger::ReportHibernationEvent(
762     HibernationEvent event) {
763   UMA_HISTOGRAM_ENUMERATION("Blink.Canvas.HibernationEvents", event);
764 }
765 
766 }  // namespace blink
767