From 2a3b118991ac0ddd19d5bd5eacb150fc82a6f6b1 Mon Sep 17 00:00:00 2001 From: Jonah Chin Date: Thu, 20 Feb 2020 07:13:37 +0000 Subject: [PATCH] Add a PaintRecorder to CanvasResourceProvider This CL is the first step in creating a PaintRecord backed resource provider for canvas OOP-R. Changes of ownership: - PaintRecorder entirely moved from Canvas2DLayerBridge to CanvasResourceProvider. - Management of needs_flush_ for MemoryManagedPaintRecorder moved to CanvasResourceProvider. - RestoreCanvasMatrixClipStack logic centralized to CanvasResourceHost and can be accessed via callback - Backing SkiaPaintCanvas now private to CanvasResourceProvider with no external access. Any users of the backing SkiaPaintCanvas in Canvas2DLayerBridge had their functionality moved to CanvasResourceProvider (ie. new RestoreBackBuffer() function) Updates to test files: - Addition of explicit FlushCanvas() calls in tests that used to expect SkiaPaintCanvas to immediately update. - Some tests expected the CanvasResourceProvider to be created on first draw. This expectation has been changed since the CanvasResourceProvider now gets set up on SetCanvasResourceHost(). Other new functionality: - Display Item List now has a bit that tracks if it contains any draw operations. This is used to see if there are draw ops to flush. Bug: 1019288 Change-Id: I717b18e22d6699dc876d8f8121a25d147738579d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1891292 Commit-Queue: Khushal Reviewed-by: Aaron Krajeski Reviewed-by: Juanmi Huertas Reviewed-by: Fernando Serboncini Reviewed-by: Khushal Cr-Commit-Position: refs/heads/master@{#743002} --- cc/paint/display_item_list.cc | 1 + cc/paint/display_item_list.h | 4 + cc/paint/paint_recorder.cc | 4 + cc/paint/paint_recorder.h | 2 + .../core/html/canvas/html_canvas_element.cc | 24 ++-- .../canvas2d/base_rendering_context_2d.cc | 6 +- .../canvas2d/canvas_rendering_context_2d.cc | 6 +- .../canvas_rendering_context_2d_test.cc | 16 +-- .../offscreen_canvas_rendering_context_2d.cc | 66 +++-------- .../offscreen_canvas_rendering_context_2d.h | 9 +- .../graphics/canvas_2d_layer_bridge.cc | 103 ++++++------------ .../graphics/canvas_2d_layer_bridge.h | 29 ++--- .../graphics/canvas_2d_layer_bridge_test.cc | 60 +++++----- .../platform/graphics/canvas_resource_host.cc | 17 +++ .../platform/graphics/canvas_resource_host.h | 2 + .../graphics/canvas_resource_provider.cc | 83 ++++++++++---- .../graphics/canvas_resource_provider.h | 23 +++- .../graphics/canvas_resource_provider_test.cc | 2 + 18 files changed, 244 insertions(+), 213 deletions(-) diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc index 2c665340219a57..9429fef1569657 100644 --- a/cc/paint/display_item_list.cc +++ b/cc/paint/display_item_list.cc @@ -241,6 +241,7 @@ void DisplayItemList::Reset() { offsets_.shrink_to_fit(); begin_paired_indices_.clear(); begin_paired_indices_.shrink_to_fit(); + has_draw_ops_ = false; } sk_sp DisplayItemList::ReleaseAsRecord() { diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h index eefd3b69040136..7a7718168d55bb 100644 --- a/cc/paint/display_item_list.h +++ b/cc/paint/display_item_list.h @@ -87,6 +87,8 @@ class CC_PAINT_EXPORT DisplayItemList if (usage_hint_ == kTopLevelDisplayItemList) offsets_.push_back(offset); const T* op = paint_op_buffer_.push(std::forward(args)...); + if (op->IsDrawOp()) + has_draw_ops_ = true; DCHECK(op->IsValid()); return offset; } @@ -197,6 +199,7 @@ class CC_PAINT_EXPORT DisplayItemList int max_ops_to_analyze = 1); std::string ToString() const; + bool has_draw_ops() const { return has_draw_ops_; } private: friend class DisplayItemListTest; @@ -246,6 +249,7 @@ class CC_PAINT_EXPORT DisplayItemList #endif UsageHint usage_hint_; + bool has_draw_ops_ = false; friend class base::RefCountedThreadSafe; FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed); diff --git a/cc/paint/paint_recorder.cc b/cc/paint/paint_recorder.cc index 08f0bd9417fc8b..1fb95143f49a49 100644 --- a/cc/paint/paint_recorder.cc +++ b/cc/paint/paint_recorder.cc @@ -47,4 +47,8 @@ std::unique_ptr PaintRecorder::CreateCanvas( return std::make_unique(list, bounds); } +bool PaintRecorder::ListHasDrawOps() const { + return display_item_list_->has_draw_ops(); +} + } // namespace cc diff --git a/cc/paint/paint_recorder.h b/cc/paint/paint_recorder.h index b57fcb239113e5..6beb024c236987 100644 --- a/cc/paint/paint_recorder.h +++ b/cc/paint/paint_recorder.h @@ -37,6 +37,8 @@ class CC_PAINT_EXPORT PaintRecorder { sk_sp finishRecordingAsPicture(); + bool ListHasDrawOps() const; + protected: virtual std::unique_ptr CreateCanvas(DisplayItemList* list, const SkRect& bounds); diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc index 967ff28ec04a5b..9d6cf1ea388a7f 100644 --- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc +++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc @@ -140,11 +140,11 @@ void HTMLCanvasElement::Dispose() { if (context_) { UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.HasRendered", bool(ResourceProvider())); - if (ResourceProvider()) { + if (context_->Host()) { UMA_HISTOGRAM_BOOLEAN("Blink.Canvas.IsComposited", context_->IsComposited()); + context_->DetachHost(); } - context_->DetachHost(); context_ = nullptr; } @@ -1506,20 +1506,30 @@ void HTMLCanvasElement::ReplaceExisting2dLayerBridge( if (canvas2d_bridge_) { image = canvas2d_bridge_->NewImageSnapshot(kPreferNoAcceleration); // image can be null if allocation failed in which case we should just - // abort the surface switch to reatain the old surface which is still + // abort the surface switch to retain the old surface which is still // functional. if (!image) return; } - new_layer_bridge->SetCanvasResourceHost(this); ReplaceResourceProvider(nullptr); canvas2d_bridge_ = std::move(new_layer_bridge); + canvas2d_bridge_->SetCanvasResourceHost(this); + + cc::PaintCanvas* canvas = canvas2d_bridge_->GetPaintCanvas(); + // Paint canvas automatically has the clip re-applied. Since image already + // contains clip, it needs to be drawn before the clip stack is re-applied. + // Remove clip from canvas and restore it after the image is drawn. + canvas->restoreToCount(1); + canvas->save(); + + // TODO(jochin): Consider using ResourceProvider()->RestoreBackBuffer() here + // to avoid all of this clip stack manipulation. if (image) canvas2d_bridge_->DrawFullImage(image->PaintImageForCurrentFrame()); - RestoreCanvasMatrixClipStack(canvas2d_bridge_->GetPaintCanvas()); - canvas2d_bridge_->DidRestoreCanvasMatrixClipStack( - canvas2d_bridge_->GetPaintCanvas()); + RestoreCanvasMatrixClipStack(canvas); + canvas2d_bridge_->DidRestoreCanvasMatrixClipStack(canvas); + UpdateMemoryUsage(); } diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc index 7b0b4bfa2a7305..5b176570083022 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc @@ -50,6 +50,11 @@ void BaseRenderingContext2D::RealizeSaves() { ValidateStateStack(); if (GetState().HasUnrealizedSaves()) { DCHECK_GE(state_stack_.size(), 1u); + // GetOrCreatePaintCanvas() can call RestoreMatrixClipStack which syncs + // canvas to state_stack_. Get the canvas before adjusting state_stack_ to + // ensure canvas is synced prior to adjusting state_stack_. + cc::PaintCanvas* canvas = GetOrCreatePaintCanvas(); + // Reduce the current state's unrealized count by one now, // to reflect the fact we are saving one state. state_stack_.back()->Restore(); @@ -62,7 +67,6 @@ void BaseRenderingContext2D::RealizeSaves() { // state (in turn necessary to support correct resizing and unwinding of the // stack). state_stack_.back()->ResetUnrealizedSaveCount(); - cc::PaintCanvas* canvas = GetOrCreatePaintCanvas(); if (canvas) canvas->save(); ValidateStateStack(); diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc index 02caa1db2c01f9..d56313c7eac0cf 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc @@ -441,14 +441,14 @@ cc::PaintCanvas* CanvasRenderingContext2D::GetOrCreatePaintCanvas() { if (isContextLost()) return nullptr; if (canvas()->GetOrCreateCanvas2DLayerBridge()) - return GetPaintCanvas(); + return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas(); return nullptr; } cc::PaintCanvas* CanvasRenderingContext2D::GetPaintCanvas() const { - if (isContextLost()) + if (isContextLost() || !canvas()->GetCanvas2DLayerBridge()) return nullptr; - if (canvas() && canvas()->GetCanvas2DLayerBridge()) + if (canvas() && canvas()->GetCanvas2DLayerBridge()->ResourceProvider()) return canvas()->GetCanvas2DLayerBridge()->GetPaintCanvas(); return nullptr; } diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc index 3b75ce5f944461..bf6055d53f3c58 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc @@ -736,13 +736,10 @@ TEST_F(CanvasRenderingContext2DTest, size, CanvasColorParams(), kPreferNoAcceleration); fake_deaccelerate_surface->SetCanvasResourceHost(&host); - cc::PaintCanvas* paint_canvas_ptr = - fake_deaccelerate_surface->GetPaintCanvas(); FakeCanvas2DLayerBridge* surface_ptr = fake_deaccelerate_surface.get(); EXPECT_CALL(*fake_deaccelerate_surface, DrawFullImage(_)).Times(1); - EXPECT_CALL(*fake_deaccelerate_surface, - DidRestoreCanvasMatrixClipStack(paint_canvas_ptr)) + EXPECT_CALL(*fake_deaccelerate_surface, DidRestoreCanvasMatrixClipStack(_)) .Times(1); EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated()); @@ -1143,11 +1140,14 @@ TEST_F(CanvasRenderingContext2DTestAccelerated, CreateContext(kNonOpaque); IntSize size(300, 300); std::unique_ptr bridge = - MakeBridge(size, Canvas2DLayerBridge::kEnableAcceleration); + std::make_unique( + size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams()); // Force hibernatation to occur in an immediate task. bridge->DontUseIdleSchedulingForTesting(); CanvasElement().SetResourceProviderForTesting(nullptr, std::move(bridge), size); + CanvasElement().GetCanvas2DLayerBridge()->SetCanvasResourceHost( + canvas_element_); EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated()); // Take a snapshot to trigger lazy resource provider creation @@ -1188,12 +1188,14 @@ TEST_F(CanvasRenderingContext2DTestAccelerated, CreateContext(kNonOpaque); IntSize size(300, 300); std::unique_ptr bridge = - MakeBridge(size, Canvas2DLayerBridge::kEnableAcceleration); + std::make_unique( + size, Canvas2DLayerBridge::kEnableAcceleration, CanvasColorParams()); // Force hibernatation to occur in an immediate task. bridge->DontUseIdleSchedulingForTesting(); CanvasElement().SetResourceProviderForTesting(nullptr, std::move(bridge), size); - + CanvasElement().GetCanvas2DLayerBridge()->SetCanvasResourceHost( + canvas_element_); EXPECT_TRUE(CanvasElement().GetCanvas2DLayerBridge()->IsAccelerated()); EXPECT_TRUE(CanvasElement().GetLayoutBoxModelObject()); diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc index 811704201094e1..112234eb1618b0 100644 --- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc @@ -19,7 +19,6 @@ #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" -#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h" #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h" #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" @@ -86,20 +85,9 @@ OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D( bernoulli_distribution_(kUMASampleProbability) { is_valid_size_ = IsValidImageSize(Host()->Size()); - // A raw pointer is safe here because the callback is only used by the - // recorder_ - set_needs_flush_callback_ = - WTF::BindRepeating(&OffscreenCanvasRenderingContext2D::SetNeedsFlush, - WrapWeakPersistent(this)); - StartRecording(); - - // Clear the background transparent or opaque. Similar code at - // CanvasResourceProvider::Clear(). + // Clear the background transparent or opaque. if (IsCanvas2DBufferValid()) { - DCHECK(recorder_); - recorder_->getRecordingCanvas()->clear( - ColorParams().GetOpacityMode() == kOpaque ? SK_ColorBLACK - : SK_ColorTRANSPARENT); + GetOrCreateCanvasResourceProvider()->Clear(); DidDraw(); } @@ -131,29 +119,13 @@ void OffscreenCanvasRenderingContext2D::commit() { GetOffscreenFontCache().PruneLocalFontCache(kMaxCachedFonts); } -void OffscreenCanvasRenderingContext2D::StartRecording() { - recorder_ = - std::make_unique(set_needs_flush_callback_); - cc::PaintCanvas* canvas = recorder_->beginRecording(Width(), Height()); - // Always save an initial frame, to support resetting the top level matrix - // and clip. - canvas->save(); - RestoreMatrixClipStack(canvas); -} - void OffscreenCanvasRenderingContext2D::FlushRecording() { if (!have_recorded_draw_commands_) return; - { // Make a new scope so that PaintRecord gets deleted and that gets timed - CanvasResourceProvider* resource_provider = GetCanvasResourceProvider(); - cc::PaintCanvas* canvas = resource_provider->Canvas(); - canvas->drawPicture(recorder_->finishRecordingAsPicture()); - resource_provider->FlushSkia(); - } + GetCanvasResourceProvider()->FlushCanvas(); GetCanvasResourceProvider()->ReleaseLockedImages(); - StartRecording(); have_recorded_draw_commands_ = false; } @@ -165,7 +137,6 @@ void OffscreenCanvasRenderingContext2D::FinalizeFrame() { if (!GetOrCreateCanvasResourceProvider()) return; FlushRecording(); - needs_flush_ = false; } // BaseRenderingContext2D implementation @@ -210,7 +181,6 @@ OffscreenCanvasRenderingContext2D::GetCanvasResourceProvider() const { void OffscreenCanvasRenderingContext2D::Reset() { Host()->DiscardResourceProvider(); BaseRenderingContext2D::Reset(); - StartRecording(); // Because the host may have changed to a zero size is_valid_size_ = IsValidImageSize(Host()->Size()); } @@ -262,11 +232,7 @@ ImageBitmap* OffscreenCanvasRenderingContext2D::TransferToImageBitmap( return nullptr; } } - - // "Transfer" means no retained buffer. Matrix transformations need to be - // preserved though. Host()->DiscardResourceProvider(); - RestoreMatrixClipStack(recorder_->getRecordingCanvas()); return MakeGarbageCollected(std::move(image)); } @@ -293,17 +259,23 @@ bool OffscreenCanvasRenderingContext2D::ParseColorOrCurrentColor( return ::blink::ParseColorOrCurrentColor(color, color_string, nullptr); } +cc::PaintCanvas* OffscreenCanvasRenderingContext2D::GetOrCreatePaintCanvas() { + if (!is_valid_size_ || !GetOrCreateCanvasResourceProvider()) + return nullptr; + return GetPaintCanvas(); +} + cc::PaintCanvas* OffscreenCanvasRenderingContext2D::GetPaintCanvas() const { - if (!is_valid_size_) + if (!is_valid_size_ || !GetCanvasResourceProvider()) return nullptr; - return recorder_->getRecordingCanvas(); + return GetCanvasResourceProvider()->Canvas(); } void OffscreenCanvasRenderingContext2D::DidDraw() { have_recorded_draw_commands_ = true; dirty_rect_for_commit_.setWH(Width(), Height()); Host()->DidDraw(); - if (needs_flush_) + if (GetCanvasResourceProvider() && GetCanvasResourceProvider()->needs_flush()) FinalizeFrame(); } @@ -311,7 +283,7 @@ void OffscreenCanvasRenderingContext2D::DidDraw(const SkIRect& dirty_rect) { have_recorded_draw_commands_ = true; dirty_rect_for_commit_.join(dirty_rect); Host()->DidDraw(SkRect::Make(dirty_rect_for_commit_)); - if (needs_flush_) + if (GetCanvasResourceProvider() && GetCanvasResourceProvider()->needs_flush()) FinalizeFrame(); } @@ -369,13 +341,6 @@ bool OffscreenCanvasRenderingContext2D::WritePixels( DCHECK(IsPaintable()); FinalizeFrame(); have_recorded_draw_commands_ = false; - // Add a save to initialize the transform/clip stack and then restore it after - // the draw. This is needed because each recording initializes and the resets - // this state after every flush. - cc::PaintCanvas* canvas = GetCanvasResourceProvider()->Canvas(); - PaintCanvasAutoRestore auto_restore(canvas, true); - if (GetOrCreateCanvasResourceProvider()) - RestoreMatrixClipStack(canvas); return offscreenCanvasForBinding()->ResourceProvider()->WritePixels( orig_info, pixels, row_bytes, x, y); @@ -538,7 +503,7 @@ void OffscreenCanvasRenderingContext2D::DrawTextInternal( double y, CanvasRenderingContext2DState::PaintType paint_type, double* max_width) { - cc::PaintCanvas* paint_canvas = GetPaintCanvas(); + cc::PaintCanvas* paint_canvas = GetOrCreatePaintCanvas(); if (!paint_canvas) return; @@ -648,7 +613,4 @@ bool OffscreenCanvasRenderingContext2D::IsCanvas2DBufferValid() const { return false; } -void OffscreenCanvasRenderingContext2D::SetNeedsFlush() { - needs_flush_ = true; -} } // namespace blink diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h index 63fec778f45a3e..b99acfdee6dda9 100644 --- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h +++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h @@ -12,7 +12,6 @@ #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h" #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h" #include "third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" namespace blink { @@ -107,7 +106,7 @@ class MODULES_EXPORT OffscreenCanvasRenderingContext2D final bool ParseColorOrCurrentColor(Color&, const String& color_string) const final; - cc::PaintCanvas* GetOrCreatePaintCanvas() final { return GetPaintCanvas(); } + cc::PaintCanvas* GetOrCreatePaintCanvas() final; cc::PaintCanvas* GetPaintCanvas() const final; void DidDraw() final; @@ -139,8 +138,6 @@ class MODULES_EXPORT OffscreenCanvasRenderingContext2D final int y) override; private: - void StartRecording(); - std::unique_ptr recorder_; bool have_recorded_draw_commands_; void FinalizeFrame() final; void FlushRecording(); @@ -165,10 +162,6 @@ class MODULES_EXPORT OffscreenCanvasRenderingContext2D final std::mt19937 random_generator_; std::bernoulli_distribution bernoulli_distribution_; - - void SetNeedsFlush(); - base::RepeatingClosure set_needs_flush_callback_; - bool needs_flush_ = false; }; } // namespace blink diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc index 96a6f667a43b20..b2b976a900f31c 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.cc @@ -46,7 +46,6 @@ #include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" -#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h" #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h" #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" @@ -124,22 +123,6 @@ Canvas2DLayerBridge::Canvas2DLayerBridge(const IntSize& size, // Used by browser tests to detect the use of a Canvas2DLayerBridge. TRACE_EVENT_INSTANT0("test_gpu", "Canvas2DLayerBridgeCreation", TRACE_EVENT_SCOPE_GLOBAL); - - // A raw pointer is safe here because the callback is only used by the - // |recorder_|. - set_needs_flush_callback_ = WTF::BindRepeating( - &Canvas2DLayerBridge::SetNeedsFlush, WTF::Unretained(this)); - StartRecording(); - - // Clear the background transparent or opaque. Similar code at - // CanvasResourceProvider::Clear(). - if (IsValid()) { - DCHECK(recorder_); - recorder_->getRecordingCanvas()->clear( - color_params_.GetOpacityMode() == kOpaque ? SK_ColorBLACK - : SK_ColorTRANSPARENT); - DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height())); - } } Canvas2DLayerBridge::~Canvas2DLayerBridge() { @@ -162,18 +145,12 @@ Canvas2DLayerBridge::~Canvas2DLayerBridge() { layer_ = nullptr; } -void Canvas2DLayerBridge::StartRecording() { - recorder_ = - std::make_unique(set_needs_flush_callback_); - cc::PaintCanvas* canvas = - recorder_->beginRecording(size_.Width(), size_.Height()); +void Canvas2DLayerBridge::SetCanvasResourceHost(CanvasResourceHost* host) { + resource_host_ = host; - // Always save an initial frame, to support resetting the top level matrix - // and clip. - canvas->save(); - - if (resource_host_) - resource_host_->RestoreCanvasMatrixClipStack(canvas); + if (resource_host_ && GetOrCreateResourceProvider()) { + EnsureCleared(); + } } void Canvas2DLayerBridge::ResetResourceProvider() { @@ -352,9 +329,11 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( // circular callstack from HTMLCanvasElement. resource_provider = resource_host_->GetOrCreateCanvasResourceProviderImpl(adjusted_hint); - if (!resource_provider) + if (!resource_provider || !resource_provider->IsValid()) return nullptr; + EnsureCleared(); + if (IsAccelerated() && !layer_) { layer_ = cc::TextureLayer::CreateForMailbox(this); layer_->SetIsDrawable(true); @@ -392,9 +371,11 @@ CanvasResourceProvider* Canvas2DLayerBridge::GetOrCreateResourceProvider( return resource_provider; } -cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() const { +cc::PaintCanvas* Canvas2DLayerBridge::GetPaintCanvas() { DCHECK(resource_host_); - return recorder_->getRecordingCanvas(); + if (GetOrCreateResourceProvider()) + return ResourceProvider()->Canvas(); + return nullptr; } void Canvas2DLayerBridge::UpdateFilterQuality() { @@ -491,14 +472,6 @@ bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info, last_record_tainted_by_write_pixels_ = true; have_recorded_draw_commands_ = false; - // Apply clipstack to canvas_ and then restore it to original state once - // we leave this scope. This is needed because each recording initializes and - // resets this state after every flush. - cc::PaintCanvas* canvas = ResourceProvider()->Canvas(); - PaintCanvasAutoRestore auto_restore(canvas, true); - if (GetOrCreateResourceProvider()) { - resource_host_->RestoreCanvasMatrixClipStack(canvas); - } ResourceProvider()->WritePixels(orig_info, pixels, row_bytes, x, y); return true; @@ -506,8 +479,7 @@ bool Canvas2DLayerBridge::WritePixels(const SkImageInfo& orig_info, void Canvas2DLayerBridge::SkipQueuedDrawCommands() { if (have_recorded_draw_commands_) { - recorder_->finishRecordingAsPicture(); - StartRecording(); + ResourceProvider()->SkipQueuedDrawCommands(); have_recorded_draw_commands_ = false; } @@ -515,8 +487,19 @@ void Canvas2DLayerBridge::SkipQueuedDrawCommands() { rate_limiter_->Reset(); } -void Canvas2DLayerBridge::CalculateDirtyRegion(int canvas_width, - int canvas_height) { +void Canvas2DLayerBridge::EnsureCleared() { + if (cleared_) + return; + cleared_ = true; + ResourceProvider()->Clear(); + DidDraw(FloatRect(0.f, 0.f, size_.Width(), size_.Height())); +} + +void Canvas2DLayerBridge::CalculateDirtyRegion() { + // 1 pixel is added around the rect for anti-alias effect + // TODO(khushalsagar) : Remove the need for this 1 pixel addition. + int canvas_width = size_.Width() + 1; + int canvas_height = size_.Height() + 1; base::CheckedNumeric area(canvas_width); area *= canvas_height; if (!area.IsValid() || area.ValueOrDie() < kMinAreaForComputingDirtyRegion) @@ -656,24 +639,15 @@ void Canvas2DLayerBridge::FlushRecording() { } timer.emplace(); } - { // Make a new scope so that PaintRecord gets deleted and that gets timed - cc::PaintCanvas* canvas = ResourceProvider()->Canvas(); - last_recording_ = recorder_->finishRecordingAsPicture(); - SkScalar canvas_width = canvas->getLocalClipBounds().width(); - SkScalar canvas_height = canvas->getLocalClipBounds().height(); - DCHECK_GE(canvas_width, size_.Width()); - DCHECK_GE(canvas_height, size_.Height()); - if (will_measure) { - CalculateDirtyRegion(canvas_width, canvas_height); - } - canvas->drawPicture(last_recording_); - last_record_tainted_by_write_pixels_ = false; - if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) { - last_recording_ = nullptr; - clear_frame_ = false; - } - ResourceProvider()->FlushSkia(); + ResourceProvider()->FlushCanvas(); + last_recording_ = ResourceProvider()->last_recording(); + if (last_recording_ && will_measure) + CalculateDirtyRegion(); + last_record_tainted_by_write_pixels_ = false; + if (!clear_frame_ || !resource_host_ || !resource_host_->IsPrinting()) { + last_recording_ = nullptr; + clear_frame_ = false; } // Finish up the timing operation @@ -697,7 +671,6 @@ void Canvas2DLayerBridge::FlushRecording() { if (GetOrCreateResourceProvider()) ResourceProvider()->ReleaseLockedImages(); - StartRecording(); have_recorded_draw_commands_ = false; } @@ -819,7 +792,7 @@ cc::Layer* Canvas2DLayerBridge::Layer() { } void Canvas2DLayerBridge::DidDraw(const FloatRect& /* rect */) { - if (needs_flush_) + if (ResourceProvider() && ResourceProvider()->needs_flush()) FinalizeFrame(); have_recorded_draw_commands_ = true; } @@ -848,8 +821,6 @@ void Canvas2DLayerBridge::FinalizeFrame() { if (rate_limiter_) rate_limiter_->Tick(); - - needs_flush_ = false; } void Canvas2DLayerBridge::DoPaintInvalidation(const FloatRect& dirty_rect) { @@ -880,10 +851,6 @@ void Canvas2DLayerBridge::WillOverwriteCanvas() { SkipQueuedDrawCommands(); } -void Canvas2DLayerBridge::SetNeedsFlush() { - needs_flush_ = true; -} - void Canvas2DLayerBridge::Logger::ReportHibernationEvent( HibernationEvent event) { UMA_HISTOGRAM_ENUMERATION("Blink.Canvas.HibernationEvents", event); diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h index 6b39d2a4ac37d2..d295f573880099 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge.h @@ -45,7 +45,6 @@ #include "third_party/blink/renderer/platform/graphics/canvas_color_params.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_host.h" #include "third_party/blink/renderer/platform/graphics/graphics_types.h" -#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/deque.h" @@ -114,7 +113,8 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { virtual void DidRestoreCanvasMatrixClipStack(cc::PaintCanvas*) {} virtual bool IsAccelerated() const; - cc::PaintCanvas* GetPaintCanvas() const; + // This may recreate CanvasResourceProvider + cc::PaintCanvas* GetPaintCanvas(); bool IsValid(); bool WritePixels(const SkImageInfo&, const void* pixels, @@ -124,9 +124,7 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { void DontUseIdleSchedulingForTesting() { dont_use_idle_scheduling_for_testing_ = true; } - void SetCanvasResourceHost(CanvasResourceHost* host) { - resource_host_ = host; - } + void SetCanvasResourceHost(CanvasResourceHost* host); void Hibernate(); bool IsHibernating() const { return hibernation_image_ != nullptr; } @@ -138,14 +136,6 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { cc::TextureLayer* layer_for_testing() { return layer_.get(); } - // TODO(jochin): Remove this function completely once recorder_ has been - // moved into CanvasResourceProvider. - sk_sp record_for_testing() { - sk_sp record = recorder_->finishRecordingAsPicture(); - StartRecording(); - return record; - } - // The values of the enum entries must not change because they are used for // usage metrics histograms. New values can be added to the end. enum HibernationEvent { @@ -199,13 +189,12 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { bool CheckResourceProviderValid(); void ResetResourceProvider(); - void StartRecording(); void SkipQueuedDrawCommands(); + void EnsureCleared(); bool ShouldAccelerate(AccelerationHint) const; - void CalculateDirtyRegion(int canvas_width, int canvas_height); + void CalculateDirtyRegion(); - std::unique_ptr recorder_; sk_sp hibernation_image_; scoped_refptr layer_; std::unique_ptr rate_limiter_; @@ -256,11 +245,11 @@ class PLATFORM_EXPORT Canvas2DLayerBridge : public cc::TextureLayerClient { Deque pending_raster_timers_; sk_sp last_recording_; - cc::InvalidationRegion dirty_invalidate_region_; - void SetNeedsFlush(); - base::RepeatingClosure set_needs_flush_callback_; - bool needs_flush_ = false; + // This tracks whether the canvas has been cleared once after + // this bridge was created. + bool cleared_ = false; + cc::InvalidationRegion dirty_invalidate_region_; base::WeakPtrFactory weak_ptr_factory_{this}; diff --git a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc index a8ed0b5cd3c17c..7c955f9c3660bb 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_2d_layer_bridge_test.cc @@ -360,6 +360,13 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareIfContextLost) { EXPECT_FALSE(bridge->IsAccelerated()); } +void DrawSomething(Canvas2DLayerBridge* bridge) { + bridge->DidDraw(FloatRect(0, 0, 1, 1)); + bridge->FinalizeFrame(); + // Grabbing an image forces a flush + bridge->NewImageSnapshot(kPreferAcceleration); +} + TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) { { // No fallback case. @@ -380,14 +387,19 @@ TEST_F(Canvas2DLayerBridgeTest, FallbackToSoftwareOnFailedTextureAlloc) { ->ContextProvider() ->GetGrContext(); std::unique_ptr bridge = - MakeBridge(IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration, - CanvasColorParams()); + std::make_unique( + IntSize(300, 150), Canvas2DLayerBridge::kEnableAcceleration, + CanvasColorParams()); + bridge->DontUseIdleSchedulingForTesting(); EXPECT_TRUE(bridge->IsValid()); EXPECT_TRUE(bridge->IsAccelerated()); // We don't yet know that // allocation will fail. // This will cause SkSurface_Gpu creation to fail without // Canvas2DLayerBridge otherwise detecting that anything was disabled. gr->abandonContext(); + host_ = std::make_unique(IntSize(300, 150)); + bridge->SetCanvasResourceHost(host_.get()); + DrawSomething(bridge.get()); scoped_refptr snapshot = bridge->NewImageSnapshot(kPreferAcceleration); EXPECT_FALSE(bridge->IsAccelerated()); @@ -403,13 +415,6 @@ class MockLogger : public Canvas2DLayerBridge::Logger { ~MockLogger() override = default; }; -void DrawSomething(Canvas2DLayerBridge* bridge) { - bridge->DidDraw(FloatRect(0, 0, 1, 1)); - bridge->FinalizeFrame(); - // Grabbing an image forces a flush - bridge->NewImageSnapshot(kPreferAcceleration); -} - #if CANVAS2D_HIBERNATION_ENABLED TEST_F(Canvas2DLayerBridgeTest, HibernationLifeCycle) #else @@ -845,13 +850,17 @@ TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) { viz::TransferableResource resources[3]; std::unique_ptr callbacks[3]; + PaintFlags flags; std::unique_ptr bridge = MakeBridge( IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting, CanvasColorParams()); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0], &callbacks[0])); + + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1], &callbacks[1])); @@ -861,6 +870,7 @@ TEST_F(Canvas2DLayerBridgeTest, ResourceRecycling) { // Now release the first resource and draw again. It should be reused due to // recycling. callbacks[0]->Run(gpu::SyncToken(), false); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[2], &callbacks[2])); @@ -880,13 +890,16 @@ TEST_F(Canvas2DLayerBridgeTest, NoResourceRecyclingWhenPageHidden) { viz::TransferableResource resources[2]; std::unique_ptr callbacks[2]; + PaintFlags flags; std::unique_ptr bridge = MakeBridge( IntSize(300, 150), Canvas2DLayerBridge::kForceAccelerationForTesting, CanvasColorParams()); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[0], &callbacks[0])); + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); DrawSomething(bridge.get()); ASSERT_TRUE(bridge->PrepareTransferableResource(nullptr, &resources[1], &callbacks[1])); @@ -1002,17 +1015,14 @@ TEST_F(Canvas2DLayerBridgeTest, ImagesLockedUntilCacheLimit) { // First 2 images are budgeted, they should remain locked after the op. bridge->GetPaintCanvas()->drawImage(images[0].paint_image(), 0u, 0u, nullptr); bridge->GetPaintCanvas()->drawImage(images[1].paint_image(), 0u, 0u, nullptr); - // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_ - // to the resource provider. The following is a temp workaround. - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 2); // Next image is not budgeted, we should unlock all images other than the last // image. image_decode_cache_.set_budget_exceeded(true); bridge->GetPaintCanvas()->drawImage(images[2].paint_image(), 0u, 0u, nullptr); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 1); // Ask the provider to release everything, no locked images should remain. @@ -1033,10 +1043,7 @@ TEST_F(Canvas2DLayerBridgeTest, QueuesCleanupTaskForLockedImages) { SkMatrix::I(), 0u, color_params.GetStorageGfxColorSpace()); bridge->GetPaintCanvas()->drawImage(image.paint_image(), 0u, 0u, nullptr); - // TODO(jochin): Can just call provider::FlushSkia() once we move recorder_ - // to the resource provider. The following is a temp workaround. - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); - canvas->drawPicture(bridge->record_for_testing()); + bridge->GetOrCreateResourceProvider()->FlushCanvas(); EXPECT_EQ(image_decode_cache_.num_locked_images(), 1); base::RunLoop().RunUntilIdle(); @@ -1109,23 +1116,26 @@ TEST_F(Canvas2DLayerBridgeTest, WritePixelsRestoresClipStack) { std::move(host)); PaintFlags flags; + // MakeBridge() results in a call to restore the matrix. So we already have 1. EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), - 0); + 5); + // Drawline so WritePixels has something to flush + bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); + bridge->DidDraw(FloatRect(0, 0, 1, 1)); - cc::PaintCanvas* canvas = bridge->GetOrCreateResourceProvider()->Canvas(); + // WritePixels flushes recording. Post flush, a new drawing canvas is created + // that should have the matrix restored onto it. bridge->WritePixels(SkImageInfo::MakeN32Premul(10, 10), nullptr, 10, 0, 0); - // Recording canvas maintain clip stack, while underlying SkCanvas should not. - EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0); EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), 5); bridge->GetPaintCanvas()->drawLine(0, 0, 2, 2, flags); - // Flush recording. Recording canvas should maintain matrix, while SkCanvas - // should not. + // Standard flush recording. Post flush, a new drawing canvas is created that + // should have the matrix restored onto it. DrawSomething(bridge.get()); + EXPECT_EQ(bridge->GetPaintCanvas()->getTotalMatrix().get(SkMatrix::kMTransX), 5); - EXPECT_EQ(canvas->getTotalMatrix().get(SkMatrix::kMTransX), 0); } TEST_F(Canvas2DLayerBridgeTest, DisplayedCanvasIsRateLimited) { diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc index f7c20310f27986..d63606224c1dc6 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.cc @@ -19,6 +19,18 @@ CanvasResourceHost::ReplaceResourceProvider( std::move(resource_provider_); resource_provider_ = std::move(new_resource_provider); UpdateMemoryUsage(); + if (resource_provider_) { + resource_provider_->Canvas()->restoreToCount(1); + InitializeForRecording(resource_provider_->Canvas()); + // Using unretained here since CanvasResourceHost owns |resource_provider_| + // and is guaranteed to outlive it + resource_provider_->SetRestoreClipStackCallback(base::BindRepeating( + &CanvasResourceHost::InitializeForRecording, base::Unretained(this))); + } + if (old_resource_provider) { + old_resource_provider->SetRestoreClipStackCallback( + CanvasResourceProvider::RestoreMatrixClipStackCb()); + } return old_resource_provider; } @@ -27,4 +39,9 @@ void CanvasResourceHost::DiscardResourceProvider() { UpdateMemoryUsage(); } +void CanvasResourceHost::InitializeForRecording(cc::PaintCanvas* canvas) { + canvas->save(); + RestoreCanvasMatrixClipStack(canvas); +} + } // namespace blink diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h index 9c1e69a63b4613..16dbe14e1697e5 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_host.h +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_host.h @@ -43,6 +43,8 @@ class PLATFORM_EXPORT CanvasResourceHost { virtual bool IsPrinting() const { return false; } private: + void InitializeForRecording(cc::PaintCanvas* canvas); + std::unique_ptr resource_provider_; }; diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc index 2c855dda1eb6b5..bcbd2a5266fc9e 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc @@ -17,9 +17,11 @@ #include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" #include "gpu/config/gpu_feature_info.h" +#include "third_party/blink/public/common/features.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" +#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h" #include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h" #include "third_party/skia/include/core/SkSurface.h" @@ -229,6 +231,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { if (IsGpuContextLost()) return nullptr; + FlushCanvas(); // Its important to end read access and ref the resource before the WillDraw // call below. Since it relies on resource ref-count to trigger // copy-on-write and asserts that we only have write access when the @@ -264,6 +267,7 @@ class CanvasResourceProviderSharedImage : public CanvasResourceProvider { return SnapshotInternal(orientation); if (!cached_snapshot_) { + FlushCanvas(); EndWriteAccess(); cached_snapshot_ = resource_->Bitmap(); } @@ -562,8 +566,8 @@ class CanvasResourceProviderSwapChain final : public CanvasResourceProvider { "CanvasResourceProviderSwapChain::ProduceCanvasResource"); if (!IsValid()) return nullptr; + FlushCanvas(); if (dirty_) { - FlushSkia(); resource_->PresentSwapChain(); dirty_ = false; } @@ -1022,11 +1026,11 @@ SkSurface* CanvasResourceProvider::GetSkSurface() const { return surface_.get(); } -void CanvasResourceProvider::InitializePaintCanvas() { - // Since canvas_ has a reference to canvas_image_provider_, canvas must be - // deleted before the image_provider. - canvas_ = nullptr; - canvas_image_provider_ = nullptr; +void CanvasResourceProvider::EnsureSkiaCanvas() { + WillDraw(); + + if (skia_canvas_) + return; // Create an ImageDecodeCache for half float images only if the canvas is // using half float back storage. @@ -1046,26 +1050,27 @@ void CanvasResourceProvider::InitializePaintCanvas() { context_flushes.enable = true; context_flushes.max_draws_before_flush = kMaxDrawsBeforeContextFlush; } - canvas_ = std::make_unique(GetSkSurface()->getCanvas(), - canvas_image_provider_.get(), - context_flushes); + skia_canvas_ = std::make_unique( + GetSkSurface()->getCanvas(), canvas_image_provider_.get(), + context_flushes); } cc::PaintCanvas* CanvasResourceProvider::Canvas() { - WillDraw(); + if (!recorder_) { + // A raw pointer is safe here because the callback is only used by the + // |recorder_|. + recorder_ = std::make_unique(WTF::BindRepeating( + &CanvasResourceProvider::SetNeedsFlush, WTF::Unretained(this))); - if (!canvas_) { - TRACE_EVENT0("blink", "CanvasResourceProvider::Canvas"); - DCHECK(!canvas_image_provider_); - InitializePaintCanvas(); + return recorder_->beginRecording(Size().Width(), Size().Height()); } - return canvas_.get(); + return recorder_->getRecordingCanvas(); } void CanvasResourceProvider::OnContextDestroyed() { if (canvas_image_provider_) { - DCHECK(canvas_); - canvas_->reset_image_provider(); + DCHECK(skia_canvas_); + skia_canvas_->reset_image_provider(); canvas_image_provider_.reset(); } } @@ -1087,6 +1092,7 @@ scoped_refptr CanvasResourceProvider::SnapshotInternal( } cc::PaintImage CanvasResourceProvider::MakeImageSnapshot() { + FlushCanvas(); auto sk_image = GetSkSurface()->makeImageSnapshot(); if (!sk_image) return cc::PaintImage(); @@ -1126,8 +1132,18 @@ GrContext* CanvasResourceProvider::GetGrContext() const { return context_provider_wrapper_->ContextProvider()->GetGrContext(); } -void CanvasResourceProvider::FlushSkia() const { +void CanvasResourceProvider::FlushCanvas() { + if (!recorder_ || !recorder_->ListHasDrawOps()) + return; + EnsureSkiaCanvas(); + last_recording_ = recorder_->finishRecordingAsPicture(); + skia_canvas_->drawPicture(last_recording_); + cc::PaintCanvas* canvas = + recorder_->beginRecording(Size().Width(), Size().Height()); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(canvas); GetSkSurface()->flush(); + needs_flush_ = false; } bool CanvasResourceProvider::IsGpuContextLost() const { @@ -1144,6 +1160,18 @@ bool CanvasResourceProvider::WritePixels(const SkImageInfo& orig_info, TRACE_EVENT0("blink", "CanvasResourceProvider::WritePixels"); DCHECK(IsValid()); + DCHECK(!recorder_->ListHasDrawOps()); + + EnsureSkiaCanvas(); + + // Apply clipstack to skia_canvas_ and then restore it to original state once + // we leave this scope. This is needed because each recording initializes and + // resets this state after every flush. restore_clip_stack_callback_ sets the + // initial save required for a restore. + cc::PaintCanvasAutoRestore auto_restore(skia_canvas_.get(), false); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(skia_canvas_.get()); + return GetSkSurface()->getCanvas()->writePixels(orig_info, pixels, row_bytes, x, y); } @@ -1253,12 +1281,29 @@ scoped_refptr CanvasResourceProvider::GetImportedResource() return canvas_resources_.back(); } +void CanvasResourceProvider::SkipQueuedDrawCommands() { + if (!recorder_) + return; + recorder_->finishRecordingAsPicture(); + cc::PaintCanvas* canvas = + recorder_->beginRecording(Size().Width(), Size().Height()); + if (restore_clip_stack_callback_) + restore_clip_stack_callback_.Run(canvas); +} + +void CanvasResourceProvider::SetRestoreClipStackCallback( + RestoreMatrixClipStackCb callback) { + DCHECK(restore_clip_stack_callback_.is_null() || callback.is_null()); + restore_clip_stack_callback_ = std::move(callback); +} + void CanvasResourceProvider::RestoreBackBuffer(const cc::PaintImage& image) { DCHECK_EQ(image.height(), Size().Height()); DCHECK_EQ(image.width(), Size().Width()); + EnsureSkiaCanvas(); cc::PaintFlags copy_paint; copy_paint.setBlendMode(SkBlendMode::kSrc); - Canvas()->drawImage(image, 0, 0, ©_paint); + skia_canvas_->drawImage(image, 0, 0, ©_paint); } } // namespace blink diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h index f6787de36f02dd..1427ccff104bb2 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h @@ -9,6 +9,7 @@ #include "cc/raster/playback_image_provider.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" #include "third_party/blink/renderer/platform/graphics/image_orientation.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h" #include "third_party/skia/include/core/SkSurface.h" class GrContext; @@ -93,6 +94,9 @@ class PLATFORM_EXPORT CanvasResourceProvider kMaxValue = kSwapChain, }; + using RestoreMatrixClipStackCb = + base::RepeatingCallback; + void static RecordTypeToUMA(ResourceProviderType type); // TODO(juanmihd): Clean up creation methods/usage. See crbug.com/1035589. @@ -138,9 +142,8 @@ class PLATFORM_EXPORT CanvasResourceProvider void OnContextDestroyed() override; cc::PaintCanvas* Canvas(); - void InitializePaintCanvas(); void ReleaseLockedImages(); - void FlushSkia() const; + void FlushCanvas(); const CanvasColorParams& ColorParams() const { return color_params_; } void SetFilterQuality(SkFilterQuality quality) { filter_quality_ = quality; } const IntSize& Size() const { return size_; } @@ -210,6 +213,12 @@ class PLATFORM_EXPORT CanvasResourceProvider return canvas_resources_.size(); } + void SkipQueuedDrawCommands(); + const sk_sp& last_recording() const { + return last_recording_; + } + void SetRestoreClipStackCallback(RestoreMatrixClipStackCb); + bool needs_flush() const { return needs_flush_; } void RestoreBackBuffer(const cc::PaintImage&); protected: @@ -261,6 +270,8 @@ class PLATFORM_EXPORT CanvasResourceProvider cc::ImageDecodeCache* ImageDecodeCacheRGBA8(); cc::ImageDecodeCache* ImageDecodeCacheF16(); + void EnsureSkiaCanvas(); + void SetNeedsFlush() { needs_flush_ = true; } base::WeakPtr context_provider_wrapper_; base::WeakPtr resource_dispatcher_; @@ -270,7 +281,11 @@ class PLATFORM_EXPORT CanvasResourceProvider const CanvasColorParams color_params_; const bool is_origin_top_left_; std::unique_ptr canvas_image_provider_; - std::unique_ptr canvas_; + std::unique_ptr skia_canvas_; + std::unique_ptr recorder_; + sk_sp last_recording_; + + bool needs_flush_ = false; const cc::PaintImage::Id snapshot_paint_image_id_; cc::PaintImage::ContentId snapshot_paint_image_content_id_ = @@ -289,6 +304,8 @@ class PLATFORM_EXPORT CanvasResourceProvider // underlying GrContext is flushed. static constexpr int kMaxDrawsBeforeContextFlush = 50; + RestoreMatrixClipStackCb restore_clip_stack_callback_; + base::WeakPtrFactory weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(CanvasResourceProvider); diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc index 88527710512ec9..53bd355e03d4f8 100644 --- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc @@ -246,6 +246,7 @@ TEST_F(CanvasResourceProviderTest, // Resource updated after draw. provider->Canvas()->clear(SK_ColorWHITE); + provider->FlushCanvas(); new_image = provider->Snapshot(); EXPECT_NE(new_image->GetMailboxHolder().mailbox, image->GetMailboxHolder().mailbox); @@ -254,6 +255,7 @@ TEST_F(CanvasResourceProviderTest, auto original_mailbox = image->GetMailboxHolder().mailbox; image.reset(); provider->Canvas()->clear(SK_ColorBLACK); + provider->FlushCanvas(); EXPECT_EQ(original_mailbox, provider->Snapshot()->GetMailboxHolder().mailbox); }