blob: 48f5968f49c9a9a92c4a341a417c25f8584227ed [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/renderer/gpu/render_widget_compositor.h"
#include "cc/output/begin_frame_args.h"
#include "cc/test/failure_output_surface.h"
#include "cc/trees/layer_tree_host.h"
#include "components/scheduler/renderer/renderer_scheduler.h"
#include "content/public/test/mock_render_thread.h"
#include "content/renderer/render_widget.h"
#include "content/test/fake_compositor_dependencies.h"
#include "content/test/fake_renderer_scheduler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebScreenInfo.h"
using testing::AllOf;
using testing::Field;
namespace content {
namespace {
class MockWebWidget : public blink::WebWidget {
public:
MOCK_METHOD1(beginFrame, void(const blink::WebBeginFrameArgs& args));
};
class TestRenderWidget : public RenderWidget {
public:
TestRenderWidget()
: RenderWidget(blink::WebPopupTypeNone,
blink::WebScreenInfo(),
true,
false,
false) {
webwidget_ = &mock_webwidget_;
}
MockWebWidget mock_webwidget_;
protected:
~TestRenderWidget() override { webwidget_ = NULL; }
DISALLOW_COPY_AND_ASSIGN(TestRenderWidget);
};
class RenderWidgetCompositorTest : public testing::Test {
public:
RenderWidgetCompositorTest()
: render_widget_(make_scoped_refptr(new TestRenderWidget())),
compositor_deps_(make_scoped_ptr(new FakeCompositorDependencies)),
render_widget_compositor_(
RenderWidgetCompositor::Create(render_widget_.get(),
compositor_deps_.get())) {}
~RenderWidgetCompositorTest() override {}
protected:
base::MessageLoop loop_;
MockRenderThread render_thread_;
scoped_refptr<TestRenderWidget> render_widget_;
scoped_ptr<FakeCompositorDependencies> compositor_deps_;
scoped_ptr<RenderWidgetCompositor> render_widget_compositor_;
private:
DISALLOW_COPY_AND_ASSIGN(RenderWidgetCompositorTest);
};
TEST_F(RenderWidgetCompositorTest, BeginMainFrame) {
base::TimeTicks frame_time(base::TimeTicks() +
base::TimeDelta::FromSeconds(1));
base::TimeTicks deadline(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
base::TimeDelta interval(base::TimeDelta::FromSeconds(3));
cc::BeginFrameArgs args(
cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
interval, cc::BeginFrameArgs::NORMAL));
EXPECT_CALL(render_widget_->mock_webwidget_,
beginFrame(AllOf(
Field(&blink::WebBeginFrameArgs::lastFrameTimeMonotonic, 1),
Field(&blink::WebBeginFrameArgs::deadline, 2),
Field(&blink::WebBeginFrameArgs::interval, 3))));
render_widget_compositor_->BeginMainFrame(args);
}
class RenderWidgetCompositorOutputSurface;
class RenderWidgetOutputSurface : public TestRenderWidget {
public:
RenderWidgetOutputSurface() : compositor_(NULL) {}
void SetCompositor(RenderWidgetCompositorOutputSurface* compositor);
scoped_ptr<cc::OutputSurface> CreateOutputSurface(bool fallback) override;
protected:
~RenderWidgetOutputSurface() override {}
private:
RenderWidgetCompositorOutputSurface* compositor_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetOutputSurface);
};
// Verify that failing to create an output surface will cause the compositor
// to attempt to repeatedly create another output surface. After enough
// failures, verify that it attempts to create a fallback output surface.
// The use null output surface parameter allows testing whether failures
// from RenderWidget (couldn't create an output surface) vs failures from
// the compositor (couldn't bind the output surface) are handled identically.
class RenderWidgetCompositorOutputSurface : public RenderWidgetCompositor {
public:
RenderWidgetCompositorOutputSurface(RenderWidget* widget,
CompositorDependencies* compositor_deps)
: RenderWidgetCompositor(widget, compositor_deps),
num_failures_before_success_(0),
expected_successes_(0),
expected_fallback_successes_(0),
expected_requests_(0),
num_requests_(0),
num_requests_since_last_success_(0),
num_successes_(0),
num_fallback_successes_(0),
num_failures_(0),
last_create_was_fallback_(false),
use_null_output_surface_(true) {}
using RenderWidgetCompositor::Initialize;
scoped_ptr<cc::OutputSurface> CreateOutputSurface(bool fallback) {
EXPECT_EQ(num_requests_since_last_success_ >
OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK,
fallback);
last_create_was_fallback_ = fallback;
bool success = num_failures_ >= num_failures_before_success_;
if (success) {
scoped_ptr<cc::TestWebGraphicsContext3D> context =
cc::TestWebGraphicsContext3D::Create();
// Image support required for synchronous compositing.
context->set_support_image(true);
return cc::FakeOutputSurface::Create3d(context.Pass());
}
return use_null_output_surface_
? nullptr
: make_scoped_ptr(new cc::FailureOutputSurface(false));
}
// Force a new output surface to be created.
void SynchronousComposite() {
layer_tree_host()->DidLoseOutputSurface();
base::TimeTicks some_time;
layer_tree_host()->Composite(some_time);
}
void RequestNewOutputSurface() override {
++num_requests_;
++num_requests_since_last_success_;
RenderWidgetCompositor::RequestNewOutputSurface();
}
void DidInitializeOutputSurface() override {
if (last_create_was_fallback_)
++num_fallback_successes_;
else
++num_successes_;
if (num_requests_ == expected_requests_) {
EndTest();
} else {
num_requests_since_last_success_ = 0;
RenderWidgetCompositor::DidInitializeOutputSurface();
// Post the synchronous composite task so that it is not called
// reentrantly as a part of RequestNewOutputSurface.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&RenderWidgetCompositorOutputSurface::SynchronousComposite,
base::Unretained(this)));
}
}
void DidFailToInitializeOutputSurface() override {
++num_failures_;
if (num_requests_ == expected_requests_) {
EndTest();
return;
}
RenderWidgetCompositor::DidFailToInitializeOutputSurface();
}
void SetUp(bool use_null_output_surface,
int num_failures_before_success,
int expected_successes,
int expected_fallback_succeses) {
use_null_output_surface_ = use_null_output_surface;
num_failures_before_success_ = num_failures_before_success;
expected_successes_ = expected_successes;
expected_fallback_successes_ = expected_fallback_succeses;
expected_requests_ = num_failures_before_success_ + expected_successes_ +
expected_fallback_successes_;
}
void EndTest() { base::MessageLoop::current()->Quit(); }
void AfterTest() {
EXPECT_EQ(num_failures_before_success_, num_failures_);
EXPECT_EQ(expected_successes_, num_successes_);
EXPECT_EQ(expected_fallback_successes_, num_fallback_successes_);
EXPECT_EQ(expected_requests_, num_requests_);
}
private:
int num_failures_before_success_;
int expected_successes_;
int expected_fallback_successes_;
int expected_requests_;
int num_requests_;
int num_requests_since_last_success_;
int num_successes_;
int num_fallback_successes_;
int num_failures_;
bool last_create_was_fallback_;
bool use_null_output_surface_;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetCompositorOutputSurface);
};
class RenderWidgetCompositorOutputSurfaceTest : public testing::Test {
public:
RenderWidgetCompositorOutputSurfaceTest()
: render_widget_(make_scoped_refptr(new RenderWidgetOutputSurface)),
compositor_deps_(make_scoped_ptr(new FakeCompositorDependencies)) {
render_widget_compositor_.reset(new RenderWidgetCompositorOutputSurface(
render_widget_.get(), compositor_deps_.get()));
render_widget_compositor_->Initialize();
render_widget_->SetCompositor(render_widget_compositor_.get());
}
void RunTest(bool use_null_output_surface,
int num_failures_before_success,
int expected_successes,
int expected_fallback_succeses) {
render_widget_compositor_->SetUp(
use_null_output_surface, num_failures_before_success,
expected_successes, expected_fallback_succeses);
render_widget_compositor_->StartCompositor();
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&RenderWidgetCompositorOutputSurface::SynchronousComposite,
base::Unretained(render_widget_compositor_.get())));
base::MessageLoop::current()->Run();
render_widget_compositor_->AfterTest();
}
protected:
base::MessageLoop ye_olde_message_loope_;
MockRenderThread render_thread_;
scoped_refptr<RenderWidgetOutputSurface> render_widget_;
scoped_ptr<FakeCompositorDependencies> compositor_deps_;
scoped_ptr<RenderWidgetCompositorOutputSurface> render_widget_compositor_;
private:
DISALLOW_COPY_AND_ASSIGN(RenderWidgetCompositorOutputSurfaceTest);
};
scoped_ptr<cc::OutputSurface> RenderWidgetOutputSurface::CreateOutputSurface(
bool fallback) {
return compositor_->CreateOutputSurface(fallback);
}
void RenderWidgetOutputSurface::SetCompositor(
RenderWidgetCompositorOutputSurface* compositor) {
compositor_ = compositor;
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, SucceedOnce) {
RunTest(false, 0, 1, 0);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, SucceedTwice) {
RunTest(false, 0, 2, 0);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, FailOnceNull) {
static_assert(
RenderWidgetCompositor::OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK >= 2,
"Adjust the values of this test if this fails");
RunTest(true, 1, 1, 0);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, FailOnceBind) {
static_assert(
RenderWidgetCompositor::OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK >= 2,
"Adjust the values of this test if this fails");
RunTest(false, 1, 1, 0);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, FallbackSuccessNull) {
RunTest(true, RenderWidgetCompositor::OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK,
0, 1);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, FallbackSuccessBind) {
RunTest(false, RenderWidgetCompositor::OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK,
0, 1);
}
TEST_F(RenderWidgetCompositorOutputSurfaceTest, FallbackSuccessNormalSuccess) {
// The first success is a fallback, but the next should not be a fallback.
RunTest(false, RenderWidgetCompositor::OUTPUT_SURFACE_RETRIES_BEFORE_FALLBACK,
1, 1);
}
} // namespace
} // namespace content