Commit 5c4bc1ff authored by Justin Novosad's avatar Justin Novosad Committed by Commit Bot

Add a single buffered mode to CanvasResourceProvider

This CL completes the initial implementation of Low Latency mode for 2d
canvases

Bug: 788439
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I58fcd3fe4ab19fa10f2876f3da4f078fd4fbc2a9
Reviewed-on: https://chromium-review.googlesource.com/1145235
Commit-Queue: Justin Novosad <junov@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#576962}
parent 48e4f9d4
......@@ -415,9 +415,13 @@ void HTMLCanvasElement::FinalizeFrame() {
if (!LowLatencyEnabled())
canvas2d_bridge_->FinalizeFrame();
}
if (LowLatencyEnabled() && !dirty_rect_.IsEmpty() &&
GetOrCreateCanvasResourceProvider(kPreferAcceleration)) {
if (LowLatencyEnabled() && !dirty_rect_.IsEmpty()) {
AccelerationHint hint =
Is2d() ? kPreferNoAcceleration : kPreferAcceleration;
if (GetOrCreateCanvasResourceProvider(hint)) {
ResourceProvider()->TryEnableSingleBuffering();
// Push a frame
base::TimeTicks start_time = WTF::CurrentTimeTicks();
scoped_refptr<CanvasResource> canvas_resource =
......@@ -1138,9 +1142,8 @@ void HTMLCanvasElement::SetCanvas2DLayerBridgeForTesting(
scoped_refptr<Image> HTMLCanvasElement::CopiedImage(
SourceDrawingBuffer source_buffer,
AccelerationHint hint) {
if (SurfaceLayerBridge()) {
if (PlaceholderFrame())
return PlaceholderFrame()->Bitmap();
}
if (!IsPaintable())
return nullptr;
......
......@@ -120,16 +120,15 @@ class CanvasRenderingContext2DTest : public PageTestBase {
return CanvasElement().GetGPUMemoryUsage();
}
void DrawSomething() {
Canvas2DLayerBridge* bridge = CanvasElement().GetCanvas2DLayerBridge();
bridge->DidDraw(FloatRect(0, 0, 1, 1));
bridge->FinalizeFrame();
CanvasElement().DidDraw();
CanvasElement().FinalizeFrame();
// Grabbing an image forces a flush
bridge->NewImageSnapshot(kPreferAcceleration);
CanvasElement().CopiedImage(kBackBuffer, kPreferAcceleration);
}
void CreateContext(OpacityMode,
String color_space = String(),
LinearPixelMathState = kLinearPixelMathDisabled);
enum LatencyMode { kNormalLatency, kLowLatency };
void CreateContext(OpacityMode, LatencyMode = kNormalLatency);
ScriptState* GetScriptState() {
return ToScriptStateForMainWorld(canvas_element_->GetFrame());
}
......@@ -185,19 +184,12 @@ CanvasRenderingContext2DTest::CanvasRenderingContext2DTest()
opaque_bitmap_(IntSize(10, 10), kOpaqueBitmap),
alpha_bitmap_(IntSize(10, 10), kTransparentBitmap) {}
void CanvasRenderingContext2DTest::CreateContext(
OpacityMode opacity_mode,
String color_space,
LinearPixelMathState LinearPixelMath_state) {
void CanvasRenderingContext2DTest::CreateContext(OpacityMode opacity_mode,
LatencyMode latency_mode) {
String canvas_type("2d");
CanvasContextCreationAttributesCore attributes;
attributes.alpha = opacity_mode == kNonOpaque;
if (!color_space.IsEmpty()) {
attributes.color_space = color_space;
if (LinearPixelMath_state == kLinearPixelMathEnabled) {
attributes.pixel_format = "float16";
}
}
attributes.low_latency = latency_mode == kLowLatency;
canvas_element_->GetCanvasRenderingContext(canvas_type, attributes);
}
......@@ -1209,4 +1201,22 @@ TEST_F(CanvasRenderingContext2DTestWithTestingPlatform,
EXPECT_FALSE(layer->NeedsCompositingInputsUpdate());
}
TEST_F(CanvasRenderingContext2DTest, LowLatencyIsSingleBuffered) {
CreateContext(kNonOpaque, kLowLatency);
// No need to set-up the layer bridge when testing low latency mode.
DrawSomething();
auto frame1_resource =
CanvasElement()
.GetOrCreateCanvasResourceProvider(kPreferNoAcceleration)
->ProduceFrame();
EXPECT_TRUE(!!frame1_resource);
DrawSomething();
auto frame2_resource =
CanvasElement()
.GetOrCreateCanvasResourceProvider(kPreferNoAcceleration)
->ProduceFrame();
EXPECT_TRUE(!!frame2_resource);
EXPECT_EQ(frame1_resource.get(), frame2_resource.get());
}
} // namespace blink
......@@ -152,6 +152,7 @@ class CanvasResourceProviderTextureGpuMemoryBuffer final
~CanvasResourceProviderTextureGpuMemoryBuffer() override = default;
bool SupportsDirectCompositing() const override { return true; }
bool SupportsSingleBuffering() const override { return true; }
private:
scoped_refptr<CanvasResource> CreateResource() final {
......@@ -263,6 +264,7 @@ class CanvasResourceProviderRamGpuMemoryBuffer final
~CanvasResourceProviderRamGpuMemoryBuffer() override = default;
bool SupportsDirectCompositing() const override { return true; }
bool SupportsSingleBuffering() const override { return true; }
private:
scoped_refptr<CanvasResource> CreateResource() final {
......@@ -318,6 +320,7 @@ class CanvasResourceProviderSharedBitmap : public CanvasResourceProviderBitmap {
}
~CanvasResourceProviderSharedBitmap() override = default;
bool SupportsDirectCompositing() const override { return true; }
bool SupportsSingleBuffering() const override { return true; }
private:
scoped_refptr<CanvasResource> CreateResource() final {
......@@ -684,6 +687,7 @@ void CanvasResourceProvider::InvalidateSurface() {
canvas_image_provider_.reset();
xform_canvas_ = nullptr;
surface_ = nullptr;
single_buffer_ = nullptr;
}
uint32_t CanvasResourceProvider::ContentUniqueID() const {
......@@ -721,6 +725,11 @@ void CanvasResourceProvider::ClearRecycledResources() {
}
scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
if (IsSingleBuffered()) {
if (!single_buffer_)
single_buffer_ = CreateResource();
return single_buffer_;
}
if (recycled_resources_.size()) {
scoped_refptr<CanvasResource> resource =
std::move(recycled_resources_.back());
......@@ -730,4 +739,11 @@ scoped_refptr<CanvasResource> CanvasResourceProvider::NewOrRecycledResource() {
return CreateResource();
}
void CanvasResourceProvider::TryEnableSingleBuffering() {
if (IsSingleBuffered() || !SupportsSingleBuffering())
return;
SetResourceRecyclingEnabled(false);
is_single_buffered_ = true;
}
} // namespace blink
......@@ -100,11 +100,26 @@ class PLATFORM_EXPORT CanvasResourceProvider
virtual bool IsValid() const = 0;
virtual bool IsAccelerated() const = 0;
virtual bool SupportsDirectCompositing() const = 0;
virtual bool SupportsSingleBuffering() const { return false; }
uint32_t ContentUniqueID() const;
CanvasResourceDispatcher* ResourceDispatcher() {
return resource_dispatcher_.get();
}
// Indicates that the compositing path is single buffered, meaning that
// ProduceFrame() return a reference to the same resource each time, which
// implies that Producing an animation frame may overwrite the resource used
// by the previous frame. This results in graphics updates skipping the
// queue, thus reducing latency, but with the possible side effects of
// tearring (in cases where the resource is scanned out directly) and
// irregular frame rate.
bool IsSingleBuffered() { return is_single_buffered_; }
// Attempt to enable single buffering mode on this resource provider. May
// fail if the CanvasResourcePRovider subclass does not support this mode of
// operation.
void TryEnableSingleBuffering();
void RecycleResource(scoped_refptr<CanvasResource>);
void SetResourceRecyclingEnabled(bool);
void ClearRecycledResources();
......@@ -189,6 +204,9 @@ class PLATFORM_EXPORT CanvasResourceProvider
WTF::Vector<scoped_refptr<CanvasResource>> recycled_resources_;
bool resource_recycling_enabled_ = true;
bool is_single_buffered_ = false;
scoped_refptr<CanvasResource> single_buffer_;
base::WeakPtrFactory<CanvasResourceProvider> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(CanvasResourceProvider);
......
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