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