blob: c4c52773605d247d7443c6f0aedd4b63e181959e [file] [log] [blame]
// Copyright 2015 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 "components/viz/service/frame_sinks/direct_layer_tree_frame_sink.h"
#include <memory>
#include "base/test/test_mock_time_task_runner.h"
#include "cc/test/fake_layer_tree_frame_sink_client.h"
#include "components/viz/common/display/renderer_settings.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/frame_sinks/delay_based_time_source.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/display/display.h"
#include "components/viz/service/display/display_scheduler.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_output_surface.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "components/viz/test/test_shared_bitmap_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace viz {
namespace {
static constexpr FrameSinkId kArbitraryFrameSinkId(1, 1);
class TestDirectLayerTreeFrameSink : public DirectLayerTreeFrameSink {
public:
using DirectLayerTreeFrameSink::DirectLayerTreeFrameSink;
};
class TestCompositorFrameSinkSupportManager
: public CompositorFrameSinkSupportManager {
public:
explicit TestCompositorFrameSinkSupportManager(
FrameSinkManagerImpl* frame_sink_manager)
: frame_sink_manager_(frame_sink_manager) {}
~TestCompositorFrameSinkSupportManager() override = default;
std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
mojom::CompositorFrameSinkClient* client,
const FrameSinkId& frame_sink_id,
bool is_root,
bool needs_sync_points) override {
return std::make_unique<CompositorFrameSinkSupport>(
client, frame_sink_manager_, frame_sink_id, is_root, needs_sync_points);
}
private:
FrameSinkManagerImpl* const frame_sink_manager_;
DISALLOW_COPY_AND_ASSIGN(TestCompositorFrameSinkSupportManager);
};
class DirectLayerTreeFrameSinkTest : public testing::Test {
public:
DirectLayerTreeFrameSinkTest()
: task_runner_(base::MakeRefCounted<base::TestMockTimeTaskRunner>(
base::TestMockTimeTaskRunner::Type::kStandalone)),
display_size_(1920, 1080),
display_rect_(display_size_),
frame_sink_manager_(&bitmap_manager_),
support_manager_(&frame_sink_manager_),
context_provider_(TestContextProvider::Create()) {
auto display_output_surface = FakeOutputSurface::Create3d();
display_output_surface_ = display_output_surface.get();
begin_frame_source_ = std::make_unique<BackToBackBeginFrameSource>(
std::make_unique<DelayBasedTimeSource>(task_runner_.get()));
int max_frames_pending = 2;
auto scheduler = std::make_unique<DisplayScheduler>(
begin_frame_source_.get(), task_runner_.get(), max_frames_pending);
display_ = std::make_unique<Display>(
&bitmap_manager_, RendererSettings(), kArbitraryFrameSinkId,
std::move(display_output_surface), std::move(scheduler), task_runner_);
layer_tree_frame_sink_ = std::make_unique<TestDirectLayerTreeFrameSink>(
kArbitraryFrameSinkId, &support_manager_, &frame_sink_manager_,
display_.get(), nullptr /* display_client */, context_provider_,
nullptr, task_runner_, &gpu_memory_buffer_manager_,
false /* use_viz_hit_test */);
layer_tree_frame_sink_->BindToClient(&layer_tree_frame_sink_client_);
display_->Resize(display_size_);
display_->SetVisible(true);
EXPECT_FALSE(
layer_tree_frame_sink_client_.did_lose_layer_tree_frame_sink_called());
}
~DirectLayerTreeFrameSinkTest() override {
layer_tree_frame_sink_->DetachFromClient();
}
void SwapBuffersWithDamage(const gfx::Rect& damage_rect) {
auto frame = CompositorFrameBuilder()
.AddRenderPass(display_rect_, damage_rect)
.Build();
layer_tree_frame_sink_->SubmitCompositorFrame(std::move(frame));
}
void SendRenderPassList(RenderPassList* pass_list) {
auto frame = CompositorFrameBuilder()
.SetRenderPassList(std::move(*pass_list))
.Build();
pass_list->clear();
layer_tree_frame_sink_->SubmitCompositorFrame(std::move(frame));
}
void SetUp() override {
// Draw the first frame to start in an "unlocked" state.
SwapBuffersWithDamage(display_rect_);
EXPECT_EQ(0u, display_output_surface_->num_sent_frames());
task_runner_->RunUntilIdle();
EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
}
protected:
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
const gfx::Size display_size_;
const gfx::Rect display_rect_;
TestSharedBitmapManager bitmap_manager_;
FrameSinkManagerImpl frame_sink_manager_;
TestCompositorFrameSinkSupportManager support_manager_;
TestGpuMemoryBufferManager gpu_memory_buffer_manager_;
scoped_refptr<TestContextProvider> context_provider_;
FakeOutputSurface* display_output_surface_ = nullptr;
std::unique_ptr<BackToBackBeginFrameSource> begin_frame_source_;
std::unique_ptr<Display> display_;
cc::FakeLayerTreeFrameSinkClient layer_tree_frame_sink_client_;
std::unique_ptr<TestDirectLayerTreeFrameSink> layer_tree_frame_sink_;
};
TEST_F(DirectLayerTreeFrameSinkTest, DamageTriggersSwapBuffers) {
SwapBuffersWithDamage(display_rect_);
EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
task_runner_->RunUntilIdle();
EXPECT_EQ(2u, display_output_surface_->num_sent_frames());
}
TEST_F(DirectLayerTreeFrameSinkTest, NoDamageDoesNotTriggerSwapBuffers) {
SwapBuffersWithDamage(gfx::Rect());
EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
task_runner_->RunUntilIdle();
EXPECT_EQ(1u, display_output_surface_->num_sent_frames());
}
// Test that hit_test_region_list are created correctly for the browser.
TEST_F(DirectLayerTreeFrameSinkTest, HitTestRegionList) {
RenderPassList pass_list;
// Add a DrawQuad that is not a SurfaceDrawQuad. |hit_test_region_list_|
// shouldn't have any child regions.
auto pass1 = RenderPass::Create();
pass1->output_rect = display_rect_;
pass1->id = 1;
auto* shared_quad_state1 = pass1->CreateAndAppendSharedQuadState();
gfx::Rect rect1(display_rect_);
shared_quad_state1->SetAll(
gfx::Transform(), rect1 /* quad_layer_rect */,
rect1 /* visible_quad_layer_rect */, rect1 /*clip_rect */,
false /* is_clipped */, false /* are_contents_opaque */,
0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
auto* quad1 = pass1->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
quad1->SetNew(shared_quad_state1, rect1 /* rect */, rect1 /* visible_rect */,
SK_ColorBLACK, false /* force_anti_aliasing_off */);
pass_list.push_back(std::move(pass1));
SendRenderPassList(&pass_list);
task_runner_->RunUntilIdle();
const auto* hit_test_region_list =
frame_sink_manager_.hit_test_manager()->GetActiveHitTestRegionList(
display_.get(), display_->CurrentSurfaceId().frame_sink_id());
EXPECT_TRUE(hit_test_region_list);
EXPECT_EQ(display_rect_, hit_test_region_list->bounds);
EXPECT_EQ(HitTestRegionFlags::kHitTestMouse |
HitTestRegionFlags::kHitTestTouch |
HitTestRegionFlags::kHitTestMine,
hit_test_region_list->flags);
EXPECT_FALSE(hit_test_region_list->regions.size());
// Add a SurfaceDrawQuad to one render pass, and add a SolidColorDrawQuad to
// another render pass, and add a SurfaceDrawQuad with a transform that's not
// invertible. |hit_test_region_list_| should contain one child
// region corresponding to that first SurfaceDrawQuad.
const SurfaceId child_surface_id(
FrameSinkId(1, 1), LocalSurfaceId(1, base::UnguessableToken::Create()));
auto pass2 = RenderPass::Create();
pass2->output_rect = display_rect_;
pass2->id = 2;
auto* shared_quad_state2 = pass2->CreateAndAppendSharedQuadState();
gfx::Rect rect2 = gfx::Rect(20, 20);
gfx::Transform transform2;
transform2.Translate(-200, -100);
shared_quad_state2->SetAll(
transform2, rect2 /* quad_layer_rect */,
rect2 /* visible_quad_layer_rect */, rect2 /*clip_rect */,
false /* is_clipped */, false /* are_contents_opaque */,
0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
auto* quad2 = pass2->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
quad2->SetNew(shared_quad_state2, rect2 /* rect */, rect2 /* visible_rect */,
SurfaceRange(base::nullopt, child_surface_id), SK_ColorBLACK,
false /* stretch_content_to_fill_bounds */);
pass_list.push_back(std::move(pass2));
auto pass3 = RenderPass::Create();
pass3->output_rect = display_rect_;
pass3->id = 3;
auto* shared_quad_state3 = pass3->CreateAndAppendSharedQuadState();
gfx::Rect rect3(display_rect_);
shared_quad_state3->SetAll(
gfx::Transform(), rect3 /* quad_layer_rect */,
rect3 /* visible_quad_layer_rect */, rect3 /*clip_rect */,
false /* is_clipped */, false /* are_contents_opaque */,
0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
auto* quad3 = pass3->quad_list.AllocateAndConstruct<SolidColorDrawQuad>();
quad3->SetNew(shared_quad_state3, rect3 /* rect */, rect3 /* visible_rect */,
SK_ColorBLACK, false /* force_anti_aliasing_off */);
pass_list.push_back(std::move(pass3));
SendRenderPassList(&pass_list);
task_runner_->RunUntilIdle();
const SurfaceId child_surface_id4(
FrameSinkId(4, 1), LocalSurfaceId(1, base::UnguessableToken::Create()));
auto pass4 = RenderPass::Create();
pass4->output_rect = display_rect_;
pass4->id = 4;
auto* shared_quad_state4 = pass4->CreateAndAppendSharedQuadState();
gfx::Rect rect4(display_rect_);
// A degenerate matrix of all zeros is not invertible.
gfx::Transform transform4;
transform4.matrix().set(0, 0, 0.f);
transform4.matrix().set(1, 1, 0.f);
transform4.matrix().set(2, 2, 0.f);
transform4.matrix().set(3, 3, 0.f);
shared_quad_state4->SetAll(
transform4, rect4 /* quad_layer_rect */,
rect4 /* visible_quad_layer_rect */, rect4 /*clip_rect */,
false /* is_clipped */, false /* are_contents_opaque */,
0.5f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */);
auto* quad4 = pass4->quad_list.AllocateAndConstruct<SurfaceDrawQuad>();
quad4->SetNew(shared_quad_state4, rect4 /* rect */, rect4 /* visible_rect */,
SurfaceRange(base::nullopt, child_surface_id4), SK_ColorBLACK,
false /* stretch_content_to_fill_bounds */);
pass_list.push_back(std::move(pass4));
const auto* hit_test_region_list1 =
frame_sink_manager_.hit_test_manager()->GetActiveHitTestRegionList(
display_.get(), display_->CurrentSurfaceId().frame_sink_id());
EXPECT_TRUE(hit_test_region_list1);
EXPECT_EQ(display_rect_, hit_test_region_list1->bounds);
EXPECT_EQ(HitTestRegionFlags::kHitTestMouse |
HitTestRegionFlags::kHitTestTouch |
HitTestRegionFlags::kHitTestMine,
hit_test_region_list1->flags);
EXPECT_EQ(1u, hit_test_region_list1->regions.size());
EXPECT_EQ(child_surface_id.frame_sink_id(),
hit_test_region_list1->regions[0].frame_sink_id);
EXPECT_EQ(HitTestRegionFlags::kHitTestMouse |
HitTestRegionFlags::kHitTestTouch |
HitTestRegionFlags::kHitTestChildSurface,
hit_test_region_list1->regions[0].flags);
EXPECT_EQ(gfx::Rect(20, 20), hit_test_region_list1->regions[0].rect);
gfx::Transform transform2_inverse;
EXPECT_TRUE(transform2.GetInverse(&transform2_inverse));
EXPECT_EQ(transform2_inverse, hit_test_region_list1->regions[0].transform);
}
} // namespace
} // namespace viz