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); }