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