blob: b7c0b634ee425b2be3c0e050fb9dd4e7e0890655 [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 <utility>
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "cc/animation/animation_host.h"
#include "cc/output/begin_frame_args.h"
#include "cc/output/copy_output_request.h"
#include "cc/test/fake_layer_tree_frame_sink.h"
#include "cc/test/test_context_provider.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "cc/trees/layer_tree_host.h"
#include "content/public/common/screen_info.h"
#include "content/public/test/mock_render_thread.h"
#include "content/renderer/render_widget.h"
#include "content/test/fake_compositor_dependencies.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::AllOf;
using testing::Field;
namespace content {
namespace {
enum FailureMode {
NO_FAILURE,
GPU_CHANNEL_FAILURE,
};
class StubRenderWidgetCompositorDelegate
: public RenderWidgetCompositorDelegate {
public:
// RenderWidgetCompositorDelegate implementation.
void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta,
const gfx::Vector2dF& outer_delta,
const gfx::Vector2dF& elastic_overscroll_delta,
float page_scale,
float top_controls_delta) override {}
void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel,
bool has_scrolled_by_touch) override {}
void BeginMainFrame(double frame_time_sec) override {}
void RequestNewLayerTreeFrameSink(
bool fallback,
const LayerTreeFrameSinkCallback& callback) override {
callback.Run(nullptr);
}
void DidCommitAndDrawCompositorFrame() override {}
void DidCommitCompositorFrame() override {}
void DidCompletePageScaleAnimation() override {}
void DidReceiveCompositorFrameAck() override {}
bool IsClosing() const override { return false; }
void RequestScheduleAnimation() override {}
void UpdateVisualState() override {}
void WillBeginCompositorFrame() override {}
std::unique_ptr<cc::SwapPromise> RequestCopyOfOutputForLayoutTest(
std::unique_ptr<cc::CopyOutputRequest> request) override {
return nullptr;
}
};
class FakeRenderWidgetCompositorDelegate
: public StubRenderWidgetCompositorDelegate {
public:
FakeRenderWidgetCompositorDelegate() = default;
void RequestNewLayerTreeFrameSink(
bool fallback,
const LayerTreeFrameSinkCallback& callback) override {
last_create_was_fallback_ = fallback;
bool success = num_failures_ >= num_failures_before_success_;
if (!success && use_null_layer_tree_frame_sink_) {
callback.Run(std::unique_ptr<cc::LayerTreeFrameSink>());
return;
}
auto context_provider = cc::TestContextProvider::Create();
if (!success) {
context_provider->UnboundTestContext3d()->loseContextCHROMIUM(
GL_GUILTY_CONTEXT_RESET_ARB, GL_INNOCENT_CONTEXT_RESET_ARB);
}
callback.Run(
cc::FakeLayerTreeFrameSink::Create3d(std::move(context_provider)));
}
void Reset() {
num_requests_ = 0;
num_requests_since_last_success_ = 0;
num_failures_ = 0;
num_failures_before_success_ = 0;
num_fallback_successes_ = 0;
num_successes_ = 0;
}
void add_success() {
if (last_create_was_fallback_)
++num_fallback_successes_;
else
++num_successes_;
num_requests_since_last_success_ = 0;
}
int num_successes() const { return num_successes_; }
int num_fallback_successes() const { return num_fallback_successes_; }
void add_request() {
++num_requests_since_last_success_;
++num_requests_;
}
int num_requests() const { return num_requests_; }
void add_failure() { ++num_failures_; }
int num_failures() const { return num_failures_; }
void set_num_failures_before_success(int n) {
num_failures_before_success_ = n;
}
int num_failures_before_success() const {
return num_failures_before_success_;
}
void set_use_null_layer_tree_frame_sink(bool u) {
use_null_layer_tree_frame_sink_ = u;
}
private:
int num_requests_ = 0;
int num_requests_since_last_success_ = 0;
int num_failures_ = 0;
int num_failures_before_success_ = 0;
int num_fallback_successes_ = 0;
int num_successes_ = 0;
bool last_create_was_fallback_ = false;
bool use_null_layer_tree_frame_sink_ = true;
DISALLOW_COPY_AND_ASSIGN(FakeRenderWidgetCompositorDelegate);
};
// 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 RenderWidgetLayerTreeFrameSink : public RenderWidgetCompositor {
public:
RenderWidgetLayerTreeFrameSink(FakeRenderWidgetCompositorDelegate* delegate,
CompositorDependencies* compositor_deps)
: RenderWidgetCompositor(delegate, compositor_deps),
delegate_(delegate) {}
using RenderWidgetCompositor::Initialize;
// Force a new output surface to be created.
void SynchronousComposite() {
layer_tree_host()->SetVisible(false);
layer_tree_host()->ReleaseLayerTreeFrameSink();
layer_tree_host()->SetVisible(true);
base::TimeTicks some_time;
layer_tree_host()->Composite(some_time);
}
void RequestNewLayerTreeFrameSink() override {
delegate_->add_request();
RenderWidgetCompositor::RequestNewLayerTreeFrameSink();
}
void DidInitializeLayerTreeFrameSink() override {
RenderWidgetCompositor::DidInitializeLayerTreeFrameSink();
delegate_->add_success();
if (delegate_->num_requests() == expected_requests_) {
EndTest();
} else {
// Post the synchronous composite task so that it is not called
// reentrantly as a part of RequestNewLayerTreeFrameSink.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&RenderWidgetLayerTreeFrameSink::SynchronousComposite,
base::Unretained(this)));
}
}
void DidFailToInitializeLayerTreeFrameSink() override {
RenderWidgetCompositor::DidFailToInitializeLayerTreeFrameSink();
delegate_->add_failure();
if (delegate_->num_requests() == expected_requests_) {
EndTest();
return;
}
}
void SetUp(int expected_successes, FailureMode failure_mode) {
failure_mode_ = failure_mode;
switch (failure_mode_) {
case NO_FAILURE:
expected_requests_ = 1;
break;
case GPU_CHANNEL_FAILURE:
expected_requests_ = 2;
break;
}
expected_successes_ = expected_successes;
expected_requests_ += (expected_successes - 1);
}
void EndTest() { base::MessageLoop::current()->QuitWhenIdle(); }
void AfterTest() {
if (failure_mode_ == NO_FAILURE) {
EXPECT_EQ(expected_successes_, delegate_->num_successes());
EXPECT_EQ(0, delegate_->num_fallback_successes());
} else if (failure_mode_ == GPU_CHANNEL_FAILURE) {
EXPECT_EQ(0, delegate_->num_successes());
EXPECT_EQ(1, delegate_->num_fallback_successes());
} else {
NOTREACHED();
}
EXPECT_EQ(expected_requests_, delegate_->num_requests());
}
private:
FakeRenderWidgetCompositorDelegate* delegate_;
int expected_successes_ = 0;
int expected_requests_ = 0;
FailureMode failure_mode_ = NO_FAILURE;
DISALLOW_COPY_AND_ASSIGN(RenderWidgetLayerTreeFrameSink);
};
class RenderWidgetLayerTreeFrameSinkTest : public testing::Test {
public:
RenderWidgetLayerTreeFrameSinkTest()
: render_widget_compositor_(&compositor_delegate_, &compositor_deps_) {
auto animation_host = cc::AnimationHost::CreateMainInstance();
ScreenInfo dummy_screen_info;
const float initial_device_scale_factor = 1.f;
auto layer_tree_host = RenderWidgetCompositor::CreateLayerTreeHost(
&render_widget_compositor_, &render_widget_compositor_,
animation_host.get(), &compositor_deps_, initial_device_scale_factor,
dummy_screen_info);
render_widget_compositor_.Initialize(std::move(layer_tree_host),
std::move(animation_host));
}
void RunTest(bool use_null_layer_tree_frame_sink,
int expected_successes,
FailureMode failure_mode) {
compositor_delegate_.Reset();
compositor_delegate_.set_use_null_layer_tree_frame_sink(
use_null_layer_tree_frame_sink);
compositor_delegate_.set_num_failures_before_success(
failure_mode == NO_FAILURE ? 0 : 1);
render_widget_compositor_.SetUp(expected_successes, failure_mode);
render_widget_compositor_.SetVisible(true);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&RenderWidgetLayerTreeFrameSink::SynchronousComposite,
base::Unretained(&render_widget_compositor_)));
base::RunLoop().Run();
render_widget_compositor_.AfterTest();
}
protected:
base::MessageLoop ye_olde_message_loope_;
MockRenderThread render_thread_;
FakeCompositorDependencies compositor_deps_;
FakeRenderWidgetCompositorDelegate compositor_delegate_;
RenderWidgetLayerTreeFrameSink render_widget_compositor_;
private:
DISALLOW_COPY_AND_ASSIGN(RenderWidgetLayerTreeFrameSinkTest);
};
TEST_F(RenderWidgetLayerTreeFrameSinkTest, SucceedOnce) {
RunTest(false, 1, NO_FAILURE);
}
TEST_F(RenderWidgetLayerTreeFrameSinkTest, SucceedTwice) {
RunTest(false, 2, NO_FAILURE);
}
TEST_F(RenderWidgetLayerTreeFrameSinkTest, FailOnceNull) {
RunTest(true, 1, NO_FAILURE);
}
// Android doesn't support fallback frame sinks. (crbug.com/721102)
#if !defined(OS_ANDROID)
TEST_F(RenderWidgetLayerTreeFrameSinkTest, SoftwareFallbackSucceed) {
RunTest(false, 1, GPU_CHANNEL_FAILURE);
}
TEST_F(RenderWidgetLayerTreeFrameSinkTest, FallbackSuccessNull) {
RunTest(true, 1, GPU_CHANNEL_FAILURE);
}
TEST_F(RenderWidgetLayerTreeFrameSinkTest, FallbackSuccessNormalSuccess) {
// The first success is a fallback, but the next should not be a fallback.
RunTest(false, 1, GPU_CHANNEL_FAILURE);
RunTest(false, 1, NO_FAILURE);
}
#endif
// Verify desktop memory limit calculations.
#if !defined(OS_ANDROID)
TEST(RenderWidgetCompositorTest, IgnoreGivenMemoryPolicy) {
auto policy = RenderWidgetCompositor::GetGpuMemoryPolicy(
cc::ManagedMemoryPolicy(256), ScreenInfo());
EXPECT_EQ(512u * 1024u * 1024u, policy.bytes_limit_when_visible);
EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
policy.priority_cutoff_when_visible);
}
TEST(RenderWidgetCompositorTest, LargeScreensUseMoreMemory) {
ScreenInfo screen_info;
screen_info.rect = gfx::Rect(4096, 2160);
screen_info.device_scale_factor = 1.f;
auto policy = RenderWidgetCompositor::GetGpuMemoryPolicy(
cc::ManagedMemoryPolicy(256), screen_info);
EXPECT_EQ(2u * 512u * 1024u * 1024u, policy.bytes_limit_when_visible);
EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
policy.priority_cutoff_when_visible);
screen_info.rect = gfx::Rect(2048, 1080);
screen_info.device_scale_factor = 2.f;
policy = RenderWidgetCompositor::GetGpuMemoryPolicy(
cc::ManagedMemoryPolicy(256), screen_info);
EXPECT_EQ(2u * 512u * 1024u * 1024u, policy.bytes_limit_when_visible);
EXPECT_EQ(gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE,
policy.priority_cutoff_when_visible);
}
#endif // !defined(OS_ANDROID)
} // namespace
} // namespace content