| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/containers/flat_set.h" |
| #include "base/test/simple_test_tick_clock.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/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 "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 bool kNeedsSyncPoints = true; |
| 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); |
| |
| std::vector<SurfaceId> empty_surface_ids() { |
| return std::vector<SurfaceId>(); |
| } |
| std::vector<SurfaceRange> empty_surface_ranges() { |
| return std::vector<SurfaceRange>(); |
| } |
| |
| SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, |
| uint32_t parent_sequence_number, |
| uint32_t child_sequence_number = 1u) { |
| return SurfaceId(frame_sink_id, |
| LocalSurfaceId(parent_sequence_number, child_sequence_number, |
| base::UnguessableToken::Deserialize(0, 1u))); |
| } |
| |
| 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)) |
| .SetReferencedSurfaces(std::move(referenced_surfaces)) |
| .SetTransferableResources(std::move(resource_list)) |
| .SetDeadline(deadline) |
| .Build(); |
| } |
| |
| } // namespace |
| |
| class SurfaceSynchronizationTest : public testing::Test { |
| public: |
| SurfaceSynchronizationTest() |
| : frame_sink_manager_(&shared_bitmap_manager_), |
| surface_observer_(false) {} |
| ~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, |
| kNeedsSyncPoints); |
| } |
| |
| 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_; } |
| |
| // 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); |
| } |
| |
| // Returns true if there is a Persistent reference for |surface_id|. |
| bool HasPersistentReference(const SurfaceId& surface_id) { |
| return frame_sink_manager().surface_manager()->HasPersistentReference( |
| 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, |
| kNeedsSyncPoints); |
| |
| supports_[kParentFrameSink] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kParentFrameSink, kIsChildRoot, |
| kNeedsSyncPoints); |
| |
| supports_[kChildFrameSink1] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kChildFrameSink1, kIsChildRoot, |
| kNeedsSyncPoints); |
| |
| supports_[kChildFrameSink2] = std::make_unique<CompositorFrameSinkSupport>( |
| &support_client_, &frame_sink_manager_, kChildFrameSink2, kIsChildRoot, |
| kNeedsSyncPoints); |
| |
| // 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); |
| } |
| |
| 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); |
| } |
| |
| 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_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SurfaceSynchronizationTest); |
| }; |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| 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()); |
| |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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()); |
| |
| 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(base::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()); |
| |
| // 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()); |
| |
| // Verify that the CompositorFrame has been activated. |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_THAT(parent_surface()->activation_dependencies(), IsEmpty()); |
| } |
| |
| // 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_F(SurfaceSynchronizationTest, 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(1); |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| 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(2); |
| child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| 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_F(SurfaceSynchronizationTest, 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 = 1337; |
| resource.format = ALPHA_8; |
| resource.filter = 1234; |
| 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()); |
| |
| std::vector<ReturnedResource> returned_resources = { |
| resource.ToReturnedResource()}; |
| EXPECT_CALL(support_client_, |
| DidReceiveCompositorFrameAck(returned_resources)); |
| |
| // 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()); |
| } |
| |
| // 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_F(SurfaceSynchronizationTest, 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(2); |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| 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()); |
| |
| // 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 resizes. |
| TEST_F(SurfaceSynchronizationTest, LimitLatencyInfo) { |
| const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); |
| const ui::LatencyComponentType latency_type1 = |
| ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT; |
| const ui::LatencyComponentType latency_type2 = |
| ui::LATENCY_BEGIN_FRAME_UI_MAIN_COMPONENT; |
| |
| // 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_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); |
| |
| builder.AddDefaultRenderPass(); |
| for (int i = 0; i < 60; ++i) |
| builder.AddLatencyInfo(info); |
| CompositorFrame frame2 = builder.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 no latency info objects because it grew |
| // too large. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->TakeLatencyInfo(&info_list); |
| EXPECT_EQ(0u, info_list.size()); |
| } |
| |
| // Checks whether the latency info are moved to the new surface from the old |
| // one when LocalSurfaceId changes. No frame has unresolved dependencies. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoCarriedOverOnResize_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_BEGIN_FRAME_UI_MAIN_COMPONENT; |
| |
| // 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->TakeLatencyInfo(&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 the latency info are moved to the new surface from the old |
| // one when LocalSurfaceId changes. Old surface has unresolved |
| // dependencies. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoCarriedOverOnResize_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_BEGIN_FRAME_UI_MAIN_COMPONENT; |
| |
| // Submit a frame with no unresolved dependecy. |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = MakeDefaultCompositorFrame(); |
| 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()); |
| |
| // 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 new surface has latency info from both active and pending |
| // frame of the old surface. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->TakeLatencyInfo(&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 the latency info are moved to the new surface from the old |
| // one when LocalSurfaceId changes. The new surface has unresolved |
| // dependencies. |
| TEST_F(SurfaceSynchronizationTest, |
| LatencyInfoCarriedOverOnResize_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_BEGIN_FRAME_UI_MAIN_COMPONENT; |
| |
| // Submit a frame with no unresolved dependencies. |
| ui::LatencyInfo info; |
| info.AddLatencyNumber(latency_type1); |
| |
| CompositorFrame frame = MakeDefaultCompositorFrame(); |
| 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()); |
| EXPECT_FALSE(surface->HasPendingFrame()); |
| EXPECT_TRUE(surface->HasActiveFrame()); |
| |
| // Both latency info elements must exist in the now-activated frame of the |
| // new surface. |
| std::vector<ui::LatencyInfo> info_list; |
| surface->TakeLatencyInfo(&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 that resources and ack are sent together if possible. |
| TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| TransferableResource resource; |
| resource.id = 1234; |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource})); |
| std::vector<ReturnedResource> returned_resources = |
| TransferableResource::ReturnResources({resource}); |
| EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); |
| EXPECT_CALL(support_client_, |
| DidReceiveCompositorFrameAck(Eq(returned_resources))); |
| parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| } |
| |
| // Verifies that arrival of a new CompositorFrame doesn't change the fact that a |
| // surface is marked for destruction. |
| TEST_F(SurfaceSynchronizationTest, 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 = 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(_)); |
| surface_observer().Reset(); |
| child_support1().SubmitCompositorFrame(child_id.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| 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()); |
| |
| { |
| std::vector<ReturnedResource> returned_resources = |
| TransferableResource::ReturnResources({resource}); |
| EXPECT_CALL(support_client_, ReclaimResources(Eq(returned_resources))); |
| 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()); |
| 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()); |
| |
| // 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()); |
| 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()); |
| } |
| |
| // 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()); |
| |
| 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()); |
| |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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 rejected and ACK'ed |
| // immediately. |
| TEST_F(SurfaceSynchronizationTest, 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); |
| |
| // child_support1 submits a CompositorFrame without any dependencies. |
| // DidReceiveCompositorFrameAck should call on immediate activation. |
| // However, resources will not be returned because this frame is a candidate |
| // for display. |
| TransferableResource resource; |
| resource.id = 1337; |
| resource.format = ALPHA_8; |
| resource.filter = 1234; |
| resource.size = gfx::Size(1234, 5678); |
| std::vector<ReturnedResource> returned_resources = |
| TransferableResource::ReturnResources({resource}); |
| |
| EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck( |
| Eq(std::vector<ReturnedResource>()))); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource}, MakeDefaultDeadline())); |
| EXPECT_FALSE(child_surface1()->has_deadline()); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // The parent is blocked on |child_id2| and references |child_id1|. The |
| // surface corresponding to |child_id1| will not accept new CompositorFrames |
| // while the parent CompositorFrame is blocked. |
| 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()); |
| |
| // Resources will be returned immediately because |child_id1|'s surface is |
| // closed. |
| TransferableResource resource2; |
| resource2.id = 1246; |
| resource2.format = ALPHA_8; |
| resource2.filter = 1357; |
| resource2.size = gfx::Size(8765, 4321); |
| std::vector<ReturnedResource> returned_resources2 = |
| TransferableResource::ReturnResources({resource2}); |
| EXPECT_CALL(support_client_, |
| DidReceiveCompositorFrameAck(Eq(returned_resources2))); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource2}, MakeDefaultDeadline())); |
| testing::Mock::VerifyAndClearExpectations(&support_client_); |
| |
| // Advance BeginFrames to trigger a deadline. This activates the |
| // CompositorFrame submitted to the parent. |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| EXPECT_TRUE(parent_surface()->has_deadline()); |
| } |
| SendNextBeginFrame(); |
| EXPECT_FALSE(parent_surface()->has_deadline()); |
| EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| |
| // Resources will be returned immediately because |child_id1|'s surface is |
| // closed forever. |
| EXPECT_CALL(support_client_, |
| DidReceiveCompositorFrameAck(Eq(returned_resources2))); |
| child_support1().SubmitCompositorFrame( |
| child_id1.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| {resource2}, MakeDefaultDeadline())); |
| 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()); |
| EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
| |
| child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| 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) surface. |
| TEST_F(SurfaceSynchronizationTest, DeadlineInheritance) { |
| 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}, empty_surface_ranges(), |
| 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 all surfaces within a dependency chain will |
| // ultimately inherit the same 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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}, empty_surface_ranges(), |
| 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()); |
| |
| 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); |
| |
| Surface* parent_surface = GetSurfaceForId(parent_id); |
| ASSERT_NE(nullptr, parent_surface); |
| |
| EXPECT_TRUE(parent_surface->has_deadline()); |
| EXPECT_TRUE(parent_surface->HasActiveFrame()); |
| EXPECT_TRUE(parent_surface->HasPendingFrame()); |
| |
| // Advance BeginFrames to trigger a deadline. This activates the |
| // CompositorFrame submitted above. |
| for (int i = 0; i < 4; ++i) |
| SendNextBeginFrame(); |
| |
| // The parent surface stays alive through the display. |
| parent_surface = GetSurfaceForId(parent_id); |
| EXPECT_NE(nullptr, parent_surface); |
| EXPECT_TRUE(surface_observer().IsSurfaceDamaged(parent_id)); |
| |
| // Submitting a new CompositorFrame to the display should free the parent. |
| display_support().SubmitCompositorFrame(display_id.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| |
| 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(base::nullopt); |
| 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(base::nullopt); |
| 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()); |
| child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
| EXPECT_EQ(3u, 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()); |
| |
| 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()); |
| |
| // 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()); |
| |
| // Verify that there is a temporary reference for child_id3. |
| EXPECT_TRUE(HasTemporaryReference(child_id3)); |
| |
| // The surface corresponding to |child_id3| will not be activated until |
| // a parent embeds it because it's a child initiated synchronization. |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, |
| std::vector<TransferableResource>())); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id3), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| 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 |
| // if it has a bogus fallback SurfaceID. |
| TEST_F(SurfaceSynchronizationTest, LatestInFlightSurfaceWithBogusFallback) { |
| 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()); |
| |
| 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); |
| |
| // If primary exists and active return it regardless of the fallback. |
| EXPECT_EQ(GetSurfaceForId(child_id1), |
| GetLatestInFlightSurface(SurfaceRange(bogus_child_id, child_id1))); |
| |
| // If primary is not active and fallback is doesn't exist, we always 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()); |
| // 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(base::nullopt, child_id2))); |
| |
| // Activate |child_id2| |
| child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| // 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(base::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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| |
| 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()); |
| |
| // |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()); |
| |
| // 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()); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id3))); |
| |
| child_support1().SubmitCompositorFrame(child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| |
| // 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()); |
| |
| // |child_id1| now should have a temporary reference. |
| EXPECT_TRUE(HasTemporaryReference(child_id1)); |
| EXPECT_FALSE(HasPersistentReference(child_id1)); |
| |
| // Activate |child_id2|. |
| child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| |
| // |child_id2| now should have a temporary reference. |
| EXPECT_TRUE(HasTemporaryReference(child_id2)); |
| EXPECT_FALSE(HasPersistentReference(child_id2)); |
| |
| // 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_FALSE(HasPersistentReference(child_id1)); |
| |
| // |child_id2| has a persistent references now. |
| EXPECT_FALSE(HasTemporaryReference(child_id2)); |
| EXPECT_TRUE(HasPersistentReference(child_id2)); |
| |
| // 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::Deserialize(0, 1)); |
| const base::UnguessableToken nonce2( |
| base::UnguessableToken::Deserialize(1, 1)); |
| const base::UnguessableToken nonce3( |
| base::UnguessableToken::Deserialize(2, 1)); |
| 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()); |
| |
| // 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()); |
| |
| EXPECT_EQ(GetSurfaceForId(child_id2), |
| GetLatestInFlightSurface(SurfaceRange(child_id1, child_id4))); |
| |
| child_support1().SubmitCompositorFrame(child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| |
| // 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. |
| TEST_F(SurfaceSynchronizationTest, DropDependenciesThatWillNeverArrive) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id11 = MakeSurfaceId(kChildFrameSink1, 1); |
| const SurfaceId child_id12 = MakeSurfaceId(kChildFrameSink1, 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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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()); |
| } |
| |
| // 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, SurfaceReferencesChangeOnChildActivation) { |
| 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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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()); |
| |
| // 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()); |
| } |
| |
| // If a parent CompositorFrame embeds a child Surface newer than the throttled |
| // child then the throttled child surface is immediately unthrottled. This |
| // unblocks the child to make progress to catch up with the parent. |
| TEST_F(SurfaceSynchronizationTest, |
| ParentEmbeddingFutureChildUnblocksCurrentChildSurface) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| |
| // |child_id2| Surface should not activate because |child_id1| was never |
| // added as a dependency by a parent. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDeadline(1u))); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_TRUE(child_surface2->has_deadline()); |
| |
| FrameDeadline deadline = MakeDefaultDeadline(); |
| base::TimeTicks deadline_wall_time = deadline.ToWallTime(); |
| EXPECT_EQ(deadline_wall_time, child_surface2->deadline_for_testing()); |
| |
| // The parent finally embeds a child surface that hasn't arrived which |
| // activates |child_id2|'s Surface in order for the child to make forward |
| // progress. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| } |
| |
| // A child surface can be blocked on its own activation dependencies and on a |
| // parent embedding it as an activation dependency. Even if a child's activation |
| // dependencies arrive, it may not activate until it is embedded. |
| TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| |
| // |child_id2| Surface should not activate because |child_id1| was never |
| // added as a dependency by a parent AND it depends on |child_id3| which has |
| // not yet arrived. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_THAT(child_surface2->activation_dependencies(), |
| UnorderedElementsAre(child_id3)); |
| |
| // |child_id2|'s dependency has arrived but |child_id2| Surface has not |
| // activated because it is still throttled by its parent. It will not |
| // activate until its parent arrives. |
| child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| |
| // The parent finally embeds a |child_id2| which activates |child_id2|'s |
| // Surface. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| } |
| |
| // Similar to the previous test, a child surface can be blocked on its own |
| // activation dependencies and on a parent embedding it as an activation |
| // dependency. In this case, the parent CompositorFrame arrives first, and then |
| // the activation dependency. |
| TEST_F(SurfaceSynchronizationTest, ChildBlockedOnDependenciesAndParent2) { |
| const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink2, 1); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| |
| // |child_id2| Surface should not activate because |child_id1| was never |
| // added as a dependency by a parent AND it depends on |child_id3| which has |
| // not yet arrived. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame({child_id3}, {SurfaceRange(base::nullopt, child_id3)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_THAT(child_surface2->activation_dependencies(), |
| UnorderedElementsAre(child_id3)); |
| EXPECT_FALSE(child_surface2->HasDependentFrame()); |
| |
| // The parent embeds |child_id2| but |child_id2|'s Surface cannot activate |
| // until its dependencies arrive. |
| parent_support().SubmitCompositorFrame( |
| parent_id.local_surface_id(), |
| MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)}, |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_THAT(child_surface2->activation_dependencies(), |
| UnorderedElementsAre(child_id3)); |
| EXPECT_TRUE(child_surface2->HasDependentFrame()); |
| |
| // |child_id2|'s dependency has arrived and |child_id2|'s Surface finally |
| // activates. |
| child_support2().SubmitCompositorFrame(child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| EXPECT_THAT(child_surface2->activation_dependencies(), IsEmpty()); |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| } |
| |
| // This test verifies that if a child-initiated synchronization is blocked |
| // on a parent, then the frame will still activate after a deadline passes. |
| TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentActivatesAfterDeadline) { |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1->HasDependentFrame()); |
| EXPECT_FALSE(child_surface1->has_deadline()); |
| |
| // |child_id2| Surface should not activate because |child_id1| was never |
| // added as a dependency by a parent. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>(), |
| MakeDefaultDeadline())); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_FALSE(child_surface2->HasDependentFrame()); |
| EXPECT_TRUE(child_surface2->has_deadline()); |
| |
| for (int i = 0; i < 3; ++i) { |
| SendNextBeginFrame(); |
| // There is still a looming deadline! Eeek! |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_FALSE(child_surface2->HasDependentFrame()); |
| EXPECT_TRUE(child_surface2->has_deadline()); |
| } |
| |
| SendNextBeginFrame(); |
| |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| // We pretend this is true so that subsequent CompositorFrames to the same |
| // surface do not block on the parent again. |
| EXPECT_TRUE(child_surface2->HasDependentFrame()); |
| EXPECT_FALSE(child_surface2->has_deadline()); |
| } |
| |
| // This test verifies that if a child-initiated synchronization is initiated |
| // with a deadline in the past, then the frame will immediately activate and |
| // be marked as having a dependent frame so that it does not block again in |
| // the future. |
| TEST_F(SurfaceSynchronizationTest, ChildBlockedOnParentDeadlineInPast) { |
| const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1); |
| const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| EXPECT_FALSE(child_surface1->HasDependentFrame()); |
| EXPECT_FALSE(child_surface1->has_deadline()); |
| |
| // Pick a deadline in the near future. |
| FrameDeadline deadline = MakeDeadline(1u); |
| |
| // Advance time beyond the |deadline| above. |
| SendLateBeginFrame(); |
| |
| // |child_id2| Surface should activate because it was submitted with a |
| // deadline in the past. |
| child_support1().SubmitCompositorFrame( |
| child_id2.local_surface_id(), |
| MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(), |
| std::vector<TransferableResource>(), deadline)); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| EXPECT_FALSE(child_surface2->has_deadline()); |
| |
| EXPECT_TRUE(child_surface2->HasDependentFrame()); |
| } |
| |
| // A child may submit CompositorFrame corresponding to a child-initiated |
| // synchronization event followed by a CompositorFrame corresponding to a |
| // parent-initiated synchronization event. |
| TEST_F(SurfaceSynchronizationTest, |
| ParentInitiatedAfterChildInitiatedSynchronization) { |
| 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); |
| |
| // |child_id1| Surface should immediately activate. |
| child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface1 = GetSurfaceForId(child_id1); |
| ASSERT_NE(nullptr, child_surface1); |
| EXPECT_FALSE(child_surface1->HasPendingFrame()); |
| EXPECT_TRUE(child_surface1->HasActiveFrame()); |
| |
| // |child_id2| Surface should not activate because |child_id1| was never |
| // added as a dependency by a parent. |
| child_support1().SubmitCompositorFrame(child_id2.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface2 = GetSurfaceForId(child_id2); |
| ASSERT_NE(nullptr, child_surface2); |
| EXPECT_TRUE(child_surface2->HasPendingFrame()); |
| EXPECT_FALSE(child_surface2->HasActiveFrame()); |
| EXPECT_TRUE(child_surface2->has_deadline()); |
| |
| // |child_id3| Surface should activate immediately because it corresponds to a |
| // parent-initiated synchronization event. |child_surface3| activating |
| // triggers all predecessors to activate as well if they're blocked on a |
| // parent. |
| child_support1().SubmitCompositorFrame(child_id3.local_surface_id(), |
| MakeDefaultCompositorFrame()); |
| Surface* child_surface3 = GetSurfaceForId(child_id3); |
| ASSERT_NE(nullptr, child_surface3); |
| EXPECT_FALSE(child_surface3->HasPendingFrame()); |
| EXPECT_TRUE(child_surface3->HasActiveFrame()); |
| EXPECT_FALSE(IsMarkedForDestruction(child_id3)); |
| |
| // |child_surface2| should have activated now (and should be a candidate for |
| // garbage collection). |
| EXPECT_FALSE(child_surface2->HasPendingFrame()); |
| EXPECT_TRUE(child_surface2->HasActiveFrame()); |
| EXPECT_TRUE(IsMarkedForDestruction(child_id2)); |
| } |
| |
| 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()); |
| |
| // 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()); |
| 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()); |
| ASSERT_NE(nullptr, GetSurfaceForId(child_id3)); |
| EXPECT_FALSE(IsMarkedForDestruction(child_id3)); |
| } |
| |
| } // namespace viz |