| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/containers/flat_set.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/surfaces/surface_id.h" |
| #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" |
| #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" |
| #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" |
| #include "components/viz/service/surfaces/frame_index_constants.h" |
| #include "components/viz/service/surfaces/surface.h" |
| #include "components/viz/service/surfaces/surface_allocation_group.h" |
| #include "components/viz/test/begin_frame_args_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/surface_id_allocator_set.h" |
| #include "components/viz/test/test_surface_id_allocator.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::_; |
| using testing::Eq; |
| using testing::IsEmpty; |
| using testing::UnorderedElementsAre; |
| |
| namespace viz { |
| namespace { |
| |
| constexpr bool kIsRoot = true; |
| constexpr bool kIsChildRoot = false; |
| constexpr FrameSinkId kDisplayFrameSink(2, 0); |
| constexpr FrameSinkId kParentFrameSink(3, 0); |
| constexpr FrameSinkId kChildFrameSink1(65563, 0); |
| constexpr FrameSinkId kChildFrameSink2(65564, 0); |
| constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); |
| |
| const uint64_t kBeginFrameSourceId = 1337; |
| |
| std::vector<SurfaceId> empty_surface_ids() { |
| return std::vector<SurfaceId>(); |
| } |
| std::vector<SurfaceRange> empty_surface_ranges() { |
| return std::vector<SurfaceRange>(); |
| } |
| |
| CompositorFrame MakeCompositorFrame( |
| std::vector<SurfaceId> activation_dependencies, |
| std::vector<SurfaceRange> referenced_surfaces, |
| std::vector<TransferableResource> resource_list, |
| const FrameDeadline& deadline = FrameDeadline()) { |
| return CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .SetActivationDependencies(std::move(activation_dependencies)) |
| .SetBeginFrameSourceId(kBeginFrameSourceId) |
| .SetReferencedSurfaces(std::move(referenced_surfaces)) |
| .SetTransferableResources(std::move(resource_list)) |
| .SetDeadline(deadline) |
| .Build(); |
| } |
| |
| } // namespace |
| |
| class SurfaceSynchronizationTest : public testing::Test { |
| public: |
| SurfaceSynchronizationTest() |
| : frame_sink_manager_( |
| FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)), |
| surface_observer_(false) {} |
| |
| SurfaceSynchronizationTest(const SurfaceSynchronizationTest&) = delete; |
| SurfaceSynchronizationTest& operator=(const SurfaceSynchronizationTest&) = |
| delete; |
| |
| ~SurfaceSynchronizationTest() override {} |
| |
| CompositorFrameSinkSupport& display_support() { |
| return *supports_[kDisplayFrameSink]; |
| } |
| Surface* display_surface() { |
| return display_support().GetLastCreatedSurfaceForTesting(); |
| } |
| |
| CompositorFrameSinkSupport& parent_support() { |
| return *supports_[kParentFrameSink]; |
| } |
| Surface* parent_surface() { |
| return parent_support().GetLastCreatedSurfaceForTesting(); |
| } |
| |
| CompositorFrameSinkSupport& child_support1() { |
| return *supports_[kChildFrameSink1]; |
| } |
| Surface* child_surface1() { |
| return child_support1().GetLastCreatedSurfaceForTesting(); |
| } |
| |
| CompositorFrameSinkSupport& child_support2() { |
| return *supports_[kChildFrameSink2]; |
| } |
| Surface* child_surface2() { |
| return child_support2().GetLastCreatedSurfaceForTesting(); |
| } |
| |
| void CreateFrameSink(const FrameSinkId& frame_sink_id, bool is_root) { |
| supports_[frame_sink_id] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, frame_sink_id, is_root); |
| } |
| |
| void DestroyFrameSink(const FrameSinkId& frame_sink_id) { |
| auto it = supports_.find(frame_sink_id); |
| if (it == supports_.end()) |
| return; |
| supports_.erase(it); |
| } |
| |
| void ExpireAllTemporaryReferencesAndGarbageCollect() { |
| frame_sink_manager_.surface_manager()->ExpireOldTemporaryReferences(); |
| frame_sink_manager_.surface_manager()->ExpireOldTemporaryReferences(); |
| frame_sink_manager_.surface_manager()->GarbageCollectSurfaces(); |
| } |
| |
| // Returns all the references where |surface_id| is the parent. |
| const base::flat_set<SurfaceId>& GetReferencesFrom( |
| const SurfaceId& surface_id) { |
| return frame_sink_manager() |
| .surface_manager() |
| ->GetSurfacesReferencedByParent(surface_id); |
| } |
| |
| FrameSinkManagerImpl& frame_sink_manager() { return frame_sink_manager_; } |
| SurfaceManager* surface_manager() { |
| return frame_sink_manager_.surface_manager(); |
| } |
| |
| // Returns all the references where |surface_id| is the parent. |
| const base::flat_set<SurfaceId>& GetChildReferences( |
| const SurfaceId& surface_id) { |
| return frame_sink_manager() |
| .surface_manager() |
| ->GetSurfacesReferencedByParent(surface_id); |
| } |
| |
| // Returns true if there is a temporary reference for |surface_id|. |
| bool HasTemporaryReference(const SurfaceId& surface_id) { |
| return frame_sink_manager().surface_manager()->HasTemporaryReference( |
| surface_id); |
| } |
| |
| Surface* GetLatestInFlightSurface(const SurfaceRange& surface_range) { |
| return frame_sink_manager().surface_manager()->GetLatestInFlightSurface( |
| surface_range); |
| } |
| |
| FakeExternalBeginFrameSource* begin_frame_source() { |
| return begin_frame_source_.get(); |
| } |
| |
| base::TimeTicks Now() { return now_src_->NowTicks(); } |
| |
| FrameDeadline MakeDefaultDeadline() { |
| return FrameDeadline(Now(), 4u, BeginFrameArgs::DefaultInterval(), false); |
| } |
| |
| FrameDeadline MakeDeadline(uint32_t deadline_in_frames) { |
| return FrameDeadline(Now(), deadline_in_frames, |
| BeginFrameArgs::DefaultInterval(), false); |
| } |
| |
| void SendNextBeginFrame() { |
| // Creep the time forward so that any BeginFrameArgs is not equal to the |
| // last one otherwise we violate the BeginFrameSource contract. |
| now_src_->Advance(BeginFrameArgs::DefaultInterval()); |
| BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs( |
| BEGINFRAME_FROM_HERE, now_src_.get()); |
| begin_frame_source_->TestOnBeginFrame(args); |
| } |
| |
| void SendLateBeginFrame() { |
| // Creep the time forward so that any BeginFrameArgs is not equal to the |
| // last one otherwise we violate the BeginFrameSource contract. |
| now_src_->Advance(4u * BeginFrameArgs::DefaultInterval()); |
| BeginFrameArgs args = begin_frame_source_->CreateBeginFrameArgs( |
| BEGINFRAME_FROM_HERE, now_src_.get()); |
| begin_frame_source_->TestOnBeginFrame(args); |
| } |
| |
| FakeSurfaceObserver& surface_observer() { return surface_observer_; } |
| |
| // testing::Test: |
| void SetUp() override { |
| testing::Test::SetUp(); |
| |
| begin_frame_source_ = |
| std::make_unique<FakeExternalBeginFrameSource>(0.f, false); |
| now_src_ = std::make_unique<base::SimpleTestTickClock>(); |
| frame_sink_manager_.surface_manager()->SetTickClockForTesting( |
| now_src_.get()); |
| frame_sink_manager_.surface_manager()->AddObserver(&surface_observer_); |
| supports_[kDisplayFrameSink] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kDisplayFrameSink, kIsRoot); |
| |
| supports_[kParentFrameSink] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kParentFrameSink, kIsChildRoot); |
| |
| supports_[kChildFrameSink1] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kChildFrameSink1, kIsChildRoot); |
| |
| supports_[kChildFrameSink2] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kChildFrameSink2, kIsChildRoot); |
| |
| // Normally, the BeginFrameSource would be registered by the Display. We |
| // register it here so that BeginFrames are received by the display support, |
| // for use in the PassesOnBeginFrameAcks test. Other supports do not receive |
| // BeginFrames, since the frame sink hierarchy is not set up in this test. |
| frame_sink_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), |
| kDisplayFrameSink); |
| frame_sink_manager_.RegisterFrameSinkHierarchy(kDisplayFrameSink, |
| kParentFrameSink); |
| frame_sink_manager_.RegisterFrameSinkHierarchy(kDisplayFrameSink, |
| kChildFrameSink1); |
| frame_sink_manager_.RegisterFrameSinkHierarchy(kDisplayFrameSink, |
| kChildFrameSink2); |
| } |
| |
| void TearDown() override { |
| frame_sink_manager_.surface_manager()->RemoveObserver(&surface_observer_); |
| frame_sink_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); |
| |
| begin_frame_source_->SetClient(nullptr); |
| begin_frame_source_.reset(); |
| |
| supports_.clear(); |
| |
| surface_observer_.Reset(); |
| } |
| |
| bool IsMarkedForDestruction(const SurfaceId& surface_id) { |
| return frame_sink_manager_.surface_manager()->IsMarkedForDestruction( |
| surface_id); |
| } |
| |
| Surface* GetSurfaceForId(const SurfaceId& surface_id) { |
| return frame_sink_manager_.surface_manager()->GetSurfaceForId(surface_id); |
| } |
| |
| SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, |
| uint32_t parent_sequence_number, |
| uint32_t child_sequence_number = 1u) { |
| return allocator_set_.MakeSurfaceId(frame_sink_id, parent_sequence_number, |
| child_sequence_number); |
| } |
| |
| bool allocation_groups_need_garbage_collection() { |
| return surface_manager()->allocation_groups_need_garbage_collection_; |
| } |
| |
| protected: |
| testing::NiceMock<MockCompositorFrameSinkClient> support_client_; |
| |
| private: |
| std::unique_ptr<base::SimpleTestTickClock> now_src_; |
| ServerSharedBitmapManager shared_bitmap_manager_; |
| FrameSinkManagerImpl frame_sink_manager_; |
| FakeSurfaceObserver surface_observer_; |
| std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; |
| std::unordered_map<FrameSinkId, |
| std::unique_ptr<CompositorFrameSinkSupport>, |
| FrameSinkIdHash> |
| supports_; |
| SurfaceIdAllocatorSet allocator_set_; |
| }; |
| |
| // The display root surface should have a surface reference from the top-level |
| // root added/removed when a CompositorFrame is submitted with a new |
| // SurfaceId. |
| TEST_F(SurfaceSynchronizationTest, RootSurfaceReceivesReferences) { |
| const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); |
| |
| // Submit a CompositorFrame for the first display root surface. |
| display_support().SubmitCompositorFrame( |
| display_id_first.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // A surface reference from the top-level root is added and there shouldn't be |
| // a temporary reference. |
| EXPECT_FALSE(HasTemporaryReference(display_id_first)); |
| EXPECT_THAT(GetChildReferences( |
| frame_sink_manager().surface_manager()->GetRootSurfaceId()), |
| UnorderedElementsAre(display_id_first)); |
| |
| // Submit a CompositorFrame for the second display root surface. |
| display_support().SubmitCompositorFrame( |
| display_id_second.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // A surface reference from the top-level root to |display_id_second| should |
| // be added and the reference to |display_root_first| removed. |
| EXPECT_FALSE(HasTemporaryReference(display_id_second)); |
| EXPECT_THAT(GetChildReferences( |
| frame_sink_manager().surface_manager()->GetRootSurfaceId()), |
| UnorderedElementsAre(display_id_second)); |
| |
| frame_sink_manager().surface_manager()->GarbageCollectSurfaces(); |
| |
| // Surface |display_id_first| is unreachable and should get deleted. |
| EXPECT_EQ(nullptr, GetSurfaceForId(display_id_first)); |
| } |
| |
| // The parent Surface is blocked on |child_id1| and |child_id2|. |
| TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // parent_support is blocked on |child_id1| and |child_id2|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1, child_id2)); |
| |
| // Submit a CompositorFrame without any dependencies to |child_id1|. |
| // parent_support should now only be blocked on |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // Submit a CompositorFrame without any dependencies to |child_id2|. |
| // parent_support should be activated. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_FALSE(child_surface2()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // The parent Surface is blocked on |child_id2| which is blocked on |
| // |child_id3|. |
| TEST_F(SurfaceSynchronizationTest, BlockedChain) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // parent_support is blocked on |child_id1|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| // The parent should not report damage until it activates. |
| EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // child_support1 should now be blocked on |child_id2|. |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| // The parent and child should not report damage until they activate. |
| EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id)); |
| EXPECT_FALSE(surface_observer().IsSurfaceDamaged(child_id1)); |
| |
| // The parent should still be blocked on |child_id1| because it's pending. |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| // Submit a CompositorFrame without any dependencies to |child_id2|. |
| // parent_support should be activated. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| EXPECT_FALSE(child_surface2()->has_deadline()); |
| |
| // child_surface1 should now be active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // parent_surface should now be active. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // All three surfaces |parent_id|, |child_id1|, and |child_id2| should |
| // now report damage. This would trigger a new display frame. |
| EXPECT_TRUE(surface_observer().IsSurfaceDamaged(parent_id)); |
| EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id1)); |
| EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id2)); |
| } |
| |
| // parent_surface and child_surface1 are blocked on |child_id2|. |
| TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(absl::nullopt, child_id2)}, |
| std::vector<TransferableResource>())); |
| |
| // parent_support is blocked on |child_id2|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // child_support1 should now be blocked on |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(absl::nullopt, child_id2)}, |
| std::vector<TransferableResource>())); |
| |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // The parent should still be blocked on |child_id2|. |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // Submit a CompositorFrame without any dependencies to |child_id2|. |
| // parent_support should be activated. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_FALSE(child_surface2()->has_deadline()); |
| |
| // child_surface1 should now be active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // parent_surface should now be active. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // parent_surface is blocked on |child_id1|, and child_surface2 is blocked on |
| // |child_id2| until the deadline hits. |
| TEST_F(SurfaceSynchronizationTest, DeadlineHits) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // parent_support is blocked on |child_id1|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // child_support1 should now be blocked on |child_id2|. |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // The parent should still be blocked on |child_id1| because it's pending. |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| // There is still a looming deadline! Eeek! |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| |
| // parent_support is still blocked on |child_id1|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| // child_support1 is still blocked on |child_id2|. |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| } |
| |
| SendNextBeginFrame(); |
| |
| // The deadline has passed. |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| // parent_surface has been activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // child_surface1 has been activated. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies that unlimited deadline mode works and that surfaces will |
| // not activate until dependencies are resolved. |
| TEST_F(SurfaceSynchronizationTest, UnlimitedDeadline) { |
| // Turn on unlimited deadline mode. |
| frame_sink_manager() |
| .surface_manager() |
| ->SetActivationDeadlineInFramesForTesting(absl::nullopt); |
| |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // The deadline specified by the parent is ignored in unlimited deadline |
| // mode. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // parent_support is blocked on |child_id1|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| for (int i = 0; i < 4; ++i) { |
| SendNextBeginFrame(); |
| // parent_support is still blocked on |child_id1|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| } |
| |
| // parent_support is STILL blocked on |child_id1|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // parent_surface has been activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // parent_surface is blocked on |child_id1| until a late BeginFrame arrives and |
| // triggers a deadline. |
| TEST_F(SurfaceSynchronizationTest, LateBeginFrameTriggersDeadline) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // parent_support is blocked on |child_id1|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| SendLateBeginFrame(); |
| |
| // The deadline has passed. |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| // parent_surface has been activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies at the Surface activates once a CompositorFrame is |
| // submitted that has no unresolved dependencies. |
| TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| // Submit a CompositorFrame that depends on |arbitrary_id|. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Verify that the CompositorFrame is blocked on |arbitrary_id|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(arbitrary_id)); |
| |
| // Submit a CompositorFrame that has no dependencies. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the CompositorFrame has been activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // Supports testing features::OnBeginFrameAcks, which changes the expectations |
| // of what IPCs are sent to the CompositorFrameSinkClient. When enabled |
| // OnBeginFrame also handles ReturnResources as well as |
| // DidReceiveCompositorFrameAck. |
| class OnBeginFrameAcksSurfaceSynchronizationTest |
| : public SurfaceSynchronizationTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| OnBeginFrameAcksSurfaceSynchronizationTest(); |
| ~OnBeginFrameAcksSurfaceSynchronizationTest() override = default; |
| |
| bool BeginFrameAcksEnabled() const { return GetParam(); } |
| |
| // SurfaceSynchronizationTest: |
| void SetUp() override; |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| OnBeginFrameAcksSurfaceSynchronizationTest:: |
| OnBeginFrameAcksSurfaceSynchronizationTest() { |
| if (BeginFrameAcksEnabled()) { |
| scoped_feature_list_.InitAndEnableFeature(features::kOnBeginFrameAcks); |
| } else { |
| scoped_feature_list_.InitAndDisableFeature(features::kOnBeginFrameAcks); |
| } |
| } |
| |
| void OnBeginFrameAcksSurfaceSynchronizationTest::SetUp() { |
| SurfaceSynchronizationTest::SetUp(); |
| if (BeginFrameAcksEnabled()) { |
| parent_support().SetWantsBeginFrameAcks(); |
| child_support1().SetWantsBeginFrameAcks(); |
| child_support2().SetWantsBeginFrameAcks(); |
| } |
| } |
| |
| // This test verifies that a pending CompositorFrame does not affect surface |
| // references. A new surface from a child will continue to exist as a temporary |
| // reference until the parent's frame activates. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, |
| OnlyActiveFramesAffectSurfaceReferences) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // child_support1 submits a CompositorFrame without any dependencies. |
| // DidReceiveCompositorFrameAck should call on immediate activation. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 1); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Verify that the child surface is not blocked. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that there's a temporary reference for |child_id1|. |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| |
| // parent_support submits a CompositorFrame that depends on |child_id1| |
| // (which is already active) and |child_id2|. Thus, the parent should not |
| // activate immediately. DidReceiveCompositorFrameAck should not be called |
| // immediately because the parent CompositorFrame is also blocked on |
| // |child_id2|. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Verify that there's a temporary reference for |child_id1| that still |
| // exists. |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| |
| // child_support2 submits a CompositorFrame without any dependencies. |
| // Both the child and the parent should immediately ACK CompositorFrames |
| // on activation. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 2); |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Verify that the child surface is not blocked. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that the parent surface's CompositorFrame has activated and that |
| // the temporary reference has been replaced by a permanent one. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| EXPECT_FALSE(HasTemporaryReference(child_id1)); |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
| } |
| |
| // This test verifies that we do not double count returned resources when a |
| // CompositorFrame starts out as pending, then becomes active, and then is |
| // replaced with another active CompositorFrame. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // The parent submits a CompositorFrame that depends on |child_id| before |
| // the child submits a CompositorFrame. The CompositorFrame also has |
| // resources in its resource list. |
| TransferableResource resource; |
| resource.id = ResourceId(1337); |
| resource.format = SharedImageFormat::SinglePlane(ALPHA_8); |
| resource.size = gfx::Size(1234, 5678); |
| std::vector<TransferableResource> resource_list = {resource}; |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), resource_list)); |
| |
| // Verify that the CompositorFrame is blocked on |child_id|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Verify that the child CompositorFrame activates immediately. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that the parent has activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| if (BeginFrameAcksEnabled()) { |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| } else { |
| std::vector<ReturnedResource> returned_resources; |
| ResourceId id = resource.ToReturnedResource().id; |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .WillOnce([=](std::vector<ReturnedResource> got) { |
| EXPECT_EQ(1u, got.size()); |
| EXPECT_EQ(id, got[0].id); |
| }); |
| } |
| |
| // The parent submits a CompositorFrame without any dependencies. That |
| // frame should activate immediately, replacing the earlier frame. The |
| // resource from the earlier frame should be returned to the client. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ranges()}, |
| std::vector<TransferableResource>())); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| if (BeginFrameAcksEnabled()) { |
| ResourceId id = resource.ToReturnedResource().id; |
| EXPECT_CALL(support_client_, OnBeginFrame(_, _, _, _)) |
| .WillOnce([=](const BeginFrameArgs& args, |
| const FrameTimingDetailsMap& timing_details, |
| bool frame_ack, std::vector<ReturnedResource> got) { |
| EXPECT_EQ(1u, got.size()); |
| EXPECT_EQ(id, got[0].id); |
| }); |
| SendNextBeginFrame(); |
| } |
| } |
| |
| // This test verifies that if a surface has both a pending and active |
| // CompositorFrame and the pending CompositorFrame activates, replacing |
| // the existing active CompositorFrame, then the surface reference hierarchy |
| // will be updated allowing garbage collection of surfaces that are no longer |
| // referenced. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, |
| DropStaleReferencesAfterActivation) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // The parent submits a CompositorFrame that depends on |child_id1| before |
| // the child submits a CompositorFrame. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Verify that the CompositorFrame is blocked on |child_id|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Verify that no references are added while the CompositorFrame is |
| // pending. |
| EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); |
| |
| // DidReceiveCompositorFrameAck should get called twice: once for the child |
| // and once for the now active parent CompositorFrame. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 2); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Verify that the child CompositorFrame activates immediately. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that the parent Surface has activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // Submit a new parent CompositorFrame to add a reference. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| |
| // Verify that the parent Surface has activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that there is no temporary reference for the child and that |
| // the reference from the parent to the child still exists. |
| EXPECT_FALSE(HasTemporaryReference(child_id1)); |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
| |
| // The parent submits another CompositorFrame that depends on |child_id2|. |
| // Submitting a pending CompositorFrame will not trigger a |
| // CompositorFrameAck. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // The parent surface should now have both a pending and activate |
| // CompositorFrame. Verify that the set of child references from |
| // |parent_id| are only from the active CompositorFrame. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
| |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the parent Surface has activated and no longer has a |
| // pending CompositorFrame. Also verify that |child_id1| is no longer a |
| // child reference of |parent_id|. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| // The parent will not immediately refer to the child until it submits a new |
| // CompositorFrame with the reference. |
| EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); |
| } |
| |
| // Verifies that LatencyInfo does not get too large after multiple frame |
| // submissions. |
| TEST_F(SurfaceSynchronizationTest, LimitLatencyInfo) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with latency info |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrameBuilder builder; |
| builder.AddDefaultRenderPass(); |
| for (int i = 0; i < 60; ++i) |
| builder.AddLatencyInfo(info); |
| CompositorFrame frame = builder.Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(frame)); |
| |
| // Verify that the surface has an active frame and no pending frame. |
| Surface* surface = GetSurfaceForId(parent_id); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Submit another frame with some other latency info. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| builder.AddDefaultRenderPass(); |
| for (int i = 0; i < 60; ++i) |
| builder.AddLatencyInfo(info); |
| CompositorFrame frame2 = builder.Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the surface has an active frame and no pending frames. |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Verify that the surface has no latency info objects because it grew |
| // too large. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->TakeActiveLatencyInfo(&info_list); |
| EXPECT_EQ(0u, info_list.size()); |
| } |
| |
| // Checks whether SurfaceAllocationGroup properly aggregates LatencyInfo of |
| // multiple surfaces. In this variation of the test, there are no pending |
| // frames. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoAggregation_NoUnresolvedDependencies) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with latency info |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .AddLatencyInfo(info) |
| .Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame)); |
| |
| // Verify that the old surface has an active frame and no pending frame. |
| Surface* old_surface = GetSurfaceForId(parent_id1); |
| ASSERT_NE(nullptr, old_surface); |
| EXPECT_TRUE(old_surface->HasActiveFrame()); |
| EXPECT_FALSE(old_surface->HasPendingFrame()); |
| |
| // Submit another frame with some other latency info and a different |
| // LocalSurfaceId. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| CompositorFrame frame2 = CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .AddLatencyInfo(info2) |
| .Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the new surface has an active frame and no pending frames. |
| Surface* surface = GetSurfaceForId(parent_id2); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Verify that the new surface has both latency info elements. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->allocation_group()->TakeAggregatedLatencyInfoUpTo(surface, |
| &info_list); |
| EXPECT_EQ(2u, info_list.size()); |
| |
| ui::LatencyInfo aggregated_latency_info = info_list[0]; |
| aggregated_latency_info.AddNewLatencyFrom(info_list[1]); |
| |
| // Two components are the original ones, and the third one is |
| // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame |
| // submit. |
| EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); |
| |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| } |
| |
| // Checks whether SurfaceAllocationGroup properly aggregates LatencyInfo of |
| // multiple surfaces. In this variation of the test, the older surface has both |
| // pending and active frames and we verify that the LatencyInfo of both pending |
| // and active frame are present in the aggregated LatencyInfo. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoAggregation_OldSurfaceHasPendingAndActiveFrame) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with no unresolved dependency. |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = MakeDefaultCompositorFrame(kBeginFrameSourceId); |
| frame.metadata.latency_info.push_back(info); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame)); |
| |
| // Submit a frame with unresolved dependencies. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| CompositorFrame frame2 = MakeCompositorFrame( |
| {child_id}, empty_surface_ranges(), std::vector<TransferableResource>()); |
| frame2.metadata.latency_info.push_back(info2); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the old surface has both an active and a pending frame. |
| Surface* old_surface = GetSurfaceForId(parent_id1); |
| ASSERT_NE(nullptr, old_surface); |
| EXPECT_TRUE(old_surface->HasActiveFrame()); |
| EXPECT_TRUE(old_surface->HasPendingFrame()); |
| |
| // Submit a frame with a new local surface id. |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the new surface has an active frame only. |
| Surface* surface = GetSurfaceForId(parent_id2); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Verify that the aggregated LatencyInfo has LatencyInfo from both active and |
| // pending frame of the old surface. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->allocation_group()->TakeAggregatedLatencyInfoUpTo(surface, |
| &info_list); |
| EXPECT_EQ(2u, info_list.size()); |
| |
| ui::LatencyInfo aggregated_latency_info = info_list[0]; |
| aggregated_latency_info.AddNewLatencyFrom(info_list[1]); |
| |
| // Two components are the original ones, and the third one is |
| // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame |
| // submit. |
| EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); |
| |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| } |
| |
| // Checks whether SurfaceAllocationGroup properly aggregates LatencyInfo of |
| // multiple surfaces. In this variation of the test, the newer surface has a |
| // pending frame that becomes active after the dependency is resolved and we |
| // make sure the LatencyInfo of the activated frame is included in the |
| // aggregated LatencyInfo. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoAggregation_NewSurfaceHasPendingFrame) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with no unresolved dependencies. |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = MakeDefaultCompositorFrame(kBeginFrameSourceId); |
| frame.metadata.latency_info.push_back(info); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame)); |
| |
| // Verify that the old surface has an active frame only. |
| Surface* old_surface = GetSurfaceForId(parent_id1); |
| ASSERT_NE(nullptr, old_surface); |
| EXPECT_TRUE(old_surface->HasActiveFrame()); |
| EXPECT_FALSE(old_surface->HasPendingFrame()); |
| |
| // Submit a frame with a new local surface id and with unresolved |
| // dependencies. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| CompositorFrame frame2 = MakeCompositorFrame( |
| {child_id}, empty_surface_ranges(), std::vector<TransferableResource>()); |
| frame2.metadata.latency_info.push_back(info2); |
| |
| parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the new surface has a pending frame and no active frame. |
| Surface* surface = GetSurfaceForId(parent_id2); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasPendingFrame()); |
| EXPECT_FALSE(surface->HasActiveFrame()); |
| |
| // Resolve the dependencies. The frame in parent's surface must become active. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| |
| // Both latency info elements must exist in the aggregated LatencyInfo. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->allocation_group()->TakeAggregatedLatencyInfoUpTo(surface, |
| &info_list); |
| EXPECT_EQ(2u, info_list.size()); |
| |
| ui::LatencyInfo aggregated_latency_info = info_list[0]; |
| aggregated_latency_info.AddNewLatencyFrom(info_list[1]); |
| |
| // Two components are the original ones, and the third one is |
| // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame |
| // submit. |
| EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); |
| |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| } |
| |
| // Checks whether SurfaceAllocationGroup properly aggregates LatencyInfo of |
| // multiple surfaces. In this variation of the test, multiple older surfaces |
| // with pending frames exist during aggregation of an activated frame on a newer |
| // surface. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoAggregation_MultipleOldSurfacesWithPendingFrames) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId parent_id3 = MakeSurfaceId(kParentFrameSink, 3); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with unresolved dependencies to parent_id1. |
| ui::LatencyInfo info1; |
| info1.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame1 = MakeCompositorFrame( |
| {child_id1}, empty_surface_ranges(), std::vector<TransferableResource>()); |
| frame1.metadata.latency_info.push_back(info1); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame1)); |
| |
| // Submit a frame with unresolved dependencies to parent_id2. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| CompositorFrame frame2 = MakeCompositorFrame( |
| {child_id2}, empty_surface_ranges(), std::vector<TransferableResource>()); |
| frame2.metadata.latency_info.push_back(info2); |
| |
| parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the both old surfaces have pending frames. |
| Surface* old_surface1 = GetSurfaceForId(parent_id1); |
| Surface* old_surface2 = GetSurfaceForId(parent_id2); |
| ASSERT_NE(nullptr, old_surface1); |
| ASSERT_NE(nullptr, old_surface2); |
| EXPECT_FALSE(old_surface1->HasActiveFrame()); |
| EXPECT_FALSE(old_surface2->HasActiveFrame()); |
| EXPECT_TRUE(old_surface1->HasPendingFrame()); |
| EXPECT_TRUE(old_surface2->HasPendingFrame()); |
| |
| // Submit a frame with no dependencies to the new surface parent_id3. |
| parent_support().SubmitCompositorFrame( |
| parent_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the new surface has an active frame only. |
| Surface* surface = GetSurfaceForId(parent_id3); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Verify that the aggregated LatencyInfo has LatencyInfo from both old |
| // surfaces. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->allocation_group()->TakeAggregatedLatencyInfoUpTo(surface, |
| &info_list); |
| EXPECT_EQ(2u, info_list.size()); |
| |
| ui::LatencyInfo aggregated_latency_info = info_list[0]; |
| aggregated_latency_info.AddNewLatencyFrom(info_list[1]); |
| |
| // Two components are the original ones, and the third one is |
| // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame |
| // submit. |
| EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); |
| |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type1, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(aggregated_latency_info.FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| } |
| |
| // This test verifies that when a new surface is created, the LatencyInfo of the |
| // previous surface does not get carried over into the new surface. |
| TEST_F(SurfaceSynchronizationTest, LatencyInfoNotCarriedOver) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_COMPONENT_TYPE_LAST; |
| |
| // Submit a frame with latency info |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .AddLatencyInfo(info) |
| .Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
| std::move(frame)); |
| |
| // Verify that the old surface has an active frame and no pending frame. |
| Surface* old_surface = GetSurfaceForId(parent_id1); |
| ASSERT_NE(nullptr, old_surface); |
| EXPECT_TRUE(old_surface->HasActiveFrame()); |
| EXPECT_FALSE(old_surface->HasPendingFrame()); |
| |
| // Submit another frame with some other latency info and a different |
| // LocalSurfaceId. |
| ui::LatencyInfo info2; |
| info2.AddLatencyNumber(latency_type2); |
| |
| CompositorFrame frame2 = CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .AddLatencyInfo(info2) |
| .Build(); |
| |
| parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), |
| std::move(frame2)); |
| |
| // Verify that the new surface has an active frame and no pending frames. |
| Surface* surface = GetSurfaceForId(parent_id2); |
| ASSERT_NE(nullptr, surface); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| |
| // Verify that the old surface still has its LatencyInfo. |
| std::vector<ui::LatencyInfo> info_list; |
| old_surface->TakeActiveLatencyInfo(&info_list); |
| EXPECT_EQ(1u, info_list.size()); |
| EXPECT_TRUE(info_list[0].FindLatency(latency_type1, nullptr)); |
| EXPECT_FALSE(info_list[0].FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(info_list[0].FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| |
| // Take the aggregated LatencyInfo. Since the LatencyInfo of the old surface |
| // is previously taken, it should not show up here. |
| info_list.clear(); |
| surface->allocation_group()->TakeAggregatedLatencyInfoUpTo(surface, |
| &info_list); |
| EXPECT_EQ(1u, info_list.size()); |
| EXPECT_EQ(2u, info_list[0].latency_components().size()); |
| EXPECT_FALSE(info_list[0].FindLatency(latency_type1, nullptr)); |
| EXPECT_TRUE(info_list[0].FindLatency(latency_type2, nullptr)); |
| EXPECT_TRUE(info_list[0].FindLatency( |
| ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); |
| } |
| |
| // Checks that resources and ack are sent together if possible. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, ReturnResourcesWithAck) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| TransferableResource resource; |
| resource.id = ResourceId(1234); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource})); |
| ResourceId id = resource.ToReturnedResource().id; |
| EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); |
| if (BeginFrameAcksEnabled()) { |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| } else { |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .WillOnce([=](std::vector<ReturnedResource> got) { |
| EXPECT_EQ(1u, got.size()); |
| EXPECT_EQ(id, got[0].id); |
| }); |
| } |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| if (BeginFrameAcksEnabled()) { |
| EXPECT_CALL(support_client_, OnBeginFrame(_, _, _, _)) |
| .WillOnce([=](const BeginFrameArgs& args, |
| const FrameTimingDetailsMap& timing_details, |
| bool frame_ack, std::vector<ReturnedResource> got) { |
| EXPECT_EQ(1u, got.size()); |
| EXPECT_EQ(id, got[0].id); |
| }); |
| SendNextBeginFrame(); |
| } |
| } |
| |
| // Verifies that arrival of a new CompositorFrame doesn't change the fact that a |
| // surface is marked for destruction. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, SubmitToDestroyedSurface) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); |
| |
| // Create the child surface by submitting a frame to it. |
| EXPECT_EQ(nullptr, GetSurfaceForId(child_id)); |
| TransferableResource resource; |
| resource.id = ResourceId(1234); |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource})); |
| // Verify that the child surface is created. |
| Surface* surface = GetSurfaceForId(child_id); |
| EXPECT_NE(nullptr, surface); |
| |
| // Add a reference from the parent to the child. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(child_id)}, |
| std::vector<TransferableResource>())); |
| |
| // Attempt to destroy the child surface. The surface must still exist since |
| // the parent needs it but it will be marked as destroyed. |
| child_support1().EvictSurface(child_id.local_surface_id()); |
| surface = GetSurfaceForId(child_id); |
| EXPECT_NE(nullptr, surface); |
| EXPECT_TRUE(IsMarkedForDestruction(child_id)); |
| |
| // Child submits another frame to the same local surface id that is marked |
| // destroyed. The frame is immediately rejected. |
| { |
| EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 1); |
| surface_observer().Reset(); |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| } |
| |
| // The parent stops referencing the child surface. This allows the child |
| // surface to be garbage collected. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| { |
| ResourceId id = resource.ToReturnedResource().id; |
| EXPECT_CALL(support_client_, ReclaimResources(_)) |
| .WillOnce([=](std::vector<ReturnedResource> got) { |
| EXPECT_EQ(1u, got.size()); |
| EXPECT_EQ(id, got[0].id); |
| }); |
| frame_sink_manager().surface_manager()->GarbageCollectSurfaces(); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| } |
| |
| // We shouldn't observe an OnFirstSurfaceActivation because we reject the |
| // CompositorFrame to the evicted surface. |
| EXPECT_EQ(SurfaceId(), surface_observer().last_created_surface_id()); |
| } |
| |
| // Verifies that if a LocalSurfaceId belonged to a surface that doesn't |
| // exist anymore, it can not be recreated. |
| TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsNotReusable) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); |
| |
| // Submit the first frame. Creates the surface. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_NE(nullptr, GetSurfaceForId(child_id)); |
| |
| // Add a reference from parent. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(child_id)}, |
| std::vector<TransferableResource>())); |
| |
| // Remove the reference from parant. This allows us to destroy the surface. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Destroy the surface. |
| child_support1().EvictSurface(child_id.local_surface_id()); |
| frame_sink_manager().surface_manager()->GarbageCollectSurfaces(); |
| |
| EXPECT_EQ(nullptr, GetSurfaceForId(child_id)); |
| |
| // Submit another frame with the same local surface id. The surface should not |
| // be recreated. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_EQ(nullptr, GetSurfaceForId(child_id)); |
| } |
| |
| // This test verifies that a crash does not occur if garbage collection is |
| // triggered during surface dependency resolution. This test triggers garbage |
| // collection during surface resolution, by causing an activation to remove |
| // a surface subtree from the root. Both the old subtree and the new |
| // activated subtree refer to the same dependency. The old subtree was activated |
| // by deadline, and the new subtree was activated by a dependency finally |
| // resolving. |
| TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| |
| // Advance BeginFrames to trigger a deadline. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // The display surface now has two CompositorFrames. One that is pending, |
| // indirectly blocked on child_id and one that is active, also indirectly |
| // referring to child_id, but activated due to the deadline above. |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| |
| // Submitting a CompositorFrame will trigger garbage collection of the |
| // |parent_id1| subtree. This should not crash. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| } |
| |
| // This test verifies that a crash does not occur if garbage collection is |
| // triggered when a deadline forces frame activation. This test triggers garbage |
| // collection during deadline activation by causing the activation of a display |
| // frame to replace a previously activated display frame that was referring to |
| // a now-unreachable surface subtree. That subtree gets garbage collected during |
| // deadline activation. SurfaceDependencyTracker is also tracking a surface |
| // from that subtree due to an unresolved dependency. This test verifies that |
| // this dependency resolution does not crash. |
| TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // |parent_id1| is blocked on |child_id|. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id1}, {SurfaceRange(parent_id1)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| EXPECT_FALSE(display_surface()->HasActiveFrame()); |
| |
| // Advance BeginFrames to trigger a deadline. This activates the |
| // CompositorFrame submitted above. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| |
| // By submitting a display CompositorFrame, and replacing the parent's |
| // CompositorFrame with another surface ID, parent_id1 becomes unreachable |
| // and a candidate for garbage collection. |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| |
| // Now |parent_id1| is only kept alive by the active |display_id| frame. |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| |
| // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| |
| // and |parent_id2|. By activating the pending |display_id| frame by deadline, |
| // |parent_id1| becomes unreachable and is garbage collected while |
| // SurfaceDependencyTracker is in the process of activating surfaces. This |
| // should not cause a crash or use-after-free. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| } |
| |
| // This test verifies that a CompositorFrame will only blocked on embedded |
| // surfaces but not on other retained surface IDs in the CompositorFrame. |
| TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| |
| // Submitting a CompositorFrame with |parent_id2| so that the display |
| // CompositorFrame can hold a reference to it. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id2}, {SurfaceRange(parent_id1)}, |
| std::vector<TransferableResource>())); |
| |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| EXPECT_FALSE(display_surface()->HasActiveFrame()); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| |
| // Verify that the display CompositorFrame will only block on |parent_id2| |
| // but not |parent_id1|. |
| EXPECT_THAT(display_surface()->activation_dependencies(), |
| UnorderedElementsAre(parent_id2)); |
| // Verify that the display surface holds no references while its |
| // CompositorFrame is pending. |
| EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); |
| |
| // Submitting a CompositorFrame with |parent_id2| should unblock the |
| // display CompositorFrame. |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_THAT(display_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies that a late arriving CompositorFrame activates |
| // immediately and does not trigger a new deadline. |
| TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame( |
| {parent_id1}, {SurfaceRange(absl::nullopt, parent_id1)}, |
| std::vector<TransferableResource>(), MakeDefaultDeadline())); |
| |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| EXPECT_FALSE(display_surface()->HasActiveFrame()); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| |
| // Advance BeginFrames to trigger a deadline. This activates the |
| // CompositorFrame submitted above. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| |
| // A late arriving CompositorFrame should activate immediately without |
| // scheduling a deadline and without waiting for dependencies to resolve. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame({child_id1}, {SurfaceRange(absl::nullopt, child_id1)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| } |
| |
| // This test verifies that a late arriving CompositorFrame activates |
| // immediately along with its subtree and does not trigger a new deadline. |
| TEST_F(SurfaceSynchronizationTest, MultiLevelLateArrivingDependency) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id}, {SurfaceRange(absl::nullopt, parent_id)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| EXPECT_FALSE(display_surface()->HasActiveFrame()); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| |
| // Issue some BeginFrames to trigger the deadline and activate the display's |
| // surface. |parent_id| is now late. Advance BeginFrames to trigger a |
| // deadline. |
| for (int i = 0; i < 4; ++i) { |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| SendNextBeginFrame(); |
| } |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| |
| // The child surface is not currently causally linked to the display's |
| // surface and so it gets a separate deadline. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame( |
| {arbitrary_id}, {SurfaceRange(absl::nullopt, arbitrary_id)}, |
| std::vector<TransferableResource>(), MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| |
| // Submitting a CompositorFrame to the parent surface creates a dependency |
| // chain from the display to the parent to the child, allowing them all to |
| // assume the same deadline. Both the parent and the child are determined to |
| // be late and activate immediately. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->has_deadline()); |
| } |
| |
| // This test verifies that CompositorFrames submitted to a surface referenced |
| // by a parent CompositorFrame as a fallback will be ACK'ed immediately. |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, FallbackSurfacesClosed) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| // This is the fallback child surface that the parent holds a reference to. |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| // This is the primary child surface that the parent wants to block on. |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kChildFrameSink2, 3); |
| |
| SendNextBeginFrame(); |
| |
| // child_support1 submits a CompositorFrame with unresolved dependencies. |
| // DidReceiveCompositorFrameAck should not be called because the frame hasn't |
| // activated yet. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, |
| {SurfaceRange(absl::nullopt, arbitrary_id)}, {}, |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // The parent is blocked on |child_id2| and references |child_id1|. |
| // |child_id1| should immediately activate and the ack must be sent. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 1); |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1, child_id2)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Any further CompositorFrames sent to |child_id1| will also activate |
| // immediately so that the child can submit another frame and catch up with |
| // the parent. |
| SendNextBeginFrame(); |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 1); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, |
| {SurfaceRange(absl::nullopt, arbitrary_id)}, {}, |
| MakeDefaultDeadline())); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| } |
| |
| // This test verifies that two surface subtrees have independent deadlines. |
| TEST_F(SurfaceSynchronizationTest, IndependentDeadlines) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_FALSE(child_surface2()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2()->HasActiveFrame()); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| // Submit another CompositorFrame to |child_id1| that blocks on |
| // |arbitrary_id|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame( |
| {arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| FrameDeadline(Now(), 3, BeginFrameArgs::DefaultInterval(), false))); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| |
| // Advance to the next BeginFrame. |child_id1|'s pending Frame should activate |
| // after 2 frames. |
| SendNextBeginFrame(); |
| |
| // Submit another CompositorFrame to |child_id2| that blocks on |
| // |arbitrary_id|. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface2()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface2()->has_deadline()); |
| |
| // If we issue another two BeginFrames both children should remain blocked. |
| SendNextBeginFrame(); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| EXPECT_TRUE(child_surface2()->has_deadline()); |
| |
| // Issuing another BeginFrame should activate the frame in |child_id1| but not |
| // |child_id2|. This verifies that |child_id1| and |child_id2| have different |
| // deadlines. |
| SendNextBeginFrame(); |
| |
| EXPECT_TRUE(child_surface2()->has_deadline()); |
| |
| EXPECT_FALSE(child_surface1()->has_deadline()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| |
| SendNextBeginFrame(); |
| |
| EXPECT_TRUE(child_surface2()->has_deadline()); |
| EXPECT_TRUE(child_surface2()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2()->HasActiveFrame()); |
| |
| // Issuing another BeginFrame should activate the frame in |child_id2|. |
| SendNextBeginFrame(); |
| |
| EXPECT_FALSE(child_surface2()->has_deadline()); |
| EXPECT_FALSE(child_surface2()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2()->HasActiveFrame()); |
| } |
| |
| // This test verifies that a child inherits its deadline from its dependent |
| // parent (embedder) if the deadline is shorter than child's deadline. |
| TEST_F(SurfaceSynchronizationTest, InheritShorterDeadline) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| // Using the default lower bound deadline results in the deadline of 2 frames |
| // effectively being ignored because the default lower bound is 4 frames. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame( |
| {child_id1}, {SurfaceRange(absl::nullopt, child_id1)}, |
| std::vector<TransferableResource>(), |
| FrameDeadline(Now(), 2, BeginFrameArgs::DefaultInterval(), true))); |
| |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| |
| // Advance to the next BeginFrame. The parent surface will activate in 3 |
| // frames. |
| SendNextBeginFrame(); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| |
| // If we issue another three BeginFrames then both the parent and the child |
| // should activate, verifying that the child's deadline is inherited from the |
| // parent. |
| for (int i = 0; i < 3; ++i) { |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| SendNextBeginFrame(); |
| } |
| |
| // Verify that both the parent and child have activated. |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->has_deadline()); |
| } |
| |
| // This test verifies that in case of A embedding B embedding C, if the deadline |
| // of A is longer than the deadline of B, B's deadline is not extended. |
| TEST_F(SurfaceSynchronizationTest, ChildDeadlineNotExtendedByInheritance) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // Parent blocks on Child1 with a deadline of 10. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDeadline(10))); |
| |
| // Child1 blocks on Child2 with a deadline of 2. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(absl::nullopt, child_id2)}, |
| std::vector<TransferableResource>(), |
| MakeDeadline(2))); |
| |
| // Both Parent and Child1 should be blocked because Child2 doesn't exist. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| |
| // Send one BeginFrame. Both Parent and Child1 should be still blocked because |
| // Child2 still doesn't exist and Child1's deadline is 2. |
| SendNextBeginFrame(); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| |
| // Send one more BeginFrame. Child1 should activate by deadline, and parent |
| // will consequenctly activates. This wouldn't happen if Child1's deadline was |
| // extended to match Parent's deadline. |
| SendNextBeginFrame(); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| } |
| |
| // This test verifies that all surfaces within a dependency chain will |
| // ultimately inherit the parent's shorter deadline even if the grandchild is |
| // available before the child. |
| TEST_F(SurfaceSynchronizationTest, MultiLevelDeadlineInheritance) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame({parent_id}, {SurfaceRange(absl::nullopt, parent_id)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(display_surface()->HasPendingFrame()); |
| EXPECT_FALSE(display_surface()->HasActiveFrame()); |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| |
| // Issue a BeginFrame to move closer to the display's deadline. |
| SendNextBeginFrame(); |
| |
| // The child surface is not currently causally linked to the display's |
| // surface and so it gets a separate deadline. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame( |
| {arbitrary_id}, {SurfaceRange(absl::nullopt, arbitrary_id)}, |
| std::vector<TransferableResource>(), MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
| EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| |
| // Submitting a CompositorFrame to the parent frame creates a dependency |
| // chain from the display to the parent to the child, allowing them all to |
| // assume the same deadline. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| |
| // Advancing the time by three BeginFrames should activate all the surfaces. |
| for (int i = 0; i < 3; ++i) { |
| EXPECT_TRUE(display_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(child_surface1()->has_deadline()); |
| SendNextBeginFrame(); |
| } |
| |
| // Verify that all the CompositorFrames have activated. |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->has_deadline()); |
| } |
| |
| // This test verifies that no crash occurs if a CompositorFrame activates AFTER |
| // its FrameSink has been destroyed. |
| TEST_F(SurfaceSynchronizationTest, FrameActivationAfterFrameSinkDestruction) { |
| const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| |
| // Submit a CompositorFrame that refers to to |parent_id|. |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(parent_id)}, |
| std::vector<TransferableResource>())); |
| |
| EXPECT_FALSE(display_surface()->has_deadline()); |
| EXPECT_FALSE(display_surface()->HasPendingFrame()); |
| EXPECT_TRUE(display_surface()->HasActiveFrame()); |
| EXPECT_THAT(GetChildReferences(display_id), UnorderedElementsAre(parent_id)); |
| |
| // Submit a new CompositorFrame to the parent CompositorFrameSink. It should |
| // now have a pending and active CompositorFrame. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| surface_observer().Reset(); |
| |
| // Destroy the parent CompositorFrameSink. The parent_surface will be kept |
| // alive by the display. |
| DestroyFrameSink(kParentFrameSink); |
| |
| // The parent surface stays alive through the display. |
| Surface* parent_surface = GetSurfaceForId(parent_id); |
| EXPECT_NE(nullptr, parent_surface); |
| |
| // Submitting a new CompositorFrame to the display should free the parent. |
| display_support().SubmitCompositorFrame( |
| display_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| frame_sink_manager().surface_manager()->GarbageCollectSurfaces(); |
| |
| parent_surface = GetSurfaceForId(parent_id); |
| EXPECT_EQ(nullptr, parent_surface); |
| } |
| |
| TEST_F(SurfaceSynchronizationTest, PreviousFrameSurfaceId) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // Submit a frame with no dependencies to |parent_id1|. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Submit a frame with unresolved dependencies to |parent_id2|. The frame |
| // should become pending and previous_frame_surface_id() should return |
| // |parent_id1|. |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeCompositorFrame({child_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| Surface* parent_surface2 = |
| frame_sink_manager().surface_manager()->GetSurfaceForId(parent_id2); |
| EXPECT_FALSE(parent_surface2->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface2->HasPendingFrame()); |
| |
| // Activate the pending frame in |parent_id2|. previous_frame_surface_id() |
| // should still return |parent_id1|. |
| parent_surface2->ActivatePendingFrameForDeadline(); |
| EXPECT_TRUE(parent_surface2->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface2->HasPendingFrame()); |
| EXPECT_EQ(parent_id1, parent_surface2->previous_frame_surface_id()); |
| } |
| |
| TEST_F(SurfaceSynchronizationTest, FrameIndexWithPendingFrames) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| constexpr int n_iterations = 7; |
| |
| // Submit a frame with no dependencies that will activate immediately. Record |
| // the initial frame index. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| Surface* parent_surface = |
| frame_sink_manager().surface_manager()->GetSurfaceForId(parent_id); |
| uint64_t initial_frame_index = parent_surface->GetActiveFrameIndex(); |
| |
| // Submit frames with unresolved dependencies. GetActiveFrameIndex should |
| // return the same value as before. |
| for (int i = 0; i < n_iterations; i++) { |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_EQ(initial_frame_index, parent_surface->GetActiveFrameIndex()); |
| } |
| |
| // Activate the pending frame. GetActiveFrameIndex should return the frame |
| // index of the newly activated frame. |
| parent_surface->ActivatePendingFrameForDeadline(); |
| EXPECT_EQ(initial_frame_index + n_iterations, |
| parent_surface->GetActiveFrameIndex()); |
| } |
| |
| // This test verifies that a new surface with a pending CompositorFrame gets |
| // a temporary reference immediately, as opposed to when the surface activates. |
| TEST_F(SurfaceSynchronizationTest, PendingSurfaceKeptAlive) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // |parent_id| depends on |child_id1|. It shouldn't activate. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(HasTemporaryReference(parent_id)); |
| } |
| |
| // Tests getting the correct active frame index. |
| TEST_F(SurfaceSynchronizationTest, ActiveFrameIndex) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1, child_id2}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // parent_support is blocked on |child_id1| and |child_id2|. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_EQ(0u, parent_surface()->GetActiveFrameIndex()); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| uint64_t expected_index = kFrameIndexStart; |
| EXPECT_EQ(expected_index, parent_surface()->GetActiveFrameIndex()); |
| } |
| |
| // This test verifies that SurfaceManager::GetLatestInFlightSurface returns |
| // the latest child surface not yet set as a fallback by the parent. |
| // Alternatively, it returns the fallback surface specified, if no tempoary |
| // references to child surfaces are available. This mechanism is used by surface |
| // synchronization to present the freshest surfaces available at aggregation |
| // time. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurface) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2); |
| const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 2, 3); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Verify that the child CompositorFrame activates immediately. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that the parent Surface has activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that there is a temporary reference for the child and there is |
| // no reference from the parent to the child yet. |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2))); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| |
| // Verify that the parent Surface has activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that there is no temporary reference for the child and there is |
| // a reference from the parent to the child. |
| EXPECT_FALSE(HasTemporaryReference(child_id1)); |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2))); |
| |
| // Submit a child CompositorFrame to a new SurfaceId and verify that |
| // GetLatestInFlightSurface returns the right surface. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that there is a temporary reference for child_id2 and there is |
| // a reference from the parent to child_id1. |
| EXPECT_TRUE(HasTemporaryReference(child_id2)); |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| |
| // If the primary surface is old, then we shouldn't return an in-flight |
| // surface that is newer than the primary. |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(child_id1))); |
| |
| // Submit a child CompositorFrame to a new SurfaceId and verify that |
| // GetLatestInFlightSurface returns the right surface. |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that there is a temporary reference for child_id3. |
| EXPECT_TRUE(HasTemporaryReference(child_id3)); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id3}, {SurfaceRange(absl::nullopt, child_id3)}, |
| std::vector<TransferableResource>())); |
| |
| EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id3)); |
| |
| // If the primary surface is active, we return it. |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface will return nullptr when the |
| // start of the range is newer than its end, even if a surface matching the end |
| // exists. |
| TEST_F(SurfaceSynchronizationTest, |
| LatestInFlightSurfaceWithInvalidSurfaceRange) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Verify that the parent and child CompositorFrames are active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| const SurfaceId bogus_child_id = MakeSurfaceId(kChildFrameSink1, 10); |
| |
| // The end exists but don't return it because the start is newer than the end. |
| EXPECT_EQ(nullptr, |
| GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id1))); |
| |
| // In this case, the end doesn't exist either. Still return nullptr. |
| EXPECT_EQ(nullptr, |
| GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id2))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface will return the primary or |
| // nullptr if fallback is not specified. |
| // TODO(akaba): this would change after https://crbug.com/861769 |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithoutFallback) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| // Verify that |child_id1| is active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(child_id1, child_id2)}, |
| std::vector<TransferableResource>())); |
| |
| // Verify that the |parent_id| is not active yet. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id2)); |
| |
| // Verify that |child_id1| is the latest active surface. |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2))); |
| |
| // Fallback is not specified and |child_id1| is the latest. |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(absl::nullopt, child_id2))); |
| |
| // Activate |child_id2| |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| // Verify that child2 is active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| |
| // Verify that |child_id2| is the latest active surface. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id2))); |
| |
| // Fallback is not specified but primary exists so we return it. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(absl::nullopt, child_id2))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface will not return null if the |
| // fallback is garbage collected, but instead returns the latest surface older |
| // than primary if that exists. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithGarbageFallback) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 3); |
| const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink1, 4); |
| |
| // Activate |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that |child_id1| CompositorFrames is active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| |
| // Verify that parent is referencing |child_id1|. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| EXPECT_THAT(parent_surface()->active_referenced_surfaces(), |
| UnorderedElementsAre(child_id1)); |
| EXPECT_FALSE(HasTemporaryReference(child_id1)); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that |child_id2| CompositorFrames is active and it has a temporary |
| // reference. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(HasTemporaryReference(child_id2)); |
| |
| // Activate |child_id3|. |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that |child_id3| CompositorFrames is active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(HasTemporaryReference(child_id3)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id2)}, |
| std::vector<TransferableResource>())); |
| |
| // Verify that parent is referencing |child_id2| which lost its temporary |
| // reference, but |child_id3| still has a temporary reference. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| EXPECT_THAT(parent_surface()->active_referenced_surfaces(), |
| UnorderedElementsAre(child_id2)); |
| EXPECT_FALSE(HasTemporaryReference(child_id2)); |
| EXPECT_TRUE(HasTemporaryReference(child_id3)); |
| |
| // Garbage collect |child_id1|. |
| frame_sink_manager().surface_manager()->GarbageCollectSurfaces(); |
| |
| // Make sure |child_id1| is garbage collected. |
| EXPECT_EQ(frame_sink_manager().surface_manager()->GetSurfaceForId(child_id1), |
| nullptr); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| } |
| |
| // This test verifies that in the case of different frame sinks |
| // GetLatestInFlightSurface will return the latest surface in the primary's |
| // FrameSinkId or the latest in the fallback's FrameSinkId if no surface exists |
| // in the primary's. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceDifferentFrameSinkIds) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); |
| const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink2, 2); |
| |
| // Activate |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id4}, {SurfaceRange(child_id1, child_id4)}, |
| std::vector<TransferableResource>())); |
| |
| // Primary's frame sink id empty and |child_id2| is the latest in fallback's |
| // frame sink. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| // Activate |child_id3| which is in different frame sink. |
| child_support2().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // |child_id3| is the latest in primary's frame sink. |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface will return the |
| // primary surface ID if it is in the temporary reference queue. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceReturnPrimary) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 3); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Create a reference from |parent_id| to |child_id|. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // GetLatestInFlightSurface will return the primary surface ID if it's |
| // available. |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface can use persistent |
| // references to compute the latest surface. |
| TEST_F(SurfaceSynchronizationTest, |
| LatestInFlightSurfaceUsesPersistentReferences) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 3); |
| |
| // Activate |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // |child_id1| now should have a temporary reference. |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| EXPECT_TRUE(surface_manager() |
| ->GetSurfacesThatReferenceChildForTesting(child_id1) |
| .empty()); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // |child_id2| now should have a temporary reference. |
| EXPECT_TRUE(HasTemporaryReference(child_id2)); |
| EXPECT_TRUE(surface_manager() |
| ->GetSurfacesThatReferenceChildForTesting(child_id2) |
| .empty()); |
| |
| // Create a reference from |parent_id| to |child_id2|. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id2)}, |
| std::vector<TransferableResource>())); |
| |
| // |child_id1| have no references and can be garbage collected. |
| EXPECT_FALSE(HasTemporaryReference(child_id1)); |
| EXPECT_TRUE(surface_manager() |
| ->GetSurfacesThatReferenceChildForTesting(child_id1) |
| .empty()); |
| |
| // |child_id2| has a persistent references now. |
| EXPECT_FALSE(HasTemporaryReference(child_id2)); |
| EXPECT_FALSE(surface_manager() |
| ->GetSurfacesThatReferenceChildForTesting(child_id2) |
| .empty()); |
| |
| // Verify that GetLatestInFlightSurface returns |child_id2|. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| } |
| |
| // This test verifies that GetLatestInFlightSurface will skip a surface if |
| // its nonce is different. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceSkipDifferentNonce) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const base::UnguessableToken nonce1 = base::UnguessableToken::Create(); |
| const base::UnguessableToken nonce2 = base::UnguessableToken::Create(); |
| const base::UnguessableToken nonce3 = base::UnguessableToken::Create(); |
| const SurfaceId child_id1 = |
| SurfaceId(kChildFrameSink1, LocalSurfaceId(1, nonce1)); |
| const SurfaceId child_id2 = |
| SurfaceId(kChildFrameSink1, LocalSurfaceId(2, nonce1)); |
| const SurfaceId child_id3 = |
| SurfaceId(kChildFrameSink1, LocalSurfaceId(3, nonce2)); |
| const SurfaceId child_id4 = |
| SurfaceId(kChildFrameSink1, LocalSurfaceId(4, nonce2)); |
| const SurfaceId child_id5 = |
| SurfaceId(kChildFrameSink1, LocalSurfaceId(5, nonce3)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Create a reference from |parent_id| to |child_id|. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), {SurfaceRange(child_id1)}, |
| std::vector<TransferableResource>())); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // GetLatestInFlightSurface will return child_id3 because the nonce |
| // matches that of child_id4. |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| // GetLatestInFlightSurface will return child_id2 because the nonce |
| // doesn't match |child_id1| or |child_id5|. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id5))); |
| } |
| |
| // This test verifies that if a child submits a LocalSurfaceId newer that the |
| // parent's dependency, then the parent will drop its dependency and activate |
| // if possible. In this version of the test, parent sequence number of the |
| // activated surface is larger than that in the dependency, while the child |
| // sequence number is smaller. |
| TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive1) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id11 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId child_id12 = MakeSurfaceId(kChildFrameSink1, 2, 1); |
| const SurfaceId child_id21 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // |parent_id| depends on { child_id11, child_id21 }. It shouldn't activate. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id11, child_id21}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| |
| // The first child submits a new CompositorFrame to |child_id12|. |parent_id| |
| // no longer depends on |child_id11| because it cannot expect it to arrive. |
| // However, the parent is still blocked on |child_id21|. |
| child_support1().SubmitCompositorFrame( |
| child_id12.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id21)); |
| |
| // Finally, the second child submits a frame to the remaining dependency and |
| // the parent activates. |
| child_support2().SubmitCompositorFrame( |
| child_id21.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies that if a child submits a LocalSurfaceId newer that the |
| // parent's dependency, then the parent will drop its dependency and activate |
| // if possible. In this version of the test, parent sequence number of the |
| // activated surface is smaller than that in the dependency, while the child |
| // sequence number is larger. |
| TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive2) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id11 = MakeSurfaceId(kChildFrameSink1, 2, 1); |
| const SurfaceId child_id12 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId child_id21 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // |parent_id| depends on { child_id11, child_id21 }. It shouldn't activate. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id11, child_id21}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| |
| // The first child submits a new CompositorFrame to |child_id12|. |parent_id| |
| // no longer depends on |child_id11| because it cannot expect it to arrive. |
| // However, the parent is still blocked on |child_id21|. |
| child_support1().SubmitCompositorFrame( |
| child_id12.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id21)); |
| |
| // Finally, the second child submits a frame to the remaining dependency and |
| // the parent activates. |
| child_support2().SubmitCompositorFrame( |
| child_id21.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies that a surface will continue to observe a child surface |
| // until its dependency arrives. |
| TEST_F(SurfaceSynchronizationTest, ObserveDependenciesUntilArrival) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id21 = MakeSurfaceId(kChildFrameSink1, 2, 1); |
| const SurfaceId child_id22 = MakeSurfaceId(kChildFrameSink1, 2, 2); |
| |
| // |parent_id| depends on |child_id22|. It shouldn't activate. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id22}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| |
| // The child submits to |child_id21|. The parent should not activate. |
| child_support1().SubmitCompositorFrame( |
| child_id21.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id22)); |
| |
| // The child submits to |child_id22|. The parent should activate. |
| child_support1().SubmitCompositorFrame( |
| child_id22.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // This test verifies that we don't mark the previous active surface for |
| // destruction until the new surface activates. |
| TEST_F(SurfaceSynchronizationTest, |
| MarkPreviousSurfaceForDestructionAfterActivation) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| // Submit a CompositorFrame that has no dependencies. |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the CompositorFrame has been activated. |
| Surface* parent_surface1 = GetSurfaceForId(parent_id1); |
| EXPECT_TRUE(parent_surface1->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface1->HasPendingFrame()); |
| EXPECT_THAT(parent_surface1->activation_dependencies(), IsEmpty()); |
| EXPECT_FALSE(IsMarkedForDestruction(parent_id1)); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // Verify that the CompositorFrame to the new surface has not been activated. |
| Surface* parent_surface2 = GetSurfaceForId(parent_id2); |
| EXPECT_FALSE(parent_surface2->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface2->HasPendingFrame()); |
| EXPECT_THAT(parent_surface2->activation_dependencies(), |
| UnorderedElementsAre(arbitrary_id)); |
| EXPECT_FALSE(IsMarkedForDestruction(parent_id1)); |
| EXPECT_FALSE(IsMarkedForDestruction(parent_id2)); |
| |
| // Advance BeginFrames to trigger a deadline. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(parent_surface2->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| EXPECT_FALSE(parent_surface2->has_deadline()); |
| |
| // Verify that the CompositorFrame has been activated. |
| EXPECT_TRUE(parent_surface2->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface2->HasPendingFrame()); |
| EXPECT_THAT(parent_surface2->activation_dependencies(), IsEmpty()); |
| |
| // Verify that the old surface is now marked for destruction. |
| EXPECT_TRUE(IsMarkedForDestruction(parent_id1)); |
| EXPECT_FALSE(IsMarkedForDestruction(parent_id2)); |
| } |
| |
| // This test verifies that CompositorFrameSinkSupport does not refer to |
| // a valid but non-existant |last_activated_surface_id_|. |
| TEST_F(SurfaceSynchronizationTest, SetPreviousFrameSurfaceDoesntCrash) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // The parent CompositorFrame is not blocked on anything and so it should |
| // immediately activate. |
| EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid()); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the parent CompositorFrame has activated. |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(parent_support().last_activated_surface_id().is_valid()); |
| |
| // Submit another CompositorFrame to |parent_id|, but this time block it |
| // on |child_id1|. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id1}, empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| // Verify that the surface has both a pending and activate CompositorFrame. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id1)); |
| |
| // Evict the activated surface in the parent_support. |
| EXPECT_TRUE(parent_support().last_activated_surface_id().is_valid()); |
| parent_support().EvictSurface( |
| parent_support().last_activated_surface_id().local_surface_id()); |
| EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid()); |
| |
| // The CompositorFrame in the evicted |parent_id| activates here because it |
| // was blocked on |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // parent_support will be informed of the activation of a CompositorFrame |
| // associated with |parent_id|, but we clear |last_active_surface_id_| because |
| // it was evicted before. |
| EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid()); |
| |
| // Perform a garbage collection. |parent_id| should no longer exist. |
| EXPECT_NE(nullptr, GetSurfaceForId(parent_id)); |
| ExpireAllTemporaryReferencesAndGarbageCollect(); |
| EXPECT_EQ(nullptr, GetSurfaceForId(parent_id)); |
| |
| // This should not crash as the previous surface was cleared in |
| // CompositorFrameSinkSupport. |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| } |
| |
| // This test verifies that when a surface activates that has the same |
| // FrameSinkId of the primary but its embed token doesn't match, we don't |
| // update the references of the parent. |
| TEST_F(SurfaceSynchronizationTest, |
| SurfaceReferenceTracking_PrimaryEmbedTokenDoesntMatch) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2( |
| kChildFrameSink2, LocalSurfaceId(2, base::UnguessableToken::Create())); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 5); |
| |
| // The parent embeds (child_id1, child_id3). |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame( |
| empty_surface_ids(), {SurfaceRange(child_id1, child_id3)}, |
| std::vector<TransferableResource>(), MakeDefaultDeadline())); |
| |
| // Verify that no references exist. |
| EXPECT_THAT(GetReferencesFrom(parent_id), empty_surface_ids()); |
| |
| // Activate |child_id2|. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Since |child_id2| has a different embed token than both primary and |
| // fallback, it should not be used as a reference even if it has the same |
| // FrameSinkId as the primary. |
| EXPECT_THAT(GetReferencesFrom(parent_id), empty_surface_ids()); |
| |
| // Activate |child_id3|. |
| child_support2().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that a reference is acquired. |
| EXPECT_THAT(GetReferencesFrom(parent_id), UnorderedElementsAre(child_id3)); |
| } |
| |
| // This test verifies that a parent referencing a SurfaceRange get updated |
| // whenever a child surface activates inside this range. This should also update |
| // the SurfaceReferences tree. |
| TEST_F(SurfaceSynchronizationTest, |
| SurfaceReferenceTracking_NewerSurfaceUpdatesReferences) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 3); |
| const SurfaceId child_id4 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame( |
| empty_surface_ids(), {SurfaceRange(child_id1, child_id4)}, |
| std::vector<TransferableResource>(), MakeDefaultDeadline())); |
| |
| // Verify that no references exist. |
| EXPECT_THAT(GetReferencesFrom(parent_id), empty_surface_ids()); |
| |
| // Activate |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that a reference is acquired. |
| EXPECT_THAT(GetReferencesFrom(parent_id), UnorderedElementsAre(child_id1)); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the reference is updated. |
| EXPECT_THAT(GetReferencesFrom(parent_id), UnorderedElementsAre(child_id2)); |
| |
| // Activate |child_id4| in a different frame sink. |
| child_support2().SubmitCompositorFrame( |
| child_id4.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the reference is updated. |
| EXPECT_THAT(GetReferencesFrom(parent_id), UnorderedElementsAre(child_id4)); |
| |
| // Activate |child_id3|. |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Verify that the reference will not get updated since |child_id3| is in the |
| // fallback's FrameSinkId. |
| EXPECT_THAT(GetReferencesFrom(parent_id), UnorderedElementsAre(child_id4)); |
| } |
| |
| // This test verifies that once a frame sink become invalidated, it should |
| // immediately unblock all pending frames depending on that sink. |
| TEST_F(SurfaceSynchronizationTest, |
| InvalidatedFrameSinkShouldNotBlockActivation) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| |
| // The parent submitted a frame that refers to a future child surface. |
| EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid()); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), MakeCompositorFrame({child_id1}, {}, {})); |
| |
| // The frame is pending because the child frame hasn't be submitted yet. |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_FALSE(parent_support().last_activated_surface_id().is_valid()); |
| |
| // Killing the child sink should unblock the frame because it is known |
| // the dependency can never fulfill. |
| frame_sink_manager().InvalidateFrameSinkId(kChildFrameSink1); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_EQ(parent_id, parent_support().last_activated_surface_id()); |
| } |
| |
| TEST_F(SurfaceSynchronizationTest, EvictSurface) { |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| // Child-initiated synchronization event: |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| // Parent-initiated synchronizaton event: |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 2, 2); |
| |
| // Submit a CompositorFrame to |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Evict |child_id1|. It should get marked for destruction immediately. |
| child_support1().EvictSurface(child_id1.local_surface_id()); |
| EXPECT_TRUE(IsMarkedForDestruction(child_id1)); |
| |
| // Submit a CompositorFrame to |child_id2|. This CompositorFrame should be |
| // immediately rejected because |child_id2| has the same parent sequence |
| // number as |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_EQ(nullptr, GetSurfaceForId(child_id2)); |
| |
| // Submit a CompositorFrame to |child_id3|. It should not be accepted and not |
| // marked for destruction. |
| child_support1().SubmitCompositorFrame( |
| child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| ASSERT_NE(nullptr, GetSurfaceForId(child_id3)); |
| EXPECT_FALSE(IsMarkedForDestruction(child_id3)); |
| } |
| |
| // Tests that in cases where a pending-deletion surface (surface A) is |
| // activated during anothother surface (surface B)'s deletion, we don't attempt |
| // to delete surface A twice. |
| TEST_F(SurfaceSynchronizationTest, SurfaceActivationDuringDeletion) { |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| // Child-initiated synchronization event: |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
| |
| // Submit a CompositorFrame to |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // Child 1 should not yet be active. |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| |
| // Submit a CompositorFrame to |child_id2|. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame({arbitrary_id}, empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| |
| // Child 2 should not yet be active. |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| |
| // Evict |child_id2|. both surfaces should be marked for deletion. |
| child_support1().EvictSurface(child_id1.local_surface_id()); |
| EXPECT_TRUE(IsMarkedForDestruction(child_id1)); |
| EXPECT_TRUE(IsMarkedForDestruction(child_id2)); |
| |
| // Garbage collect to delete the above surfaces. This will activate |
| // |child_id2|, which will cause it to attempt re-deletion. |
| ExpireAllTemporaryReferencesAndGarbageCollect(); |
| |
| // Neither should still be marked for deletion. |
| EXPECT_FALSE(IsMarkedForDestruction(child_id1)); |
| EXPECT_FALSE(IsMarkedForDestruction(child_id2)); |
| } |
| |
| // This test verifies that if a surface is created with new embed token, a new |
| // allocation group is created, and once that surface is destroyed, the |
| // allocation group is destroyed as well. |
| TEST_F(SurfaceSynchronizationTest, |
| AllocationGroupCreationInitiatedBySubmitter) { |
| const SurfaceId surface_id = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| |
| // The allocation group should not exist yet. |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(surface_id)); |
| |
| // Submit a CompositorFrame to |child_id1|. |
| child_support1().SubmitCompositorFrame( |
| surface_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| |
| // The allocation group should now exist. |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(surface_id)); |
| |
| // Mark the surface for destruction. The allocation group should continue to |
| // exist because the surface won't be actually destroyed until garbage |
| // collection time. |
| child_support1().EvictSurface(surface_id.local_surface_id()); |
| EXPECT_TRUE(surface_manager()->GetSurfaceForId(surface_id)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(surface_id)); |
| |
| // Now garbage-collect. Both allocation group and the surface itself will be |
| // destroyed. |
| surface_manager()->GarbageCollectSurfaces(); |
| EXPECT_FALSE(surface_manager()->GetSurfaceForId(surface_id)); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(surface_id)); |
| } |
| |
| // This test verifies that if a surface references another surface that has an |
| // embed token that was never seen before, an allocation group will be created |
| // for the embedded surface. |
| TEST_F(SurfaceSynchronizationTest, AllocationGroupCreationInitiatedByEmbedder) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1, 1); |
| const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| |
| // The allocation group should not exist yet. |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id)); |
| |
| // Now submit a CompositorFrame that references |child_id|. An allocation |
| // group will be created for it. |
| CompositorFrame frame = |
| MakeCompositorFrame({}, {SurfaceRange(absl::nullopt, child_id)}, {}); |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(frame)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id)); |
| |
| // Now the parent unembeds the child surface. The allocation group for child |
| // surface should be marked for destruction. However, it won't get actually |
| // destroyed until garbage collection time. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| ASSERT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id)); |
| EXPECT_TRUE(surface_manager() |
| ->GetAllocationGroupForSurfaceId(child_id) |
| ->IsReadyToDestroy()); |
| EXPECT_TRUE(allocation_groups_need_garbage_collection()); |
| |
| // Now start garbage-collection. Note that no surface has been deleted |
| // recently, but the allocation group is ready to destroy and must be |
| // garbage-collected. |
| surface_manager()->GarbageCollectSurfaces(); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id)); |
| EXPECT_FALSE(allocation_groups_need_garbage_collection()); |
| } |
| |
| // This test verifies that if the parent embeds a SurfaceRange that has |
| // different embed tokens at start and end, then initially both allocation |
| // groups are created, but once the primary becomes available, the allocation |
| // group for the fallback gets destroyed. |
| TEST_F(SurfaceSynchronizationTest, |
| FallbackAllocationGroupDestroyedAfterPrimaryAvailable) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1, 1); |
| |
| // The allocation group should not exist yet. |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| |
| // Now submit a CompositorFrame that references |child_id2| as primary and |
| // |child_id1| as the fallback. An allocation group will be created for both |
| // of them. |
| CompositorFrame frame = |
| MakeCompositorFrame({}, {SurfaceRange(child_id1, child_id2)}, {}); |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(frame)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| |
| // Make |child_id2| available. The allocation group for |child_id1| should be |
| // marked for destruction. |
| EXPECT_FALSE(allocation_groups_need_garbage_collection()); |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| ASSERT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_TRUE(surface_manager() |
| ->GetAllocationGroupForSurfaceId(child_id1) |
| ->IsReadyToDestroy()); |
| ASSERT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| EXPECT_FALSE(surface_manager() |
| ->GetAllocationGroupForSurfaceId(child_id2) |
| ->IsReadyToDestroy()); |
| EXPECT_TRUE(allocation_groups_need_garbage_collection()); |
| |
| // Initiate garbage-collection. The allocation group for |child_id1| should be |
| // destroyed but the one for |child_id2| should stay alive. |
| surface_manager()->GarbageCollectSurfaces(); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| EXPECT_FALSE(allocation_groups_need_garbage_collection()); |
| } |
| |
| // This test verifies that if the parent embeds a SurfaceRange that has |
| // different embed tokens for primary and fallback and a surface already exists |
| // in the primary's allocation group, we don't create an allocation group for |
| // the fallback at all. |
| TEST_F(SurfaceSynchronizationTest, |
| FallbackAllocationGroupNotCreatedIfPrimaryAvailable) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1, 1); |
| |
| // The allocation group should not exist yet. |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| |
| // Make |child_id2| available. An allocation group should be created for it. |
| child_support2().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| |
| // Now submit a CompositorFrame that references |child_id2| as primary and |
| // |child_id1| as the fallback. An allocation group should not be created for |
| // the fallback because if the primary is available, we don't need the |
| // fallback. |
| CompositorFrame frame = |
| MakeCompositorFrame({}, {SurfaceRange(child_id1, child_id2)}, {}); |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(frame)); |
| EXPECT_FALSE(surface_manager()->GetAllocationGroupForSurfaceId(child_id1)); |
| EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2)); |
| } |
| |
| // Verifies that the value of last active surface is correct after embed token |
| // changes. https://crbug.com/967012 |
| TEST_F(SurfaceSynchronizationTest, |
| CheckLastActiveSurfaceAfterEmbedTokenChange) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 2); |
| const SurfaceId parent_id2( |
| kParentFrameSink, LocalSurfaceId(1, base::UnguessableToken::Create())); |
| parent_support().SubmitCompositorFrame( |
| parent_id1.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| Surface* parent_surface1 = GetSurfaceForId(parent_id1); |
| EXPECT_TRUE(parent_surface1->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface1->HasPendingFrame()); |
| parent_support().SubmitCompositorFrame( |
| parent_id2.local_surface_id(), |
| MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| Surface* parent_surface2 = GetSurfaceForId(parent_id2); |
| EXPECT_TRUE(parent_surface2->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface2->HasPendingFrame()); |
| |
| // Even though |parent_id1| has a larger sequence number, the value of |
| // |last_activated_surface_id| should be |parent_id2|. |
| EXPECT_EQ(parent_id2, parent_support().last_activated_surface_id()); |
| } |
| |
| // Regression test for https://crbug.com/1000868. Verify that the output of |
| // GetLatestInFlightSurface is correct when there is a conflict between the last |
| // surface in the group and the queried SurfaceRange. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceConflict) { |
| const SurfaceId id1 = MakeSurfaceId(kParentFrameSink, 1, 1); |
| const SurfaceId id2 = MakeSurfaceId(kParentFrameSink, 2, 2); |
| const SurfaceId id3 = MakeSurfaceId(kParentFrameSink, 1, 3); |
| |
| parent_support().SubmitCompositorFrame( |
| id1.local_surface_id(), MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| parent_support().SubmitCompositorFrame( |
| id2.local_surface_id(), MakeDefaultCompositorFrame(kBeginFrameSourceId)); |
| EXPECT_EQ(GetSurfaceForId(id1), |
| GetLatestInFlightSurface(SurfaceRange(absl::nullopt, id3))); |
| } |
| |
| // Check that if two different SurfaceIds with the same embed token are |
| // embedded, viz doesn't crash. https://crbug.com/1001143 |
| TEST_F(SurfaceSynchronizationTest, |
| DuplicateAllocationGroupInActivationDependencies) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child1_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child1_id2 = MakeSurfaceId(kChildFrameSink1, 2); |
| const SurfaceId child2_id1 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // Submit a CompositorFrame to |child1_id1| embedding |child2_id1|. |
| CompositorFrame child1_frame = |
| CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .SetActivationDependencies({child2_id1}) |
| .SetReferencedSurfaces({SurfaceRange(absl::nullopt, child2_id1)}) |
| .Build(); |
| child_support1().SubmitCompositorFrame(child1_id1.local_surface_id(), |
| std::move(child1_frame)); |
| |
| // Submit a CompositorFrame to |parent_id| embedding both |child1_id1| and |
| // |child1_id2|. |
| CompositorFrame parent_frame = |
| CompositorFrameBuilder() |
| .AddDefaultRenderPass() |
| .SetActivationDependencies({child1_id1, child1_id2}) |
| .SetReferencedSurfaces({SurfaceRange(absl::nullopt, child1_id1), |
| SurfaceRange(absl::nullopt, child1_id2)}) |
| .Build(); |
| // This shouldn't crash. |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| std::move(parent_frame)); |
| |
| // When multiple dependencies have the same embed token, only the first one |
| // should be taken into account. |
| EXPECT_EQ(1u, parent_surface()->activation_dependencies().size()); |
| EXPECT_EQ(child1_id1, *parent_surface()->activation_dependencies().begin()); |
| } |
| |
| // Tests that when a new CompositorFrame for an Embedded Surface arrives, and is |
| // not immediately ACKed, that when a CompositorFrame from its Embedder arrives |
| // with new ActivationDependencies, that the UnACKed frame receives and ACK so |
| // that that client can begin frame production to satistfy the new dependencies. |
| // (https://crbug.com/1203804) |
| TEST_F(SurfaceSynchronizationTest, |
| UnAckedSurfaceArrivesBeforeNewActivationDependencies) { |
| TestSurfaceIdAllocator parent_id(kParentFrameSink); |
| TestSurfaceIdAllocator child_id(kChildFrameSink1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>())); |
| // |parent_support| is blocked on |child_id|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| // |child_surface| should now be active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_FALSE(child_surface1()->HasUnackedActiveFrame()); |
| |
| // |parent_surface| should now be active. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // We start tracking that surfaces will damage the display. This will lead to |
| // frames not being immediately ACKed. |
| surface_observer().set_damage_display(true); |
| // Submit second frame at the same LocalSurfaceId. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| // |child_surface| should still have an active frame, which will be the newly |
| // submitted frame. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| // There should also be an un-acked frame, which was just submitted. |
| EXPECT_TRUE(child_surface1()->HasUnackedActiveFrame()); |
| |
| // Submit new |parent_surface|, with ActivationDependencies that are newer |
| // than the currently unACKed |child_surface|. |
| parent_id.Increment(); |
| child_id.Increment(); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>())); |
| // |parent_support| is blocked on |child_id2| the previous parent_surface |
| // should still be active. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id)); |
| EXPECT_FALSE(child_surface1()->HasUnackedActiveFrame()); |
| |
| // Submitting a new child frame for the newer dependencies should activate the |
| // parent frame. |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| } |
| |
| // Tests that when a CompositorFrame for an Embedded Surface arrives after its |
| // Embedder has submitted new ActivationDependencies, that it is immediately |
| // ACKed, even if normally it would not be due to damage. This way we don't have |
| // an Embedder blocked on an unACKed frame. (https://crbug.com/1203804) |
| TEST_P(OnBeginFrameAcksSurfaceSynchronizationTest, |
| UnAckedOldActivationDependencyArrivesAfterNewDependencies) { |
| TestSurfaceIdAllocator parent_id(kParentFrameSink); |
| TestSurfaceIdAllocator child_id(kChildFrameSink1); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>())); |
| // |parent_support| is blocked on |child_id|. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id)); |
| |
| child_support1().SubmitCompositorFrame( |
| child_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| // |child_surface| should now be active. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| EXPECT_FALSE(child_surface1()->HasUnackedActiveFrame()); |
| |
| // |parent_surface| should now be active. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| |
| // Submit new |parent_surface|, with ActivationDependencies that are newer |
| // than the currently |child_surface|. |
| parent_id.Increment(); |
| LocalSurfaceId old_child_id = child_id.local_surface_id(); |
| child_id.Increment(); |
| |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id}, {SurfaceRange(absl::nullopt, child_id)}, |
| std::vector<TransferableResource>())); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| // |parent_support| is blocked on |child_id2| the previous |parent_surface| |
| // should still be active. |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), |
| UnorderedElementsAre(child_id)); |
| // There should not be an unACKed |child_surface|. |
| EXPECT_FALSE(child_surface1()->HasUnackedActiveFrame()); |
| |
| // We start tracking that surfaces will damage the display. This will lead to |
| // frames not being immediately ACKed. |
| surface_observer().set_damage_display(true); |
| // Submitting a CompositorFrame to the old SurfaceId, which is no longer the |
| // dependency, should lead to an immediate ACK. |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)) |
| .Times(BeginFrameAcksEnabled() ? 0 : 1); |
| child_support1().SubmitCompositorFrame( |
| old_child_id, |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>())); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| // |child_surface| should still have an active frame. |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_THAT(child_surface1()->activation_dependencies(), IsEmpty()); |
| // Since we are blocking our embedder, we should have been ACKed to allow for |
| // frame production to begin on the new dependency. |
| EXPECT_FALSE(child_surface1()->HasUnackedActiveFrame()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| OnBeginFrameAcksSurfaceSynchronizationTest, |
| testing::Bool(), |
| [](auto& info) { |
| return info.param ? "BeginFrameAcks" |
| : "CompositoFrameAcks"; |
| }); |
| |
| } // namespace viz |