blob: 3faa62a9544b5f69e86caf20c5f14b63b893d638 [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 <stdint.h>
#include <iostream>
#include <set>
#include <vector>
#include "base/location.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "cc/animation/animation_host.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/surface_layer.h"
#include "cc/layers/surface_layer_impl.h"
#include "cc/test/fake_impl_task_runner_provider.h"
#include "cc/test/fake_layer_tree_host.h"
#include "cc/test/fake_layer_tree_host_client.h"
#include "cc/test/fake_layer_tree_host_impl.h"
#include "cc/test/layer_tree_test.h"
#include "cc/test/test_task_graph_runner.h"
#include "cc/trees/layer_tree_host.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
using testing::_;
using testing::Eq;
using testing::ElementsAre;
using testing::SizeIs;
constexpr viz::FrameSinkId kArbitraryFrameSinkId(1, 1);
class SurfaceLayerTest : public testing::Test {
public:
SurfaceLayerTest()
: host_impl_(&task_runner_provider_, &task_graph_runner_) {}
// Synchronizes |layer_tree_host_| and |host_impl_| and pushes surface ids.
void SynchronizeTrees() {
TreeSynchronizer::PushLayerProperties(layer_tree_host_.get(),
host_impl_.pending_tree());
layer_tree_host_->PushSurfaceRangesTo(host_impl_.pending_tree());
}
protected:
void SetUp() override {
animation_host_ = AnimationHost::CreateForTesting(ThreadInstance::MAIN);
layer_tree_host_ = FakeLayerTreeHost::Create(
&fake_client_, &task_graph_runner_, animation_host_.get());
layer_tree_host_->SetViewportSizeAndScale(gfx::Size(10, 10), 1.f,
viz::LocalSurfaceIdAllocation());
host_impl_.CreatePendingTree();
}
void TearDown() override {
if (layer_tree_host_) {
layer_tree_host_->SetRootLayer(nullptr);
layer_tree_host_ = nullptr;
}
}
FakeLayerTreeHostClient fake_client_;
FakeImplTaskRunnerProvider task_runner_provider_;
TestTaskGraphRunner task_graph_runner_;
std::unique_ptr<AnimationHost> animation_host_;
std::unique_ptr<FakeLayerTreeHost> layer_tree_host_;
FakeLayerTreeHostImpl host_impl_;
};
// This test verifies that if UseExistingDeadline() is used on a new
// SurfaceLayer then the deadline will be 0 frames.
TEST_F(SurfaceLayerTest, UseExistingDeadlineForNewSurfaceLayer) {
scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create();
layer_tree_host_->SetRootLayer(layer);
viz::SurfaceId primary_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseExistingDeadline());
EXPECT_EQ(0u, layer->deadline_in_frames());
}
// This test verifies that if UseInfiniteDeadline() is used on a new
// SurfaceLayer then the deadline will be max number of frames.
TEST_F(SurfaceLayerTest, UseInfiniteDeadlineForNewSurfaceLayer) {
scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create();
layer_tree_host_->SetRootLayer(layer);
viz::SurfaceId primary_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseInfiniteDeadline());
EXPECT_EQ(std::numeric_limits<uint32_t>::max(), layer->deadline_in_frames());
}
// This test verifies that if an invalid primary surface ID is set then the
// deadline will be reset to 0 frames.
TEST_F(SurfaceLayerTest, ResetDeadlineOnInvalidSurfaceId) {
scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create();
layer_tree_host_->SetRootLayer(layer);
viz::SurfaceId primary_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseSpecifiedDeadline(3u));
EXPECT_EQ(3u, layer->deadline_in_frames());
// Reset the surface layer to an invalid SurfaceId. Verify that the deadline
// is reset.
layer->SetSurfaceId(viz::SurfaceId(),
DeadlinePolicy::UseSpecifiedDeadline(3u));
EXPECT_EQ(0u, layer->deadline_in_frames());
}
// This test verifies that SurfaceLayer properties are pushed across to
// SurfaceLayerImpl.
TEST_F(SurfaceLayerTest, PushProperties) {
scoped_refptr<SurfaceLayer> layer = SurfaceLayer::Create();
layer_tree_host_->SetRootLayer(layer);
viz::SurfaceId primary_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseSpecifiedDeadline(1u));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseSpecifiedDeadline(2u));
layer->SetSurfaceId(primary_id, DeadlinePolicy::UseExistingDeadline());
layer->SetOldestAcceptableFallback(primary_id);
layer->SetBackgroundColor(SK_ColorBLUE);
layer->SetStretchContentToFillBounds(true);
EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u);
// Verify that pending tree has no surface ids already.
EXPECT_FALSE(host_impl_.pending_tree()->needs_surface_ranges_sync());
EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 0u);
std::unique_ptr<SurfaceLayerImpl> layer_impl =
SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer->id());
SynchronizeTrees();
// Verify that pending tree received the surface id and also has
// needs_surface_ranges_sync set to true as it needs to sync with active tree.
EXPECT_TRUE(host_impl_.pending_tree()->needs_surface_ranges_sync());
EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u);
// Verify we have reset the state on layer tree host.
EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
// Verify that the primary and fallback SurfaceIds are pushed through.
EXPECT_EQ(primary_id, layer_impl->range().end());
EXPECT_EQ(primary_id, layer_impl->range().start());
EXPECT_EQ(SK_ColorBLUE, layer_impl->background_color());
EXPECT_TRUE(layer_impl->stretch_content_to_fill_bounds());
EXPECT_EQ(2u, layer_impl->deadline_in_frames());
viz::SurfaceId fallback_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(2, base::UnguessableToken::Create()));
layer->SetOldestAcceptableFallback(fallback_id);
layer->SetSurfaceId(fallback_id, DeadlinePolicy::UseExistingDeadline());
layer->SetBackgroundColor(SK_ColorGREEN);
layer->SetStretchContentToFillBounds(false);
// Verify that fallback surface id is not recorded on the layer tree host as
// surface synchronization is not enabled.
EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
EXPECT_EQ(layer_tree_host_->SurfaceRanges().size(), 1u);
SynchronizeTrees();
EXPECT_EQ(host_impl_.pending_tree()->SurfaceRanges().size(), 1u);
// Verify that the primary viz::SurfaceId stays the same and the new
// fallback viz::SurfaceId is pushed through.
EXPECT_EQ(fallback_id, layer_impl->range().end());
EXPECT_EQ(fallback_id, layer_impl->range().start());
EXPECT_EQ(SK_ColorGREEN, layer_impl->background_color());
// The deadline resets back to 0 (no deadline) after the first commit.
EXPECT_EQ(0u, layer_impl->deadline_in_frames());
EXPECT_FALSE(layer_impl->stretch_content_to_fill_bounds());
}
// This test verifies the list of surface ids is correct when there are cloned
// surface layers. This emulates the flow of maximize and minimize animations on
// Chrome OS.
TEST_F(SurfaceLayerTest, CheckSurfaceReferencesForClonedLayer) {
const viz::SurfaceId old_surface_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
// This layer will always contain the old surface id and will be deleted when
// animation is done.
scoped_refptr<SurfaceLayer> layer1 = SurfaceLayer::Create();
layer1->SetLayerTreeHost(layer_tree_host_.get());
layer1->SetSurfaceId(old_surface_id, DeadlinePolicy::UseDefaultDeadline());
layer1->SetOldestAcceptableFallback(old_surface_id);
// This layer will eventually be switched be switched to show the new surface
// id and will be retained when animation is done.
scoped_refptr<SurfaceLayer> layer2 = SurfaceLayer::Create();
layer2->SetLayerTreeHost(layer_tree_host_.get());
layer2->SetSurfaceId(old_surface_id, DeadlinePolicy::UseDefaultDeadline());
layer2->SetOldestAcceptableFallback(old_surface_id);
std::unique_ptr<SurfaceLayerImpl> layer_impl1 =
SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer1->id());
std::unique_ptr<SurfaceLayerImpl> layer_impl2 =
SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer2->id());
SynchronizeTrees();
// Verify that only |old_surface_id| is going to be referenced.
EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(old_surface_id)));
EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(old_surface_id)));
const viz::SurfaceId new_surface_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(2, base::UnguessableToken::Create()));
// Switch the new layer to use |new_surface_id|.
layer2->SetSurfaceId(new_surface_id, DeadlinePolicy::UseDefaultDeadline());
layer2->SetOldestAcceptableFallback(new_surface_id);
SynchronizeTrees();
// Verify that both surface ids are going to be referenced.
EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(old_surface_id),
viz::SurfaceRange(new_surface_id)));
EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(old_surface_id),
viz::SurfaceRange(new_surface_id)));
// Unparent the old layer like it's being destroyed at the end of animation.
layer1->SetLayerTreeHost(nullptr);
SynchronizeTrees();
// Verify that only |new_surface_id| is going to be referenced.
EXPECT_THAT(layer_tree_host_->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(new_surface_id)));
EXPECT_THAT(host_impl_.pending_tree()->SurfaceRanges(),
ElementsAre(viz::SurfaceRange(new_surface_id)));
// Cleanup for destruction.
layer2->SetLayerTreeHost(nullptr);
}
// This test verifies LayerTreeHost::needs_surface_ranges_sync() is correct when
// there are cloned surface layers.
TEST_F(SurfaceLayerTest, CheckNeedsSurfaceIdsSyncForClonedLayers) {
const viz::SurfaceId surface_id(
kArbitraryFrameSinkId,
viz::LocalSurfaceId(1, base::UnguessableToken::Create()));
scoped_refptr<SurfaceLayer> layer1 = SurfaceLayer::Create();
layer1->SetLayerTreeHost(layer_tree_host_.get());
layer1->SetSurfaceId(surface_id, DeadlinePolicy::UseDefaultDeadline());
layer1->SetOldestAcceptableFallback(surface_id);
// Verify the surface id is in SurfaceLayerIds() and
// needs_surface_ranges_sync() is true.
EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1));
std::unique_ptr<SurfaceLayerImpl> layer_impl1 =
SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer1->id());
SynchronizeTrees();
// After syncchronizing trees verify needs_surface_ranges_sync() is false.
EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
// Create the second layer that is a clone of the first.
scoped_refptr<SurfaceLayer> layer2 = SurfaceLayer::Create();
layer2->SetLayerTreeHost(layer_tree_host_.get());
layer2->SetSurfaceId(surface_id, DeadlinePolicy::UseDefaultDeadline());
layer2->SetOldestAcceptableFallback(surface_id);
// Verify that after creating the second layer with the same surface id that
// needs_surface_ranges_sync() is still false.
EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(1));
std::unique_ptr<SurfaceLayerImpl> layer_impl2 =
SurfaceLayerImpl::Create(host_impl_.pending_tree(), layer2->id());
SynchronizeTrees();
// Verify needs_surface_ranges_sync() is still false after synchronizing
// trees.
EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
// Destroy one of the layers, leaving one layer with the surface id.
layer1->SetLayerTreeHost(nullptr);
// Verify needs_surface_ranges_sync() is still false.
EXPECT_FALSE(layer_tree_host_->needs_surface_ranges_sync());
// Destroy the last layer, this should change the set of layer surface ids.
layer2->SetLayerTreeHost(nullptr);
// Verify SurfaceLayerIds() is empty and needs_surface_ranges_sync() is true.
EXPECT_TRUE(layer_tree_host_->needs_surface_ranges_sync());
EXPECT_THAT(layer_tree_host_->SurfaceRanges(), SizeIs(0));
}
} // namespace
} // namespace cc