blob: 268f58ea8e219912d28a42cc93318ea9b86efacb [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <tuple>
#include "base/containers/contains.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_trace_processor.h"
#include "components/input/features.h"
#include "components/viz/common/constants.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/gl/mock_gpu_service_impl.h"
#include "components/viz/service/input/mock_input_manager.h"
#include "components/viz/service/input/render_input_router_iterator_impl.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/test/begin_frame_source_test.h"
#include "components/viz/test/compositor_frame_helpers.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
#include "components/viz/test/fake_surface_observer.h"
#include "components/viz/test/mock_compositor_frame_sink_client.h"
#include "components/viz/test/mock_display_client.h"
#include "components/viz/test/test_output_surface_provider.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/perfetto/include/perfetto/tracing/tracing.h"
namespace viz {
namespace {
constexpr FrameSinkId kFrameSinkIdRoot(1, 1);
constexpr FrameSinkId kFrameSinkIdRoot2(2, 2);
constexpr FrameSinkId kFrameSinkIdA(2, 1);
constexpr FrameSinkId kFrameSinkIdB(3, 1);
constexpr FrameSinkId kFrameSinkIdC(4, 1);
constexpr FrameSinkId kFrameSinkIdD(5, 1);
constexpr FrameSinkId kFrameSinkIdE(6, 1);
constexpr FrameSinkId kFrameSinkIdF(7, 1);
// Holds the four interface objects needed to create a RootCompositorFrameSink.
struct RootCompositorFrameSinkData {
mojom::RootCompositorFrameSinkParamsPtr BuildParams(
const FrameSinkId& frame_sink_id) {
auto params = mojom::RootCompositorFrameSinkParams::New();
params->frame_sink_id = frame_sink_id;
params->widget = gpu::kNullSurfaceHandle;
params->compositor_frame_sink =
compositor_frame_sink.BindNewEndpointAndPassReceiver();
params->compositor_frame_sink_client =
compositor_frame_sink_client.BindInterfaceRemote();
params->display_private = display_private.BindNewEndpointAndPassReceiver();
params->display_client = display_client.BindRemote();
return params;
}
mojo::AssociatedRemote<mojom::CompositorFrameSink> compositor_frame_sink;
MockCompositorFrameSinkClient compositor_frame_sink_client;
mojo::AssociatedRemote<mojom::DisplayPrivate> display_private;
MockDisplayClient display_client;
};
} // namespace
class FrameSinkManagerTest : public testing::Test {
public:
FrameSinkManagerTest() {
FrameSinkManagerImpl::InitParams init_params(&output_surface_provider_);
init_params.gpu_service = &gpu_service_;
manager_ = std::make_unique<FrameSinkManagerImpl>(std::move(init_params));
surface_observer_ =
std::make_unique<FakeSurfaceObserver>(manager_->surface_manager());
}
~FrameSinkManagerTest() override = default;
RootCompositorFrameSinkImpl* GetRootCompositorFrameSinkImpl() {
auto it = manager_->root_sink_map_.find(kFrameSinkIdRoot);
return it == manager_->root_sink_map_.end() ? nullptr : it->second.get();
}
std::unique_ptr<CompositorFrameSinkSupport> CreateCompositorFrameSinkSupport(
const FrameSinkId& frame_sink_id) {
return std::make_unique<CompositorFrameSinkSupport>(nullptr, manager_.get(),
frame_sink_id, false);
}
const BeginFrameSource* GetBeginFrameSource(
const std::unique_ptr<CompositorFrameSinkSupport>& support) {
return support->begin_frame_source_;
}
void ExpireAllTemporaryReferencesAndGarbageCollect() {
manager_->surface_manager()->ExpireOldTemporaryReferences();
manager_->surface_manager()->ExpireOldTemporaryReferences();
manager_->surface_manager()->GarbageCollectSurfaces();
}
// Checks if a [Root]CompositorFrameSinkImpl exists for |frame_sink_id|.
bool CompositorFrameSinkExists(const FrameSinkId& frame_sink_id) {
return base::Contains(manager_->sink_map_, frame_sink_id) ||
base::Contains(manager_->root_sink_map_, frame_sink_id);
}
CompositorFrameSinkSupport* GetFrameSinkSupport(const FrameSinkId& id) {
return manager_->support_map_.find(id)->second;
}
bool InputManagerExists() { return manager_->GetInputManager(); }
MockInputManager* GetMockInputManager() {
return static_cast<MockInputManager*>(manager_->GetInputManager());
}
CapturableFrameSink* FindCapturableFrameSink(const FrameSinkId& id) {
return manager_->FindCapturableFrameSink(VideoCaptureTarget(id));
}
// Verifies the frame sinks with provided id in |ids| are throttled at
// |interval|.
void VerifyThrottling(base::TimeDelta interval,
const std::vector<FrameSinkId>& ids) {
for (auto& id : ids) {
EXPECT_EQ(interval, manager_->support_map_[id]->begin_frame_interval_);
}
}
// Creates a CompositorFrameSinkImpl.
void CreateCompositorFrameSink(
const FrameSinkId& frame_sink_id,
input::mojom::RenderInputRouterConfigPtr config) {
MockCompositorFrameSinkClient compositor_frame_sink_client;
mojo::Remote<mojom::CompositorFrameSink> compositor_frame_sink;
manager_->CreateCompositorFrameSink(
frame_sink_id, /*bundle_id=*/std::nullopt,
compositor_frame_sink.BindNewPipeAndPassReceiver(),
compositor_frame_sink_client.BindInterfaceRemote(), std::move(config));
EXPECT_TRUE(CompositorFrameSinkExists(frame_sink_id));
}
input::mojom::RenderInputRouterConfigPtr CreateRIRConfig(
const base::UnguessableToken& grouping_id) {
auto config = input::mojom::RenderInputRouterConfig::New();
mojo::PendingReceiver<blink::mojom::RenderInputRouterClient>
rir_client_receiver;
config->rir_client = rir_client_receiver.InitWithNewPipeAndPassRemote();
config->grouping_id = grouping_id;
return config;
}
base::flat_set<FrameSinkId> GetEmbeddedRenderInputRouters(
const FrameSinkId& frame_sink_id) {
auto rir_iterator =
GetMockInputManager()->GetEmbeddedRenderInputRouters(frame_sink_id);
return static_cast<RenderInputRouterIteratorImpl*>(rir_iterator.get())
->GetRenderInputRoutersForTesting();
}
// testing::Test implementation.
void SetUp() override {
manager_->SetInputManagerForTesting(
std::make_unique<MockInputManager>(manager_.get()));
}
// testing::Test implementation.
void TearDown() override {
// Make sure that all FrameSinkSourceMappings have been deleted.
EXPECT_TRUE(manager_->frame_sink_source_map_.empty());
// Make sure test cleans up all [Root]CompositorFrameSinkImpls.
EXPECT_TRUE(manager_->support_map_.empty());
// Make sure test has invalidated all registered FrameSinkIds.
EXPECT_TRUE(manager_->frame_sink_data_.empty());
}
protected:
DebugRendererSettings debug_settings_;
TestOutputSurfaceProvider output_surface_provider_;
std::unique_ptr<FrameSinkManagerImpl> manager_;
std::unique_ptr<FakeSurfaceObserver> surface_observer_;
base::test::ScopedFeatureList scoped_feature_list_;
private:
MockGpuServiceImpl gpu_service_;
};
TEST_F(FrameSinkManagerTest, CreateRootCompositorFrameSink) {
manager_->RegisterFrameSinkId(kFrameSinkIdRoot, true /* report_activation */);
// Create a RootCompositorFrameSinkImpl.
RootCompositorFrameSinkData root_data;
manager_->CreateRootCompositorFrameSink(
root_data.BuildParams(kFrameSinkIdRoot));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot));
// Invalidating should destroy the RootCompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdRoot));
}
TEST_F(FrameSinkManagerTest, InputManagerCreation) {
ASSERT_FALSE(input::InputUtils::IsTransferInputToVizSupported());
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA,
/* render_input_router_config= */ nullptr);
// InputManager is not created since IsTransferInputToVizSupported() returns
// false.
EXPECT_FALSE(InputManagerExists());
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
}
TEST_F(FrameSinkManagerTest, CreateCompositorFrameSink) {
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA,
/* render_input_router_config= */ nullptr);
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
}
TEST_F(FrameSinkManagerTest, CompositorFrameSinkConnectionLost) {
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
// Create a CompositorFrameSinkImpl.
MockCompositorFrameSinkClient compositor_frame_sink_client;
mojo::Remote<mojom::CompositorFrameSink> compositor_frame_sink;
manager_->CreateCompositorFrameSink(
kFrameSinkIdA, /*bundle_id=*/std::nullopt,
compositor_frame_sink.BindNewPipeAndPassReceiver(),
compositor_frame_sink_client.BindInterfaceRemote(),
/* render_input_router_config= */ nullptr);
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdA));
// Close the connection from the renderer.
compositor_frame_sink.reset();
// Closing the connection will destroy the CompositorFrameSinkImpl along with
// the mojom::CompositorFrameSinkClient binding.
base::RunLoop run_loop;
compositor_frame_sink_client.set_disconnect_handler(run_loop.QuitClosure());
run_loop.Run();
// Check that the CompositorFrameSinkImpl was destroyed.
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
}
TEST_F(FrameSinkManagerTest, SingleClients) {
auto client = CreateCompositorFrameSinkSupport(FrameSinkId(1, 1));
auto other_client = CreateCompositorFrameSinkSupport(FrameSinkId(2, 2));
StubBeginFrameSource source;
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
EXPECT_EQ(nullptr, GetBeginFrameSource(other_client));
// Test setting unsetting BFS
manager_->RegisterBeginFrameSource(&source, client->frame_sink_id());
EXPECT_EQ(&source, GetBeginFrameSource(client));
EXPECT_EQ(nullptr, GetBeginFrameSource(other_client));
manager_->UnregisterBeginFrameSource(&source);
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
EXPECT_EQ(nullptr, GetBeginFrameSource(other_client));
// Set BFS for other namespace
manager_->RegisterBeginFrameSource(&source, other_client->frame_sink_id());
EXPECT_EQ(&source, GetBeginFrameSource(other_client));
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
manager_->UnregisterBeginFrameSource(&source);
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
EXPECT_EQ(nullptr, GetBeginFrameSource(other_client));
// Re-set BFS for original
manager_->RegisterBeginFrameSource(&source, client->frame_sink_id());
EXPECT_EQ(&source, GetBeginFrameSource(client));
manager_->UnregisterBeginFrameSource(&source);
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
}
// This test verifies that a client is still connected to the BeginFrameSource
// after restart.
TEST_F(FrameSinkManagerTest, ClientRestart) {
auto client = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
StubBeginFrameSource source;
manager_->RegisterBeginFrameSource(&source, kFrameSinkIdRoot);
EXPECT_EQ(&source, GetBeginFrameSource(client));
client.reset();
// |client| is reconnected with |source| after being recreated..
client = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
EXPECT_EQ(&source, GetBeginFrameSource(client));
manager_->UnregisterBeginFrameSource(&source);
EXPECT_EQ(nullptr, GetBeginFrameSource(client));
}
TEST_F(FrameSinkManagerTest, MultipleDisplays) {
StubBeginFrameSource root1_source;
StubBeginFrameSource root2_source;
// root1 -> A -> B
// root2 -> C
auto root1 = CreateCompositorFrameSinkSupport(FrameSinkId(1, 1));
auto root2 = CreateCompositorFrameSinkSupport(FrameSinkId(2, 2));
auto client_a = CreateCompositorFrameSinkSupport(FrameSinkId(3, 3));
auto client_b = CreateCompositorFrameSinkSupport(FrameSinkId(4, 4));
auto client_c = CreateCompositorFrameSinkSupport(FrameSinkId(5, 5));
manager_->RegisterBeginFrameSource(&root1_source, root1->frame_sink_id());
manager_->RegisterBeginFrameSource(&root2_source, root2->frame_sink_id());
EXPECT_EQ(GetBeginFrameSource(root1), &root1_source);
EXPECT_EQ(GetBeginFrameSource(root2), &root2_source);
// Set up initial hierarchy.
manager_->RegisterFrameSinkHierarchy(root1->frame_sink_id(),
client_a->frame_sink_id());
EXPECT_EQ(GetBeginFrameSource(client_a), GetBeginFrameSource(root1));
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
EXPECT_EQ(GetBeginFrameSource(client_b), GetBeginFrameSource(root1));
manager_->RegisterFrameSinkHierarchy(root2->frame_sink_id(),
client_c->frame_sink_id());
EXPECT_EQ(GetBeginFrameSource(client_c), GetBeginFrameSource(root2));
// Attach A into root2's subtree, like a window moving across displays.
// root1 -> A -> B
// root2 -> C -> A -> B
manager_->RegisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_a->frame_sink_id());
// With the heuristic of just keeping existing BFS in the face of multiple,
// no client sources should change.
EXPECT_EQ(GetBeginFrameSource(client_a), GetBeginFrameSource(root1));
EXPECT_EQ(GetBeginFrameSource(client_b), GetBeginFrameSource(root1));
EXPECT_EQ(GetBeginFrameSource(client_c), GetBeginFrameSource(root2));
// Detach A from root1-> A and B should now be updated to root2->
manager_->UnregisterFrameSinkHierarchy(root1->frame_sink_id(),
client_a->frame_sink_id());
EXPECT_EQ(GetBeginFrameSource(client_a), GetBeginFrameSource(root2));
EXPECT_EQ(GetBeginFrameSource(client_b), GetBeginFrameSource(root2));
EXPECT_EQ(GetBeginFrameSource(client_c), GetBeginFrameSource(root2));
// Detach root1 from BFS. root1 should now have no source.
manager_->UnregisterBeginFrameSource(&root1_source);
EXPECT_EQ(nullptr, GetBeginFrameSource(root1));
EXPECT_NE(nullptr, GetBeginFrameSource(root2));
// Detach root2 from BFS.
manager_->UnregisterBeginFrameSource(&root2_source);
EXPECT_EQ(nullptr, GetBeginFrameSource(client_a));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_b));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_c));
EXPECT_EQ(nullptr, GetBeginFrameSource(root2));
// Cleanup hierarchy.
manager_->UnregisterFrameSinkHierarchy(root2->frame_sink_id(),
client_c->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
}
TEST_F(FrameSinkManagerTest, FrameSinkParentChildRelationship) {
// Create 2 RootCompositorFrameSinks.
RootCompositorFrameSinkData root_data1;
manager_->CreateRootCompositorFrameSink(
root_data1.BuildParams(kFrameSinkIdRoot));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot));
RootCompositorFrameSinkData root_data2;
manager_->CreateRootCompositorFrameSink(
root_data2.BuildParams(kFrameSinkIdRoot2));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot2));
auto* root1 = GetFrameSinkSupport(kFrameSinkIdRoot);
auto* root2 = GetFrameSinkSupport(kFrameSinkIdRoot2);
auto client_a = CreateCompositorFrameSinkSupport(FrameSinkId(3, 3));
auto client_b = CreateCompositorFrameSinkSupport(FrameSinkId(4, 4));
auto client_c = CreateCompositorFrameSinkSupport(FrameSinkId(5, 5));
auto client_d = CreateCompositorFrameSinkSupport(FrameSinkId(6, 6));
auto client_e = CreateCompositorFrameSinkSupport(FrameSinkId(7, 7));
// Set up initial hierarchy.
// root1 -> A -> B -> C
// + -> D
// root2 -> E
manager_->RegisterFrameSinkHierarchy(root1->frame_sink_id(),
client_a->frame_sink_id());
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_a->frame_sink_id()),
root1->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_b->frame_sink_id()),
client_a->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_c->frame_sink_id());
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_c->frame_sink_id()),
client_b->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_d->frame_sink_id());
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_d->frame_sink_id()),
client_b->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(root2->frame_sink_id(),
client_e->frame_sink_id());
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_e->frame_sink_id()),
root2->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_a->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_b->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_c->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_d->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_e->frame_sink_id()),
root2->frame_sink_id());
// Attach A into root2's subtree, like a window moving across displays.
// root1 -> A -> B -> C
// + -> D
// root2 -> E -> A -> B -> C
// + -> D
manager_->RegisterFrameSinkHierarchy(client_e->frame_sink_id(),
client_a->frame_sink_id());
// With the heuristic of just keeping existing parent in the face of multiple,
// no client's corresponding RootCompositorFrameSink should change.
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_a->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_b->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_c->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_d->frame_sink_id()),
root1->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_e->frame_sink_id()),
root2->frame_sink_id());
// Detach A from root1.
manager_->UnregisterFrameSinkHierarchy(root1->frame_sink_id(),
client_a->frame_sink_id());
// root1
// root2 -> E -> A -> B -> C
// + -> D
EXPECT_EQ(manager_->GetOldestParentByChildFrameId(client_a->frame_sink_id()),
client_e->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_a->frame_sink_id()),
root2->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_b->frame_sink_id()),
root2->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_c->frame_sink_id()),
root2->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_d->frame_sink_id()),
root2->frame_sink_id());
EXPECT_EQ(
manager_->GetOldestRootCompositorFrameSinkId(client_e->frame_sink_id()),
root2->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(root2->frame_sink_id(),
client_e->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_e->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_d->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_c->frame_sink_id());
// Delete RootCompositorFrameSinks.
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot2, {});
}
// This test verifies that a BeginFrameSource path to the root from a
// FrameSinkId is preserved even if that FrameSinkId has no children
// and does not have a corresponding CompositorFrameSinkSupport.
TEST_F(FrameSinkManagerTest, ParentWithoutClientRetained) {
StubBeginFrameSource root_source;
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
manager_->RegisterBeginFrameSource(&root_source, root->frame_sink_id());
EXPECT_EQ(&root_source, GetBeginFrameSource(root));
// Set up initial hierarchy: root -> A -> B.
// Note that A does not have a CompositorFrameSinkSupport.
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
// The root's BeginFrameSource should propagate to B.
EXPECT_EQ(GetBeginFrameSource(root), GetBeginFrameSource(client_b));
// Unregister B, and attach C to A: root -> A -> C
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
EXPECT_EQ(nullptr, GetBeginFrameSource(client_b));
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
// The root's BeginFrameSource should propagate to C.
EXPECT_EQ(GetBeginFrameSource(root), GetBeginFrameSource(client_c));
manager_->UnregisterBeginFrameSource(&root_source);
EXPECT_EQ(nullptr, GetBeginFrameSource(root));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_c));
// Unregister all registered hierarchy.
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
}
// This test sets up the same hierarchy as ParentWithoutClientRetained.
// However, this unit test registers the BeginFrameSource AFTER C
// has been attached to A. This test verifies that the BeginFrameSource
// propagates all the way to C.
TEST_F(FrameSinkManagerTest,
ParentWithoutClientRetained_LateBeginFrameRegistration) {
StubBeginFrameSource root_source;
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
// Set up initial hierarchy: root -> A -> B.
// Note that A does not have a CompositorFrameSinkSupport.
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
// The root does not yet have a BeginFrameSource so client B should not have
// one either.
EXPECT_EQ(nullptr, GetBeginFrameSource(client_b));
// Unregister B, and attach C to A: root -> A -> C
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
// Registering a BeginFrameSource at the root should propagate it to C.
manager_->RegisterBeginFrameSource(&root_source, root->frame_sink_id());
// The root's BeginFrameSource should propagate to C.
EXPECT_EQ(&root_source, GetBeginFrameSource(root));
EXPECT_EQ(GetBeginFrameSource(root), GetBeginFrameSource(client_c));
manager_->UnregisterBeginFrameSource(&root_source);
EXPECT_EQ(nullptr, GetBeginFrameSource(root));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_c));
// Unregister all registered hierarchy.
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdC);
}
// Verifies that the SurfaceIds passed to EvictSurfaces will be destroyed in the
// next garbage collection.
TEST_F(FrameSinkManagerTest, EvictSurfaces) {
ParentLocalSurfaceIdAllocator allocator1;
ParentLocalSurfaceIdAllocator allocator2;
allocator1.GenerateId();
LocalSurfaceId local_surface_id1 = allocator1.GetCurrentLocalSurfaceId();
allocator2.GenerateId();
LocalSurfaceId local_surface_id2 = allocator2.GetCurrentLocalSurfaceId();
SurfaceId surface_id1(kFrameSinkIdA, local_surface_id1);
SurfaceId surface_id2(kFrameSinkIdB, local_surface_id2);
// Create two frame sinks. Each create a surface.
auto sink1 = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
auto sink2 = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
sink1->SubmitCompositorFrame(local_surface_id1, MakeDefaultCompositorFrame());
sink2->SubmitCompositorFrame(local_surface_id2, MakeDefaultCompositorFrame());
// |surface_id1| and |surface_id2| should remain alive after garbage
// collection because they're not marked for destruction.
ExpireAllTemporaryReferencesAndGarbageCollect();
EXPECT_TRUE(manager_->surface_manager()->GetSurfaceForId(surface_id1));
EXPECT_TRUE(manager_->surface_manager()->GetSurfaceForId(surface_id2));
// Call EvictSurfaces. Now the garbage collector can destroy the surfaces.
manager_->EvictSurfaces({surface_id1, surface_id2});
// Garbage collection is synchronous.
EXPECT_FALSE(manager_->surface_manager()->GetSurfaceForId(surface_id1));
EXPECT_FALSE(manager_->surface_manager()->GetSurfaceForId(surface_id1));
}
// Verify that setting debug label works and that debug labels are cleared when
// FrameSinkId is invalidated.
TEST_F(FrameSinkManagerTest, DebugLabel) {
const std::string label = "Test Label";
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
manager_->SetFrameSinkDebugLabel(kFrameSinkIdA, label);
EXPECT_EQ(label, manager_->GetFrameSinkDebugLabel(kFrameSinkIdA));
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_EQ("", manager_->GetFrameSinkDebugLabel(kFrameSinkIdA));
}
// Verifies the the begin frames are throttled properly for the requested frame
// sinks and their children.
TEST_F(FrameSinkManagerTest, Throttle) {
// root -> A -> B
// -> C -> D
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
auto client_d = CreateCompositorFrameSinkSupport(kFrameSinkIdD);
// Set up the hierarchy.
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_c->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_d->frame_sink_id());
constexpr base::TimeDelta interval = base::Hertz(20);
std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB,
kFrameSinkIdC, kFrameSinkIdD};
// By default, a CompositorFrameSinkSupport shouldn't have its
// |begin_frame_interval| set.
VerifyThrottling(base::TimeDelta(), ids);
manager_->Throttle({kFrameSinkIdRoot}, interval);
VerifyThrottling(interval, ids);
manager_->Throttle({}, base::TimeDelta());
VerifyThrottling(base::TimeDelta(), ids);
manager_->Throttle({kFrameSinkIdB, kFrameSinkIdC}, interval);
VerifyThrottling(interval, {kFrameSinkIdB, kFrameSinkIdC, kFrameSinkIdD});
VerifyThrottling(base::TimeDelta(), {kFrameSinkIdA, kFrameSinkIdRoot});
manager_->Throttle({}, base::TimeDelta());
VerifyThrottling(base::TimeDelta(), ids);
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_c->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_d->frame_sink_id());
}
TEST_F(FrameSinkManagerTest, GlobalThrottle) {
// root -> A -> B
// -> C -> D
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
auto client_d = CreateCompositorFrameSinkSupport(kFrameSinkIdD);
// Set up the hierarchy.
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_c->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_d->frame_sink_id());
constexpr base::TimeDelta global_interval = base::Hertz(30);
constexpr base::TimeDelta interval = base::Hertz(20);
std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB,
kFrameSinkIdC, kFrameSinkIdD};
// By default, a CompositorFrameSinkSupport shouldn't have its
// |begin_frame_interval| set.
VerifyThrottling(base::TimeDelta(), ids);
// Starting global throttling should throttle the entire hierarchy.
manager_->StartThrottlingAllFrameSinks(global_interval);
VerifyThrottling(global_interval, ids);
// Throttling more aggressively on top of global throttling should further
// throttle the specified frame sink hierarchy, but preserve global throttling
// on the unaffected framesinks.
manager_->Throttle({kFrameSinkIdC}, interval);
VerifyThrottling(global_interval,
{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB});
VerifyThrottling(interval, {kFrameSinkIdC, kFrameSinkIdD});
// Attempting to per-sink throttle to an interval shorter than the global
// throttling should still throttle all frame sinks to the global interval.
manager_->Throttle({kFrameSinkIdA}, base::Hertz(40));
VerifyThrottling(global_interval, ids);
// Add a new branch to the hierarchy. These new frame sinks should be globally
// throttled immediately. root -> A -> B
// -> C -> D
// -> E -> F
auto client_e = CreateCompositorFrameSinkSupport(kFrameSinkIdE);
auto client_f = CreateCompositorFrameSinkSupport(kFrameSinkIdF);
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_e->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_e->frame_sink_id(),
client_f->frame_sink_id());
VerifyThrottling(
global_interval,
{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB, kFrameSinkIdC,
kFrameSinkIdD, kFrameSinkIdE, kFrameSinkIdF});
// Disabling global throttling should revert back to only the up-to-date
// per-frame sink throttling.
manager_->StopThrottlingAllFrameSinks();
VerifyThrottling(base::Hertz(40), {kFrameSinkIdA, kFrameSinkIdB});
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_c->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_c->frame_sink_id(),
client_d->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_e->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_e->frame_sink_id(),
client_f->frame_sink_id());
}
// Verifies if a frame sink is being captured, it should not be throttled.
TEST_F(FrameSinkManagerTest, NoThrottleOnFrameSinksBeingCaptured) {
// root -> A -> B -> C
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
auto client_c = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
// Set up the hierarchy.
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_c->frame_sink_id());
constexpr base::TimeDelta interval = base::Hertz(20);
std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB,
kFrameSinkIdC};
// By default, a CompositorFrameSinkSupport shouldn't have its
// |begin_frame_interval| set.
VerifyThrottling(base::TimeDelta(), ids);
// Throttle all frame sinks.
manager_->Throttle({kFrameSinkIdRoot}, interval);
VerifyThrottling(interval, ids);
// Start capturing frame sink B.
CapturableFrameSink* capturable_frame_sink =
FindCapturableFrameSink(kFrameSinkIdB);
capturable_frame_sink->OnClientCaptureStarted();
// Throttling should be stopped on frame sink B and its child C, but not
// affected on root frame sink and frame sink A.
VerifyThrottling(interval, {kFrameSinkIdRoot, kFrameSinkIdA});
VerifyThrottling(base::TimeDelta(), {kFrameSinkIdB, kFrameSinkIdC});
// Explicitly request to throttle all frame sinks. This would not affect B or
// C while B is still being captured.
manager_->Throttle(ids, interval);
VerifyThrottling(interval, {kFrameSinkIdRoot, kFrameSinkIdA});
VerifyThrottling(base::TimeDelta(), {kFrameSinkIdB, kFrameSinkIdC});
// Stop capturing.
capturable_frame_sink->OnClientCaptureStopped();
// Now the throttling state should be the same as before capturing started,
// i.e. all frame sinks will now be throttled.
VerifyThrottling(interval, ids);
manager_->Throttle({}, base::TimeDelta());
VerifyThrottling(base::TimeDelta(), ids);
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_b->frame_sink_id(),
client_c->frame_sink_id());
}
// Verifies if throttling on frame sinks is updated properly when hierarchy
// changes.
TEST_F(FrameSinkManagerTest, ThrottleUponHierarchyChange) {
// root -> A -> B
auto root = CreateCompositorFrameSinkSupport(kFrameSinkIdRoot);
auto client_a = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
auto client_b = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
// Set up the hierarchy.
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->RegisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
constexpr base::TimeDelta interval = base::Hertz(20);
std::vector<FrameSinkId> ids{kFrameSinkIdRoot, kFrameSinkIdA, kFrameSinkIdB};
// Throttle the root frame sink.
manager_->Throttle({kFrameSinkIdRoot}, interval);
// All frame sinks should now be throttled.
VerifyThrottling(interval, ids);
// Unparent A from root. Root should remain throttled while A and B should be
// unthrottled.
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
VerifyThrottling(interval, {kFrameSinkIdRoot});
VerifyThrottling(base::TimeDelta(), {kFrameSinkIdA, kFrameSinkIdB});
// Reparent A to root. Both A and B should now be throttled along with root.
manager_->RegisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
VerifyThrottling(interval, ids);
// Unthrottle all frame sinks.
manager_->Throttle({}, base::TimeDelta());
VerifyThrottling(base::TimeDelta(), ids);
manager_->UnregisterFrameSinkHierarchy(root->frame_sink_id(),
client_a->frame_sink_id());
manager_->UnregisterFrameSinkHierarchy(client_a->frame_sink_id(),
client_b->frame_sink_id());
}
TEST_F(FrameSinkManagerTest, EvictRootSurfaceId) {
manager_->RegisterFrameSinkId(kFrameSinkIdRoot, true /* report_activation */);
// Create a RootCompositorFrameSinkImpl.
RootCompositorFrameSinkData root_data;
manager_->CreateRootCompositorFrameSink(
root_data.BuildParams(kFrameSinkIdRoot));
GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20));
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId();
const SurfaceId surface_id(kFrameSinkIdRoot, local_surface_id);
GetRootCompositorFrameSinkImpl()->SubmitCompositorFrame(
local_surface_id, MakeDefaultCompositorFrame(), std::nullopt, 0);
EXPECT_EQ(surface_id, GetRootCompositorFrameSinkImpl()->CurrentSurfaceId());
manager_->EvictSurfaces({surface_id});
EXPECT_FALSE(GetRootCompositorFrameSinkImpl()->CurrentSurfaceId().is_valid());
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
}
TEST_F(FrameSinkManagerTest, EvictNewerRootSurfaceId) {
manager_->RegisterFrameSinkId(kFrameSinkIdRoot, true /* report_activation */);
// Create a RootCompositorFrameSinkImpl.
RootCompositorFrameSinkData root_data;
manager_->CreateRootCompositorFrameSink(
root_data.BuildParams(kFrameSinkIdRoot));
GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20));
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId();
const SurfaceId surface_id(kFrameSinkIdRoot, local_surface_id);
GetRootCompositorFrameSinkImpl()->SubmitCompositorFrame(
local_surface_id, MakeDefaultCompositorFrame(), std::nullopt, 0);
EXPECT_EQ(surface_id, GetRootCompositorFrameSinkImpl()->CurrentSurfaceId());
allocator.GenerateId();
const LocalSurfaceId next_local_surface_id =
allocator.GetCurrentLocalSurfaceId();
manager_->EvictSurfaces({{kFrameSinkIdRoot, next_local_surface_id}});
EXPECT_FALSE(GetRootCompositorFrameSinkImpl()->CurrentSurfaceId().is_valid());
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
}
TEST_F(FrameSinkManagerTest, SubmitCompositorFrameWithEvictedSurfaceId) {
manager_->RegisterFrameSinkId(kFrameSinkIdRoot, true /* report_activation */);
// Create a RootCompositorFrameSinkImpl.
RootCompositorFrameSinkData root_data;
manager_->CreateRootCompositorFrameSink(
root_data.BuildParams(kFrameSinkIdRoot));
GetRootCompositorFrameSinkImpl()->Resize(gfx::Size(20, 20));
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const LocalSurfaceId local_surface_id = allocator.GetCurrentLocalSurfaceId();
const SurfaceId surface_id(kFrameSinkIdRoot, local_surface_id);
allocator.GenerateId();
const LocalSurfaceId local_surface_id2 = allocator.GetCurrentLocalSurfaceId();
const SurfaceId surface_id2(kFrameSinkIdRoot, local_surface_id2);
GetRootCompositorFrameSinkImpl()->SubmitCompositorFrame(
local_surface_id, MakeDefaultCompositorFrame(), std::nullopt, 0);
EXPECT_EQ(surface_id, GetRootCompositorFrameSinkImpl()->CurrentSurfaceId());
manager_->EvictSurfaces({surface_id, surface_id2});
EXPECT_FALSE(GetRootCompositorFrameSinkImpl()->CurrentSurfaceId().is_valid());
GetRootCompositorFrameSinkImpl()->SubmitCompositorFrame(
local_surface_id2, MakeDefaultCompositorFrame(), std::nullopt, 0);
// Even though `surface_id2` was just submitted, Display should not reference
// it because it was evicted.
EXPECT_NE(surface_id2, GetRootCompositorFrameSinkImpl()->CurrentSurfaceId());
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
}
// Test that `FrameSinkManagerImpl::DiscardPendingCopyOfOutputRequests`
// relocates the exact `PendingCopyOutputRequest`s to the target surfaces.
TEST_F(FrameSinkManagerTest,
CopyOutputRequestPreservedAfterDiscardPendingCopyOfOutputRequests) {
StubBeginFrameSource source;
manager_->RegisterBeginFrameSource(&source, kFrameSinkIdA);
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA,
/* render_input_router_config= */ nullptr);
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const auto id1 = allocator.GetCurrentLocalSurfaceId();
const auto surface_id1 = SurfaceId(kFrameSinkIdA, id1);
manager_->GetFrameSinkForId(kFrameSinkIdA)
->SubmitCompositorFrame(id1, MakeDefaultCompositorFrame());
auto* surface1 = manager_->surface_manager()->GetSurfaceForId(surface_id1);
ASSERT_TRUE(surface1);
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA,
CopyOutputRequest::ResultDestination::kSystemMemory,
base::BindOnce([](std::unique_ptr<CopyOutputResult> result) {}));
auto* request_ptr = request.get();
manager_->RequestCopyOfOutput(surface_id1, std::move(request),
/*capture_exact_surface_id=*/true);
manager_->DiscardPendingCopyOfOutputRequests(&source);
ASSERT_TRUE(surface_observer_->IsSurfaceDamaged(surface_id1));
// `request` is emplaced at the end of the root RenderPass.
const auto& preserved_request = *(
surface1->GetActiveFrame().render_pass_list.back()->copy_requests.back());
// Expect the identical `CopyOutputRequest`.
ASSERT_EQ(&preserved_request, request_ptr);
// For `manager_->CreateCompositorFrameSink`.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
// For `manager_->RegisterBeginFrameSource`.
manager_->UnregisterBeginFrameSource(&source);
}
// Submit an exact copy request while there is no frame sink. Such request can
// only be picked up by the specified surface.
TEST_F(FrameSinkManagerTest, ExactCopyOutputRequestTakenBySurfaceRightAway) {
StubBeginFrameSource source;
manager_->RegisterBeginFrameSource(&source, kFrameSinkIdA);
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA,
/* render_input_router_config= */ nullptr);
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const auto id1 = allocator.GetCurrentLocalSurfaceId();
const auto surface_id1 = SurfaceId(kFrameSinkIdA, id1);
manager_->GetFrameSinkForId(kFrameSinkIdA)
->SubmitCompositorFrame(id1, MakeDefaultCompositorFrame());
auto* surface1 = manager_->surface_manager()->GetSurfaceForId(surface_id1);
ASSERT_TRUE(surface1);
// Invalidate the frame sink after we create the surface. This makes sure
// the exact request can only be taken by the exact surface, instead of being
// queued in the `CompositorFrameSinkSupport`.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA,
CopyOutputRequest::ResultDestination::kSystemMemory,
base::BindOnce([](std::unique_ptr<CopyOutputResult> result) {}));
auto* request_ptr = request.get();
manager_->RequestCopyOfOutput(surface_id1, std::move(request),
/*capture_exact_surface_id=*/true);
ASSERT_TRUE(surface_observer_->IsSurfaceDamaged(surface_id1));
// `request` is emplaced at the end of the root RenderPass.
const auto& preserved_request = *(
surface1->GetActiveFrame().render_pass_list.back()->copy_requests.back());
// Expect the identical `CopyOutputRequest`.
ASSERT_EQ(&preserved_request, request_ptr);
// Already de-registered `kFrameSinkIdA`.
// For `manager_->RegisterBeginFrameSource`.
manager_->UnregisterBeginFrameSource(&source);
}
// Submit an exact copy request while there is no specified surface. Such
// request will be queued in the `CompositorFrameSinkSupport`, just like the
// non-exact requests.
TEST_F(FrameSinkManagerTest,
ExactCopyOutputRequestQueuedInCompositorFrameSinkSupport) {
StubBeginFrameSource source;
manager_->RegisterBeginFrameSource(&source, kFrameSinkIdA);
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA,
/* render_input_router_config= */ nullptr);
ParentLocalSurfaceIdAllocator allocator;
allocator.GenerateId();
const auto id1 = allocator.GetCurrentLocalSurfaceId();
const auto surface_id1 = SurfaceId(kFrameSinkIdA, id1);
// Submit the request.
auto request = std::make_unique<CopyOutputRequest>(
CopyOutputRequest::ResultFormat::RGBA,
CopyOutputRequest::ResultDestination::kSystemMemory,
base::BindOnce([](std::unique_ptr<CopyOutputResult> result) {}));
auto* request_ptr = request.get();
manager_->RequestCopyOfOutput(surface_id1, std::move(request),
/*capture_exact_surface_id=*/true);
// Won't be marked because the surface does not exist.
ASSERT_FALSE(surface_observer_->IsSurfaceDamaged(surface_id1));
auto* cfss = manager_->GetFrameSinkForId(kFrameSinkIdA);
ASSERT_TRUE(cfss);
// Since this is an exact request, it can only be taken by the matching
// `LocalSurfaceId`.
auto requests = cfss->TakeCopyOutputRequests(id1);
ASSERT_EQ(requests.size(), 1u);
ASSERT_EQ(requests[0].copy_output_request.get(), request_ptr);
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
manager_->UnregisterBeginFrameSource(&source);
}
#if BUILDFLAG(IS_ANDROID)
class AndroidFrameSinkManagerTest : public FrameSinkManagerTest,
public ::testing::WithParamInterface<bool> {
public:
AndroidFrameSinkManagerTest() {
scoped_feature_list_.InitWithFeatureState(input::features::kInputOnViz,
/* enabled= */ GetParam());
}
bool ExpectedInputManagerCreation() {
return input::InputUtils::IsTransferInputToVizSupported();
}
bool IsRenderInputRouterSupportChildFrame(const FrameSinkId& frame_sink_id) {
auto* mock_input_manager = GetMockInputManager();
EXPECT_TRUE(mock_input_manager);
auto* support = mock_input_manager->GetSupportForFrameSink(frame_sink_id);
EXPECT_TRUE(support);
return support->IsRenderInputRouterSupportChildFrame();
}
private:
base::test::TracingEnvironment tracing_environment_;
};
TEST_P(AndroidFrameSinkManagerTest, InputManagerCreation) {
EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
}
TEST_P(AndroidFrameSinkManagerTest, RenderInputRouterLifecycle) {
EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
base::test::TestTraceProcessor ttp;
ttp.StartTrace("viz");
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
// Create a grouping id.
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
if (InputManagerExists()) {
EXPECT_TRUE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdA));
}
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
if (InputManagerExists()) {
EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdA));
}
absl::Status status = ttp.StopAndParseTrace();
EXPECT_TRUE(status.ok()) << status.message();
std::string query = R"(
SELECT COUNT(*) AS cnt,
EXTRACT_ARG(arg_set_id, 'debug.config_is_null') as config_is_null,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
as client_id,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
FROM slice
WHERE slice.name = 'InputManager::OnCreateCompositorFrameSink'
)";
auto result = ttp.RunQuery(query);
EXPECT_TRUE(result.has_value());
// `result.value()` would look something like this: {{"cnt", "config_is_null",
// "client_id", "sink_id"}, {"<num>", "<boolean>" "<clientId>", "<sinkId>"}}.
EXPECT_EQ(result.value().size(), 2u);
EXPECT_EQ(result.value()[1].size(), 4u);
if (input::InputUtils::IsTransferInputToVizSupported()) {
// Checks if `InputManger::OnCreateCompositorFrameSink` was called for
// kFrameSinkIdA.
EXPECT_THAT(
result.value(),
testing::ElementsAre(
testing::ElementsAre("cnt", "config_is_null", "client_id",
"sink_id"),
testing::ElementsAre(
"1", "0", base::NumberToString(kFrameSinkIdA.client_id()),
base::NumberToString(kFrameSinkIdA.sink_id()))));
} else {
EXPECT_EQ(result.value()[1][0], "0");
}
std::string query2 = R"(
SELECT COUNT(*) AS cnt,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
as client_id,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
FROM slice
WHERE slice.name = 'InputManager::OnDestroyedCompositorFrameSink'
)";
auto result2 = ttp.RunQuery(query2);
EXPECT_TRUE(result2.has_value());
if (input::InputUtils::IsTransferInputToVizSupported()) {
EXPECT_THAT(result2.value(),
testing::ElementsAre(
testing::ElementsAre("cnt", "client_id", "sink_id"),
testing::ElementsAre(
"1", base::NumberToString(kFrameSinkIdA.client_id()),
base::NumberToString(kFrameSinkIdA.sink_id()))));
} else {
EXPECT_EQ(result2.value()[1][0], "0");
}
}
TEST_P(AndroidFrameSinkManagerTest,
RenderInputRouterLifecycleNonLayerFrameSink) {
EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
base::test::TestTraceProcessor ttp;
ttp.StartTrace("viz");
// Register a non layer tree frame sink.
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
CreateCompositorFrameSink(kFrameSinkIdB,
/* render_input_router_config= */ nullptr);
if (InputManagerExists()) {
// RIR should not be created for non layer tree frame sink.
EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdB));
}
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdB));
if (InputManagerExists()) {
EXPECT_FALSE(GetMockInputManager()->RIRExistsForFrameSinkId(kFrameSinkIdB));
}
absl::Status status = ttp.StopAndParseTrace();
EXPECT_TRUE(status.ok()) << status.message();
std::string query = R"(
SELECT COUNT(*) AS cnt,
EXTRACT_ARG(arg_set_id, 'debug.config_is_null') as config_is_null,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_client_id')
as client_id,
EXTRACT_ARG(arg_set_id, 'debug.frame_sink_id.frame_sink_id') as sink_id
FROM slice
WHERE slice.name = 'InputManager::OnCreateCompositorFrameSink'
)";
auto result = ttp.RunQuery(query);
EXPECT_TRUE(result.has_value());
// `result.value()` would look something like this: {{"cnt", "config_is_null",
// "client_id", "sink_id"}, {"<num>", "<boolean>" "<clientId>", "<sinkId>"}}.
EXPECT_EQ(result.value().size(), 2u);
EXPECT_EQ(result.value()[1].size(), 4u);
if (input::InputUtils::IsTransferInputToVizSupported()) {
EXPECT_THAT(
result.value(),
testing::ElementsAre(
testing::ElementsAre("cnt", "config_is_null", "client_id",
"sink_id"),
testing::ElementsAre(
"1", "1", base::NumberToString(kFrameSinkIdB.client_id()),
base::NumberToString(kFrameSinkIdB.sink_id()))));
} else {
EXPECT_EQ(result.value()[1][0], "0");
}
}
TEST_P(AndroidFrameSinkManagerTest, RWHIERLifecycleDiffWebContents) {
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
base::UnguessableToken grouping_id_1 = base::UnguessableToken::Create();
base::UnguessableToken grouping_id_2 = base::UnguessableToken::Create();
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id_1));
EXPECT_EQ(InputManagerExists(), expected_creation);
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
// Create another CompositorFrameSinkImpl for a different WebContent.
CreateCompositorFrameSink(kFrameSinkIdB, CreateRIRConfig(grouping_id_2));
EXPECT_EQ(InputManagerExists(), expected_creation);
auto* mock_input_manager = GetMockInputManager();
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 2);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 2);
}
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 1);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 1);
}
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdB));
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 0);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 0);
}
}
TEST_P(AndroidFrameSinkManagerTest, RWHIERLifecycleSameWebContents) {
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
EXPECT_EQ(InputManagerExists(), expected_creation);
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
// Create another CompositorFrameSinkImpl for the same WebContent.
CreateCompositorFrameSink(kFrameSinkIdB, CreateRIRConfig(grouping_id));
EXPECT_EQ(InputManagerExists(), expected_creation);
auto* mock_input_manager = GetMockInputManager();
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 2);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 1);
}
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 1);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 1);
}
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdB));
if (expected_creation) {
EXPECT_EQ(mock_input_manager->GetRenderInputRouterMapSize(), 0);
EXPECT_EQ(mock_input_manager->GetInputEventRouterMapSize(), 0);
}
}
TEST_P(AndroidFrameSinkManagerTest, VizRIRDelegateLifecycle) {
base::test::TestTraceProcessor ttp;
ttp.StartTrace("viz, input");
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
EXPECT_EQ(InputManagerExists(), expected_creation);
EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
absl::Status status = ttp.StopAndParseTrace();
EXPECT_TRUE(status.ok()) << status.message();
std::string query = R"(
SELECT name
FROM slice
WHERE
(
name = 'RenderInputRouter::RenderInputRouter'
OR
name = 'RenderInputRouter::~RenderInputRouter'
OR
name = 'RenderInputRouterDelegateImpl::RenderInputRouterDelegateImpl'
OR
name = 'RenderInputRouterDelegateImpl::~RenderInputRouterDelegateImpl'
)
ORDER BY ts ASC
)";
auto result = ttp.RunQuery(query);
EXPECT_TRUE(result.has_value());
// `result.value()` would look something like this: {{"name"},
// {"<name1>"}, {"<name2>"}, {"<name3>"}, {"<name4>"}}.
if (input::InputUtils::IsTransferInputToVizSupported()) {
EXPECT_EQ(result.value().size(), 5u);
EXPECT_EQ(result.value()[1].size(), 1u);
EXPECT_THAT(
result.value(),
testing::ElementsAre(
testing::ElementsAre("name"),
testing::ElementsAre("RenderInputRouterDelegateImpl::"
"RenderInputRouterDelegateImpl"),
testing::ElementsAre("RenderInputRouter::RenderInputRouter"),
testing::ElementsAre("RenderInputRouter::~RenderInputRouter"),
testing::ElementsAre("RenderInputRouterDelegateImpl::~"
"RenderInputRouterDelegateImpl")));
} else {
EXPECT_EQ(result.value()[0][0], "name");
}
}
TEST_P(AndroidFrameSinkManagerTest, VizRenderInputRouterSupportBaseLifecycle) {
base::test::TestTraceProcessor ttp;
ttp.StartTrace("viz, input");
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
// Create a grouping id.
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
// Create a CompositorFrameSinkImpl.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
EXPECT_EQ(InputManagerExists(), ExpectedInputManagerCreation());
// Invalidating should destroy the CompositorFrameSinkImpl.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
EXPECT_FALSE(CompositorFrameSinkExists(kFrameSinkIdA));
absl::Status status = ttp.StopAndParseTrace();
ASSERT_TRUE(status.ok()) << status.message();
std::string query = R"(
SELECT name
FROM slice
WHERE
(
name = 'RenderInputRouter::RenderInputRouter'
OR
name = 'RenderInputRouter::~RenderInputRouter'
OR
name = 'RenderInputRouterSupportBase::RenderInputRouterSupportBase'
OR
name = 'RenderInputRouterSupportBase::~RenderInputRouterSupportBase'
)
ORDER BY ts ASC
)";
auto result = ttp.RunQuery(query);
ASSERT_TRUE(result.has_value());
// `result.value()` would look something like this: {{"name"},
// {"<name1>"}, {"<name2>"}, {"<name3>"}, {"<name4>"}}.
if (input::InputUtils::IsTransferInputToVizSupported()) {
EXPECT_THAT(
result.value(),
testing::ElementsAre(
testing::ElementsAre("name"),
testing::ElementsAre("RenderInputRouter::RenderInputRouter"),
testing::ElementsAre(
"RenderInputRouterSupportBase::RenderInputRouterSupportBase"),
testing::ElementsAre(
"RenderInputRouterSupportBase::~RenderInputRouterSupportBase"),
testing::ElementsAre("RenderInputRouter::~RenderInputRouter")));
} else {
EXPECT_EQ(result.value()[0][0], "name");
}
}
TEST_P(AndroidFrameSinkManagerTest, RenderInputRouterSupportTraversals) {
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
if (!expected_creation) {
return;
}
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
RootCompositorFrameSinkData root_data1;
manager_->CreateRootCompositorFrameSink(
root_data1.BuildParams(kFrameSinkIdRoot));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot));
RootCompositorFrameSinkData root_data2;
manager_->CreateRootCompositorFrameSink(
root_data2.BuildParams(kFrameSinkIdRoot2));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot2));
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdC, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdD, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdE, true /* report_activation */);
// Create CompositorFrameSinkImpl's.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdB, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdC, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdD, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdE, CreateRIRConfig(grouping_id));
// Set up initial hierarchy.
// root1 -> A -> B -> C
// + -> D
// root2 -> E
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdE);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
EXPECT_EQ(
GetMockInputManager()->GetParentRenderInputRouterSupport(kFrameSinkIdB),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
EXPECT_EQ(
GetMockInputManager()->GetParentRenderInputRouterSupport(kFrameSinkIdC),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdB));
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdD);
EXPECT_EQ(
GetMockInputManager()->GetParentRenderInputRouterSupport(kFrameSinkIdD),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdB));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdB),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdC),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdD),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
// Attach A into root2's subtree, like a window moving across displays.
// root1 -> A -> B -> C
// + -> D
// root2 -> E -> A -> B -> C
// + -> D
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdE, kFrameSinkIdA);
// With the heuristic of just keeping existing parent in the face of multiple,
// no client's corresponding RootCompositorFrameSink should change.
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdB),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdC),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdD),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdA));
// Detach A from root1.
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
// root1
// root2 -> E -> A -> B -> C
// + -> D
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdB),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdE));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdC),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdE));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdD),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdE));
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdE);
// root1
// root2
// E -> A -> B -> C
// + -> D
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdD),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdE));
EXPECT_EQ(
GetMockInputManager()->GetRootRenderInputRouterSupport(kFrameSinkIdA),
GetMockInputManager()->GetSupportForFrameSink(kFrameSinkIdE));
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdD);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdE, kFrameSinkIdA);
// Delete RootCompositorFrameSinks.
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot2, {});
// Invalidating should destroy the CompositorFrameSinkImpl's.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdC, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdD, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdE, {});
}
TEST_P(AndroidFrameSinkManagerTest, EmbeddedRenderInputRouters) {
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
if (!expected_creation) {
return;
}
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
RootCompositorFrameSinkData root_data1;
manager_->CreateRootCompositorFrameSink(
root_data1.BuildParams(kFrameSinkIdRoot));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot));
RootCompositorFrameSinkData root_data2;
manager_->CreateRootCompositorFrameSink(
root_data2.BuildParams(kFrameSinkIdRoot2));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot2));
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdC, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdD, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdE, true /* report_activation */);
// Create CompositorFrameSinkImpl's.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdB, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdC, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdD, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdE, CreateRIRConfig(grouping_id));
// Set up initial hierarchy.
// root1 -> A -> B -> C
// + -> D
// root2 -> E
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdE);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdD);
EXPECT_THAT(
GetEmbeddedRenderInputRouters(kFrameSinkIdA),
testing::ContainerEq(base::flat_set<FrameSinkId>({kFrameSinkIdB})));
EXPECT_THAT(GetEmbeddedRenderInputRouters(kFrameSinkIdB),
testing::ContainerEq(
base::flat_set<FrameSinkId>({kFrameSinkIdC, kFrameSinkIdD})));
EXPECT_THAT(GetEmbeddedRenderInputRouters(kFrameSinkIdC),
testing::ContainerEq(base::flat_set<FrameSinkId>({})));
// Attach A into root2's subtree, like a window moving across displays.
// root1 -> A -> B -> C
// + -> D
// root2 -> E -> A -> B -> C
// + -> D
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdE, kFrameSinkIdA);
// Detach A from root1.
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
// root1
// root2 -> E -> A -> B -> C
// + -> D
EXPECT_THAT(
GetEmbeddedRenderInputRouters(kFrameSinkIdE),
testing::ContainerEq(base::flat_set<FrameSinkId>({kFrameSinkIdA})));
EXPECT_THAT(
GetEmbeddedRenderInputRouters(kFrameSinkIdA),
testing::ContainerEq(base::flat_set<FrameSinkId>({kFrameSinkIdB})));
EXPECT_THAT(GetEmbeddedRenderInputRouters(kFrameSinkIdB),
testing::ContainerEq(
base::flat_set<FrameSinkId>({kFrameSinkIdC, kFrameSinkIdD})));
EXPECT_THAT(
GetEmbeddedRenderInputRouters(kFrameSinkIdRoot2),
testing::ContainerEq(base::flat_set<FrameSinkId>({kFrameSinkIdE})));
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdE);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdD);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdE, kFrameSinkIdA);
// Delete RootCompositorFrameSinks.
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot2, {});
// Invalidating should destroy the CompositorFrameSinkImpl's.
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdC, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdD, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdE, {});
}
TEST_P(AndroidFrameSinkManagerTest, ReconstructsRenderInputRouterSupports) {
const bool expected_creation =
input::InputUtils::IsTransferInputToVizSupported();
if (!expected_creation) {
return;
}
base::UnguessableToken grouping_id = base::UnguessableToken::Create();
RootCompositorFrameSinkData root_data1;
manager_->CreateRootCompositorFrameSink(
root_data1.BuildParams(kFrameSinkIdRoot));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot));
RootCompositorFrameSinkData root_data2;
manager_->CreateRootCompositorFrameSink(
root_data2.BuildParams(kFrameSinkIdRoot2));
EXPECT_TRUE(CompositorFrameSinkExists(kFrameSinkIdRoot2));
manager_->RegisterFrameSinkId(kFrameSinkIdA, true /* report_activation */);
manager_->RegisterFrameSinkId(kFrameSinkIdB, true /* report_activation */);
// Create CompositorFrameSinkImpl's.
CreateCompositorFrameSink(kFrameSinkIdA, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdB, CreateRIRConfig(grouping_id));
CreateCompositorFrameSink(kFrameSinkIdC, CreateRIRConfig(grouping_id));
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdB));
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdC));
// root1 -> A
// -> C
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdC);
// root2 -> B
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdB);
// RenderInputRouterSupport is reconstructed as non-child support.
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdB));
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdC));
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdA);
// `A` has multiple parents, nothing should have happened until the number of
// parents go back to 1.
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
// root1 -> C
// root2 -> B -> A
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
// `A` becomes a child support.
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
// root1
// root2 -> B -> A
// -> C
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdC);
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdC));
// C becomes a child support.
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdC));
// root1 -> A
// root2 -> B -> C
// Move A back under root 1
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
// `A` stays as a child support since it has multiple parents.
EXPECT_TRUE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdA);
EXPECT_FALSE(IsRenderInputRouterSupportChildFrame(kFrameSinkIdA));
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot, kFrameSinkIdA);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdRoot2, kFrameSinkIdB);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdRoot2, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdA, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdB, {});
manager_->InvalidateFrameSinkId(kFrameSinkIdC, {});
}
INSTANTIATE_TEST_SUITE_P(All,
AndroidFrameSinkManagerTest,
::testing::Bool(),
[](auto& info) {
return info.param ? "InputOnViz_Enabled"
: "InputOnViz_Disabled";
});
#endif // BUILDFLAG(IS_ANDROID)
namespace {
enum RegisterOrder { REGISTER_HIERARCHY_FIRST, REGISTER_CLIENTS_FIRST };
enum UnregisterOrder { UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST };
enum BFSOrder { BFS_FIRST, BFS_SECOND, BFS_THIRD };
static const RegisterOrder kRegisterOrderList[] = {REGISTER_HIERARCHY_FIRST,
REGISTER_CLIENTS_FIRST};
static const UnregisterOrder kUnregisterOrderList[] = {
UNREGISTER_HIERARCHY_FIRST, UNREGISTER_CLIENTS_FIRST};
static const BFSOrder kBFSOrderList[] = {BFS_FIRST, BFS_SECOND, BFS_THIRD};
} // namespace
// In practice, registering and unregistering both parent/child relationships
// and CompositorFrameSinkSupports can happen in any ordering with respect to
// each other. These following tests verify that all the data structures
// are properly set up and cleaned up under the four permutations of orderings
// of this nesting.
class FrameSinkManagerOrderingTest : public FrameSinkManagerTest {
public:
FrameSinkManagerOrderingTest()
: hierarchy_registered_(false),
clients_registered_(false),
bfs_registered_(false) {
AssertCorrectBFSState();
}
~FrameSinkManagerOrderingTest() override {
EXPECT_FALSE(hierarchy_registered_);
EXPECT_FALSE(clients_registered_);
EXPECT_FALSE(bfs_registered_);
AssertCorrectBFSState();
}
void RegisterHierarchy() {
DCHECK(!hierarchy_registered_);
hierarchy_registered_ = true;
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->RegisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
AssertCorrectBFSState();
}
void UnregisterHierarchy() {
DCHECK(hierarchy_registered_);
hierarchy_registered_ = false;
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdA, kFrameSinkIdB);
manager_->UnregisterFrameSinkHierarchy(kFrameSinkIdB, kFrameSinkIdC);
AssertCorrectBFSState();
}
void RegisterClients() {
DCHECK(!clients_registered_);
clients_registered_ = true;
client_a_ = CreateCompositorFrameSinkSupport(kFrameSinkIdA);
client_b_ = CreateCompositorFrameSinkSupport(kFrameSinkIdB);
client_c_ = CreateCompositorFrameSinkSupport(kFrameSinkIdC);
AssertCorrectBFSState();
}
void UnregisterClients() {
DCHECK(clients_registered_);
clients_registered_ = false;
client_a_.reset();
client_b_.reset();
client_c_.reset();
AssertCorrectBFSState();
}
void RegisterBFS() {
DCHECK(!bfs_registered_);
bfs_registered_ = true;
manager_->RegisterBeginFrameSource(&source_, kFrameSinkIdA);
AssertCorrectBFSState();
}
void UnregisterBFS() {
DCHECK(bfs_registered_);
bfs_registered_ = false;
manager_->UnregisterBeginFrameSource(&source_);
AssertCorrectBFSState();
}
void AssertEmptyBFS() {
EXPECT_EQ(nullptr, GetBeginFrameSource(client_a_));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_b_));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_c_));
}
void AssertAllValidBFS() {
EXPECT_EQ(&source_, GetBeginFrameSource(client_a_));
EXPECT_EQ(&source_, GetBeginFrameSource(client_b_));
EXPECT_EQ(&source_, GetBeginFrameSource(client_c_));
}
protected:
void AssertCorrectBFSState() {
if (!clients_registered_)
return;
if (!bfs_registered_) {
AssertEmptyBFS();
return;
}
if (!hierarchy_registered_) {
// A valid but not attached to anything.
EXPECT_EQ(&source_, GetBeginFrameSource(client_a_));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_b_));
EXPECT_EQ(nullptr, GetBeginFrameSource(client_c_));
return;
}
AssertAllValidBFS();
}
StubBeginFrameSource source_;
// A -> B -> C hierarchy, with A always having the BFS.
std::unique_ptr<CompositorFrameSinkSupport> client_a_;
std::unique_ptr<CompositorFrameSinkSupport> client_b_;
std::unique_ptr<CompositorFrameSinkSupport> client_c_;
bool hierarchy_registered_;
bool clients_registered_;
bool bfs_registered_;
};
class FrameSinkManagerOrderingParamTest
: public FrameSinkManagerOrderingTest,
public ::testing::WithParamInterface<
std::tuple<RegisterOrder, UnregisterOrder, BFSOrder>> {};
TEST_P(FrameSinkManagerOrderingParamTest, Ordering) {
// Test the four permutations of client/hierarchy setting/unsetting and test
// each place the BFS can be added and removed. The BFS and the
// client/hierarchy are less related, so BFS is tested independently instead
// of every permutation of BFS setting and unsetting.
// The register/unregister functions themselves test most of the state.
RegisterOrder register_order = std::get<0>(GetParam());
UnregisterOrder unregister_order = std::get<1>(GetParam());
BFSOrder bfs_order = std::get<2>(GetParam());
// Attach everything up in the specified order.
if (bfs_order == BFS_FIRST)
RegisterBFS();
if (register_order == REGISTER_HIERARCHY_FIRST)
RegisterHierarchy();
else
RegisterClients();
if (bfs_order == BFS_SECOND)
RegisterBFS();
if (register_order == REGISTER_HIERARCHY_FIRST)
RegisterClients();
else
RegisterHierarchy();
if (bfs_order == BFS_THIRD)
RegisterBFS();
// Everything hooked up, so should be valid.
AssertAllValidBFS();
// Detach everything in the specified order.
if (bfs_order == BFS_THIRD)
UnregisterBFS();
if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
UnregisterHierarchy();
else
UnregisterClients();
if (bfs_order == BFS_SECOND)
UnregisterBFS();
if (unregister_order == UNREGISTER_HIERARCHY_FIRST)
UnregisterClients();
else
UnregisterHierarchy();
if (bfs_order == BFS_FIRST)
UnregisterBFS();
}
INSTANTIATE_TEST_SUITE_P(
FrameSinkManagerOrderingParamTestInstantiation,
FrameSinkManagerOrderingParamTest,
::testing::Combine(::testing::ValuesIn(kRegisterOrderList),
::testing::ValuesIn(kUnregisterOrderList),
::testing::ValuesIn(kBFSOrderList)));
} // namespace viz