Commit 2eb27bd0 authored by Aaron Krajeski's avatar Aaron Krajeski Committed by Commit Bot

Add pre and post finalize functions to canvas hosts

For low latency canvases the order of events is important. The resource
must get single buffered before it is drawn. Because drawing is
something that the rendering context owns, it makes sense to split up
the parts of finalizing the frame on the host.

Bug: 1002523
Change-Id: Ie57965c4394d25f4197b4683bc238670edf1693d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1848934Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Reviewed-by: default avatarKai Ninomiya <kainino@chromium.org>
Commit-Queue: Aaron Krajeski <aaronhk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#704640}
parent 711a5e45
...@@ -127,7 +127,10 @@ void CanvasRenderingContext::DidProcessTask( ...@@ -127,7 +127,10 @@ void CanvasRenderingContext::DidProcessTask(
// The end of a script task that drew content to the canvas is the point // The end of a script task that drew content to the canvas is the point
// at which the current frame may be considered complete. // at which the current frame may be considered complete.
if (Host()) if (Host())
Host()->FinalizeFrame(); Host()->PreFinalizeFrame();
FinalizeFrame();
if (Host())
Host()->PostFinalizeFrame();
} }
CanvasRenderingContext::ContextType CanvasRenderingContext::ContextTypeFromId( CanvasRenderingContext::ContextType CanvasRenderingContext::ContextTypeFromId(
......
...@@ -94,7 +94,7 @@ class CORE_EXPORT CanvasRenderingContext : public ScriptWrappable, ...@@ -94,7 +94,7 @@ class CORE_EXPORT CanvasRenderingContext : public ScriptWrappable,
virtual bool IsOriginTopLeft() const { virtual bool IsOriginTopLeft() const {
// Canvas contexts have the origin of coordinates on the top left corner. // Canvas contexts have the origin of coordinates on the top left corner.
// Accelerated resources (e.g. GPU textures) have their origin of // Accelerated resources (e.g. GPU textures) have their origin of
// coordinates in the uppper left corner. // coordinates in the upper left corner.
return !IsAccelerated(); return !IsAccelerated();
} }
virtual bool ShouldAntialias() const { return false; } virtual bool ShouldAntialias() const { return false; }
......
...@@ -46,7 +46,8 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost, ...@@ -46,7 +46,8 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
virtual void DidDraw(const FloatRect& rect) = 0; virtual void DidDraw(const FloatRect& rect) = 0;
virtual void DidDraw() = 0; virtual void DidDraw() = 0;
virtual void FinalizeFrame() = 0; virtual void PreFinalizeFrame() = 0;
virtual void PostFinalizeFrame() = 0;
virtual bool PushFrame(scoped_refptr<CanvasResource> frame, virtual bool PushFrame(scoped_refptr<CanvasResource> frame,
const SkIRect& damage_rect) = 0; const SkIRect& damage_rect) = 0;
virtual bool OriginClean() const = 0; virtual bool OriginClean() const = 0;
...@@ -96,7 +97,7 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost, ...@@ -96,7 +97,7 @@ class CORE_EXPORT CanvasRenderingContextHost : public CanvasResourceHost,
CanvasColorParams ColorParams() const; CanvasColorParams ColorParams() const;
// For deferred canvases this will have the side effect of drawing recorded // For deferred canvases this will have the side effect of drawing recorded
// commands in order to finalize the frame // commands in order to finalize the frame.
ScriptPromise convertToBlob(ScriptState*, ScriptPromise convertToBlob(ScriptState*,
const ImageEncodeOptions*, const ImageEncodeOptions*,
ExceptionState&); ExceptionState&);
......
...@@ -393,43 +393,35 @@ void HTMLCanvasElement::DidDraw() { ...@@ -393,43 +393,35 @@ void HTMLCanvasElement::DidDraw() {
DidDraw(FloatRect(0, 0, Size().Width(), Size().Height())); DidDraw(FloatRect(0, 0, Size().Width(), Size().Height()));
} }
void HTMLCanvasElement::FinalizeFrame() { void HTMLCanvasElement::PreFinalizeFrame() {
TRACE_EVENT0("blink", "HTMLCanvasElement::FinalizeFrame");
RecordCanvasSizeToUMA(size_); RecordCanvasSizeToUMA(size_);
// FinalizeFrame indicates the end of a script task that may have rendered // PreFinalizeFrame indicates the end of a script task that may have rendered
// into the canvas, now is a good time to unlock cache entries. // into the canvas, now is a good time to unlock cache entries.
auto* resource_provider = ResourceProvider(); auto* resource_provider = ResourceProvider();
if (resource_provider) if (resource_provider)
resource_provider->ReleaseLockedImages(); resource_provider->ReleaseLockedImages();
// Low-latency 2d canvases will produce their frames after the resource gets // Low-latency 2d canvases produce their frames after the resource gets
// single buffered // single buffered.
if (context_ && !(Is2d() && LowLatencyEnabled()))
context_->FinalizeFrame();
if (LowLatencyEnabled() && !dirty_rect_.IsEmpty()) { if (LowLatencyEnabled() && !dirty_rect_.IsEmpty()) {
if (GetOrCreateCanvasResourceProvider(kPreferAcceleration)) { if (GetOrCreateCanvasResourceProvider(kPreferAcceleration)) {
const bool webgl_overlay_enabled = const bool webgl_overlay_enabled =
RuntimeEnabledFeatures::WebGLImageChromiumEnabled() || RuntimeEnabledFeatures::WebGLImageChromiumEnabled() ||
context_->UsingSwapChain(); context_->UsingSwapChain();
// TryEnableSingleBuffering() the first time we FinalizeFrame(). // TryEnableSingleBuffering() the first time we finalize a frame.
if (!ResourceProvider()->IsSingleBuffered()) { if (!ResourceProvider()->IsSingleBuffered()) {
ResourceProvider()->TryEnableSingleBuffering(); ResourceProvider()->TryEnableSingleBuffering();
if (Is3d() && webgl_overlay_enabled) if (Is3d() && webgl_overlay_enabled)
context_->ProvideBackBufferToResourceProvider(); context_->ProvideBackBufferToResourceProvider();
} }
}
}
}
// TODO(aaronhk) This should just be a generic call to finalize frame void HTMLCanvasElement::PostFinalizeFrame() {
// but the pixel 2 bot hanging prevents it (crbug.com/1002946) if (LowLatencyEnabled() && !dirty_rect_.IsEmpty()) {
if (canvas2d_bridge_) { if (GetOrCreateCanvasResourceProvider(kPreferAcceleration)) {
canvas2d_bridge_->FlushRecording();
} else {
DCHECK(Is3d());
if (!webgl_overlay_enabled)
context_->PaintRenderingResultsToCanvas(kBackBuffer);
}
const base::TimeTicks start_time = base::TimeTicks::Now(); const base::TimeTicks start_time = base::TimeTicks::Now();
const scoped_refptr<CanvasResource> canvas_resource = const scoped_refptr<CanvasResource> canvas_resource =
ResourceProvider()->ProduceCanvasResource(); ResourceProvider()->ProduceCanvasResource();
......
...@@ -185,7 +185,8 @@ class CORE_EXPORT HTMLCanvasElement final ...@@ -185,7 +185,8 @@ class CORE_EXPORT HTMLCanvasElement final
void DoDeferredPaintInvalidation(); void DoDeferredPaintInvalidation();
void FinalizeFrame() override; void PreFinalizeFrame() override;
void PostFinalizeFrame() override;
CanvasResourceDispatcher* GetOrCreateResourceDispatcher() override; CanvasResourceDispatcher* GetOrCreateResourceDispatcher() override;
......
...@@ -111,7 +111,8 @@ class CORE_EXPORT OffscreenCanvas final ...@@ -111,7 +111,8 @@ class CORE_EXPORT OffscreenCanvas final
} }
// CanvasRenderingContextHost implementation. // CanvasRenderingContextHost implementation.
void FinalizeFrame() override {} void PreFinalizeFrame() override {}
void PostFinalizeFrame() override {}
void DetachContext() override { context_ = nullptr; } void DetachContext() override { context_ = nullptr; }
CanvasRenderingContext* RenderingContext() const override { return context_; } CanvasRenderingContext* RenderingContext() const override { return context_; }
......
...@@ -85,7 +85,9 @@ TEST_P(HTMLCanvasPainterTestForCAP, Canvas2DLayerAppearsInLayerTree) { ...@@ -85,7 +85,9 @@ TEST_P(HTMLCanvasPainterTestForCAP, Canvas2DLayerAppearsInLayerTree) {
ASSERT_TRUE(element->IsAccelerated()); ASSERT_TRUE(element->IsAccelerated());
// Force the page to paint. // Force the page to paint.
element->FinalizeFrame(); element->PreFinalizeFrame();
context->FinalizeFrame();
element->PostFinalizeFrame();
UpdateAllLifecyclePhasesForTest(); UpdateAllLifecyclePhasesForTest();
// Fetch the layer associated with the <canvas>, and check that it was // Fetch the layer associated with the <canvas>, and check that it was
......
...@@ -672,6 +672,7 @@ scoped_refptr<StaticBitmapImage> blink::CanvasRenderingContext2D::GetImage( ...@@ -672,6 +672,7 @@ scoped_refptr<StaticBitmapImage> blink::CanvasRenderingContext2D::GetImage(
} }
void CanvasRenderingContext2D::FinalizeFrame() { void CanvasRenderingContext2D::FinalizeFrame() {
TRACE_EVENT0("blink", "CanvasRenderingContext2D::FinalizeFrame");
if (canvas() && canvas()->GetCanvas2DLayerBridge()) if (canvas() && canvas()->GetCanvas2DLayerBridge())
canvas()->GetCanvas2DLayerBridge()->FinalizeFrame(); canvas()->GetCanvas2DLayerBridge()->FinalizeFrame();
usage_counters_.num_frames_since_reset++; usage_counters_.num_frames_since_reset++;
......
...@@ -116,7 +116,9 @@ class CanvasRenderingContext2DTest : public ::testing::Test { ...@@ -116,7 +116,9 @@ class CanvasRenderingContext2DTest : public ::testing::Test {
} }
void DrawSomething() { void DrawSomething() {
CanvasElement().DidDraw(); CanvasElement().DidDraw();
CanvasElement().FinalizeFrame(); CanvasElement().PreFinalizeFrame();
Context2d()->FinalizeFrame();
CanvasElement().PostFinalizeFrame();
// Grabbing an image forces a flush // Grabbing an image forces a flush
CanvasElement().Snapshot(kBackBuffer, kPreferAcceleration); CanvasElement().Snapshot(kBackBuffer, kPreferAcceleration);
} }
......
...@@ -145,7 +145,9 @@ TEST_P(HTMLCanvasElementModuleTest, LowLatencyCanvasCompositorFrameOpacity) { ...@@ -145,7 +145,9 @@ TEST_P(HTMLCanvasElementModuleTest, LowLatencyCanvasCompositorFrameOpacity) {
EXPECT_NE(shared_quad_state_list.front()->are_contents_opaque, EXPECT_NE(shared_quad_state_list.front()->are_contents_opaque,
context_alpha); context_alpha);
}))); })));
canvas_element().FinalizeFrame(); canvas_element().PreFinalizeFrame();
context_->FinalizeFrame();
canvas_element().PostFinalizeFrame();
platform->RunUntilIdle(); platform->RunUntilIdle();
} }
......
...@@ -43,7 +43,7 @@ class MODULES_EXPORT ImageBitmapRenderingContextBase ...@@ -43,7 +43,7 @@ class MODULES_EXPORT ImageBitmapRenderingContextBase
void SetIsHidden(bool) override {} void SetIsHidden(bool) override {}
bool isContextLost() const override { return false; } bool isContextLost() const override { return false; }
void SetImage(ImageBitmap*); void SetImage(ImageBitmap*);
// The acceleration hing here is ignored as GetImage(AccelerationHint) only // The acceleration hint here is ignored as GetImage(AccelerationHint) only
// calls to image_layer_bridge->GetImage(), without giving it a hint // calls to image_layer_bridge->GetImage(), without giving it a hint
scoped_refptr<StaticBitmapImage> GetImage(AccelerationHint) final; scoped_refptr<StaticBitmapImage> GetImage(AccelerationHint) final;
// This function resets the internal image resource to a image of the same // This function resets the internal image resource to a image of the same
......
...@@ -1410,6 +1410,12 @@ void WebGLRenderingContextBase::FinalizeFrame() { ...@@ -1410,6 +1410,12 @@ void WebGLRenderingContextBase::FinalizeFrame() {
if (GetDrawingBuffer() && GetDrawingBuffer()->UsingSwapChain()) if (GetDrawingBuffer() && GetDrawingBuffer()->UsingSwapChain())
GetDrawingBuffer()->PresentSwapChain(); GetDrawingBuffer()->PresentSwapChain();
marked_canvas_dirty_ = false; marked_canvas_dirty_ = false;
// For low-latency canvases
const bool webgl_overlay_enabled =
RuntimeEnabledFeatures::WebGLImageChromiumEnabled() || UsingSwapChain();
if (canvas() && canvas()->LowLatencyEnabled() && !webgl_overlay_enabled)
PaintRenderingResultsToCanvas(kBackBuffer);
} }
void WebGLRenderingContextBase::OnErrorMessage(const char* message, void WebGLRenderingContextBase::OnErrorMessage(const char* message,
......
...@@ -732,11 +732,9 @@ void Canvas2DLayerBridge::FinalizeFrame() { ...@@ -732,11 +732,9 @@ void Canvas2DLayerBridge::FinalizeFrame() {
if (!GetOrCreateResourceProvider(kPreferAcceleration)) if (!GetOrCreateResourceProvider(kPreferAcceleration))
return; return;
FlushRecording();
++frames_since_last_commit_; ++frames_since_last_commit_;
if (frames_since_last_commit_ >= 2) { if (frames_since_last_commit_ >= 2) {
// TODO(aaronhk) Ideally we'd want to call FlushRecording() here, but this
// causes webview to hang for pixel 2. See crbug.com/1002946
ResourceProvider()->FlushSkia();
if (IsAccelerated() && !rate_limiter_) { if (IsAccelerated() && !rate_limiter_) {
// Make sure the GPU is never more than two animation frames behind. // Make sure the GPU is never more than two animation frames behind.
constexpr unsigned kMaxCanvasAnimationBacklog = 2; constexpr unsigned kMaxCanvasAnimationBacklog = 2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment