Index: components/viz/hittest/hittest_aggregator_unittest.cc |
diff --git a/components/viz/hittest/hittest_aggregator_unittest.cc b/components/viz/hittest/hittest_aggregator_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..13257f063534336639d0bf47ff8f220ea393ceb1 |
--- /dev/null |
+++ b/components/viz/hittest/hittest_aggregator_unittest.cc |
@@ -0,0 +1,1455 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
rjkroege
2017/06/02 22:45:45
removed the commented out code?
gklassen
2017/06/05 21:32:14
Done.
|
+// 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 "cc/surfaces/compositor_frame_sink_support.h" |
+#include "cc/surfaces/surface_manager.h" |
+#include "cc/test/begin_frame_args_test.h" |
+#include "cc/test/compositor_frame_helpers.h" |
+#include "cc/test/fake_external_begin_frame_source.h" |
+#include "cc/test/fake_surface_observer.h" |
+#include "cc/test/mock_compositor_frame_sink_support_client.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+*/ |
+ |
+#include "cc/surfaces/frame_sink_id.h" |
+#include "cc/surfaces/local_surface_id.h" |
+#include "cc/surfaces/surface_id.h" |
+#include "hittest_aggregator.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace viz { |
+namespace test { |
+ |
+namespace { |
+ |
+/* |
+constexpr bool kIsRoot = true; |
+constexpr bool kIsChildRoot = false; |
+constexpr bool kHandlesFrameSinkIdInvalidation = true; |
+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>(); |
+} |
+*/ |
+constexpr cc::FrameSinkId kDisplayFrameSink(2, 0); |
+// constexpr gfx::Transform identityTransform(); |
+ |
+cc::SurfaceId MakeSurfaceId(const cc::FrameSinkId& frame_sink_id, |
+ uint32_t local_id) { |
+ return cc::SurfaceId( |
+ frame_sink_id, |
+ cc::LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); |
+} |
+ |
+} // namespace |
+ |
+using namespace hittest::mojom; |
+ |
+class HittestAggregatorTest : public testing::Test { |
+ public: |
+ HittestAggregatorTest() {} |
+ ~HittestAggregatorTest() override {} |
+ |
+ void SetUp() override {} |
+ |
+ void TearDown() override {} |
+ |
+ HittestAggregator aggregator_; |
+ |
+ int count() { |
+ HittestAggregator::Element* end = aggregator_.current_regions_; |
+ while (end->child_count_ != LAST_REGION) { |
+ end++; |
+ } |
+ return end - aggregator_.current_regions_; |
+ } |
+ |
+ HittestRegion RegionAtIndex(int i) { |
+ return aggregator_.current_regions_[i].region_; |
+ } |
+}; |
+ |
+TEST_F(HittestAggregatorTest, HittestAggregation) { |
+ EXPECT_TRUE(true); |
+} |
+ |
+TEST(HittestAggregatorTestNoFixture, HittestDataValidation) { |
+ EXPECT_TRUE(false); |
+} |
+ |
+// tests brainstorm |
+// A. Validation |
+// - SurfaceId is valid and exists |
rjkroege
2017/06/02 22:45:45
let cc worry about it. You can assume in your obse
gklassen
2017/06/05 21:32:14
Acknowledged.
|
+// - Rect is within the display |
+// - flags is one of available values? |
+// B. Aggregation |
+// - happy paths: |
+// - missing surface parent |
+// - cyclic surfaces ( possible? ) |
rjkroege
2017/06/02 22:45:46
probably can't/won't happen
gklassen
2017/06/05 21:32:14
Acknowledged.
|
+// C. SurfaceFinding |
+// - |
+ |
+TEST_F(HittestAggregatorTest, SimplestHappyPath) { |
rjkroege
2017/06/02 22:45:46
A happy path is like a happy meal for the input ta
|
+ // one hittest_data with no sub-regions gets all events |
rjkroege
2017/06/02 22:45:45
Ascii art would be simply spiffy here.
gklassen
2017/06/05 21:32:14
Acknowledged.
|
+ |
+ EXPECT_TRUE(count() == 0); |
+ |
+ cc::SurfaceId display_surface_id = MakeSurfaceId(kDisplayFrameSink, 1); |
+ |
+ auto hittest_data = HittestData::New(); |
rjkroege
2017/06/02 22:45:46
New is an unfortunate choice of method name. Given
gklassen
2017/06/05 21:32:14
perhaps, but it's the name given by the auto-gen c
|
+ hittest_data->surface_id_ = display_surface_id; |
+ hittest_data->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_NONE; |
+ hittest_data->rect_.SetRect(0, 0, 1024, 768); |
+ |
+ aggregator_.SubmitHittestData(std::move(hittest_data)); |
+ |
+ EXPECT_TRUE(count() == 0); |
+ |
+ aggregator_.Aggregate(display_surface_id); |
+ |
+ // there should now be only one region |
+ EXPECT_TRUE(count() == 1); |
+ |
+ HittestRegion region = RegionAtIndex(0); |
+ EXPECT_TRUE(region.rect_ == gfx::Rect(0, 0, 1027, 768)); |
+ EXPECT_TRUE(region.flags_ == HittestRegionFlags::HITTEST_NONE); |
+} |
+ |
+TEST_F(HittestAggregatorTest, HittestDataValidation) { |
+ auto hittest_data = hittest::mojom::HittestData::New(); |
+ hittest_data->surface_id_ = MakeSurfaceId(kDisplayFrameSink, 1); |
+ hittest_data->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_NONE; |
+ hittest_data->rect_.SetRect(0, 0, 10, 10); |
+ |
+ auto hittest_region = hittest::mojom::HittestRegion::New(); |
+ hittest_region->surface_id_ = MakeSurfaceId(kDisplayFrameSink, 2); |
+ hittest_region->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_ASK; |
+ hittest_region->rect_.SetRect(0, 0, 10, 10); |
+ |
+ // todo: confirm that transform inits to Identity |
+ // DCHECK( hittest_region.transform_ ) |
+ |
+ hittest_data->regions_.push_back(std::move(hittest_region)); |
+ |
+ aggregator_.SubmitHittestData(std::move(hittest_data)); |
+} |
+ |
+/* |
rjkroege
2017/06/02 22:45:46
this looks copied from elsewhere. not reviewing ye
gklassen
2017/06/05 21:32:14
Acknowledged.
|
+ |
+class SurfaceSynchronizationTest : public testing::Test { |
+ public: |
+ SurfaceSynchronizationTest() |
+ : surface_manager_(SurfaceManager::LifetimeType::REFERENCES), |
+ surface_observer_(false) {} |
+ ~SurfaceSynchronizationTest() override {} |
+ |
+ CompositorFrameSinkSupport& display_support() { return *supports_[0]; } |
+ Surface* display_surface() { |
+ return display_support().current_surface_for_testing(); |
+ } |
+ |
+ CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } |
+ Surface* parent_surface() { |
+ return parent_support().current_surface_for_testing(); |
+ } |
+ const ReferencedSurfaceTracker& parent_reference_tracker() { |
+ return parent_support().ReferenceTrackerForTesting(); |
+ } |
+ |
+ CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } |
+ Surface* child_surface1() { |
+ return child_support1().current_surface_for_testing(); |
+ } |
+ |
+ CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } |
+ Surface* child_surface2() { |
+ return child_support2().current_surface_for_testing(); |
+ } |
+ |
+ CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } |
+ Surface* surface(int index) { |
+ return support(index).current_surface_for_testing(); |
+ } |
+ |
+ SurfaceManager& surface_manager() { return surface_manager_; } |
+ |
+ // Returns all the references where |surface_id| is the parent. |
+ const base::flat_set<SurfaceId>& GetChildReferences( |
+ const SurfaceId& surface_id) { |
+ return surface_manager().parent_to_child_refs_[surface_id]; |
+ } |
+ |
+ // Returns true if there is a temporary reference for |surface_id|. |
+ bool HasTemporaryReference(const SurfaceId& surface_id) { |
+ return surface_manager().HasTemporaryReference(surface_id); |
+ } |
+ |
+ SurfaceDependencyTracker& dependency_tracker() { |
+ return *surface_manager_.dependency_tracker(); |
+ } |
+ |
+ FakeExternalBeginFrameSource* begin_frame_source() { |
+ return begin_frame_source_.get(); |
+ } |
+ |
+ FakeSurfaceObserver& surface_observer() { return surface_observer_; } |
+ |
+ // testing::Test: |
+ void SetUp() override { |
+ testing::Test::SetUp(); |
+ |
+ begin_frame_source_ = |
+ base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); |
+ dependency_tracker_ = base::MakeUnique<SurfaceDependencyTracker>( |
+ &surface_manager_, begin_frame_source_.get()); |
+ surface_manager_.SetDependencyTracker(dependency_tracker_.get()); |
+ surface_manager_.AddObserver(&surface_observer_); |
+ supports_.push_back(CompositorFrameSinkSupport::Create( |
+ &support_client_, &surface_manager_, kDisplayFrameSink, kIsRoot, |
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); |
+ supports_.push_back(CompositorFrameSinkSupport::Create( |
+ &support_client_, &surface_manager_, kParentFrameSink, kIsChildRoot, |
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); |
+ supports_.push_back(CompositorFrameSinkSupport::Create( |
+ &support_client_, &surface_manager_, kChildFrameSink1, kIsChildRoot, |
+ kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); |
+ supports_.push_back(CompositorFrameSinkSupport::Create( |
+ &support_client_, &surface_manager_, kChildFrameSink2, kIsChildRoot, |
+ kHandlesFrameSinkIdInvalidation, 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. |
+ surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), |
+ kDisplayFrameSink); |
+ } |
+ |
+ void TearDown() override { |
+ surface_manager_.RemoveObserver(&surface_observer_); |
+ surface_manager_.SetDependencyTracker(nullptr); |
+ surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); |
+ |
+ dependency_tracker_.reset(); |
+ |
+ // SurfaceDependencyTracker depends on this BeginFrameSource and so it must |
+ // be destroyed AFTER the dependency tracker is destroyed. |
+ begin_frame_source_.reset(); |
+ |
+ supports_.clear(); |
+ |
+ surface_observer_.Reset(); |
+ } |
+ |
+ protected: |
+ testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; |
+ |
+ private: |
+ SurfaceManager surface_manager_; |
+ FakeSurfaceObserver surface_observer_; |
+ std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; |
+ std::unique_ptr<SurfaceDependencyTracker> dependency_tracker_; |
+ std::vector<std::unique_ptr<CompositorFrameSinkSupport>> 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(), |
+ MakeCompositorFrame()); |
+ |
+ // 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(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(), |
+ MakeCompositorFrame()); |
+ |
+ // 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(surface_manager().GetRootSurfaceId()), |
+ UnorderedElementsAre(display_id_second)); |
+ |
+ // Surface |display_id_first| is unreachable and should get deleted. |
+ EXPECT_EQ(nullptr, surface_manager().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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // parent_support is blocked on |child_id1| and |child_id2|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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(), |
+ MakeCompositorFrame()); |
+ |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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(), |
+ MakeCompositorFrame()); |
+ |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // parent_support is blocked on |child_id1|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // child_support1 should now be blocked on |child_id2|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
+ EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), |
+ 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()->blocking_surfaces(), |
+ 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ |
+ // child_surface1 should now be active. |
+ EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
+ EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); |
+ |
+ // parent_surface should now be active. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // parent_support is blocked on |child_id2|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
+ EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id2)); |
+ |
+ // The parent should still be blocked on |child_id2|. |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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(), |
+ MakeCompositorFrame()); |
+ |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ |
+ // child_surface1 should now be active. |
+ EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
+ EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); |
+ |
+ // parent_surface should now be active. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // parent_support is blocked on |child_id1|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id1)); |
+ |
+ child_support1().SubmitCompositorFrame( |
+ child_id1.local_surface_id(), |
+ MakeCompositorFrame({child_id2}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ // child_support1 should now be blocked on |child_id2|. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
+ EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id2)); |
+ |
+ // The parent should still be blocked on |child_id1| because it's pending. |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id1)); |
+ |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ |
+ for (int i = 0; i < 3; ++i) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ // There is still a looming deadline! Eeek! |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ |
+ // parent_support is still blocked on |child_id1|. |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id2)); |
+ } |
+ |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ |
+ // The deadline has passed. |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ |
+ // parent_surface has been activated. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); |
+ |
+ // child_surface1 has been activated. |
+ EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
+ EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); |
+} |
+ |
+// Verifies that the deadline does not reset if we submit CompositorFrames |
+// to new Surfaces with unresolved dependencies. |
+TEST_F(SurfaceSynchronizationTest, FramesSubmittedAfterDeadlineSet) { |
+ const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ for (int i = 0; i < 3; ++i) { |
+ LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); |
+ support(i).SubmitCompositorFrame( |
+ local_surface_id, |
+ MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ // The deadline has been set. |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ |
+ // support(i) should be blocked on arbitrary_id. |
+ EXPECT_FALSE(surface(i)->HasActiveFrame()); |
+ EXPECT_TRUE(surface(i)->HasPendingFrame()); |
+ EXPECT_THAT(surface(i)->blocking_surfaces(), |
+ UnorderedElementsAre(arbitrary_id)); |
+ |
+ // Issue a BeginFrame to get closer to the deadline. |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ } |
+ |
+ // The deadline hits and all the Surfaces should activate. |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ for (int i = 0; i < 3; ++i) { |
+ EXPECT_TRUE(surface(i)->HasActiveFrame()); |
+ EXPECT_FALSE(surface(i)->HasPendingFrame()); |
+ EXPECT_THAT(surface(i)->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // Verify that the CompositorFrame is blocked on |arbitrary_id|. |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ UnorderedElementsAre(arbitrary_id)); |
+ |
+ // Submit a CompositorFrame that has no dependencies. |
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ // Verify that the CompositorFrame has been activated. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), 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(), |
+ MakeCompositorFrame()); |
+ 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()->blocking_surfaces(), 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}, {child_id1}, |
+ TransferableResourceArray())); |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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(), |
+ MakeCompositorFrame()); |
+ 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()->blocking_surfaces(), 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()->blocking_surfaces(), 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); |
+ TransferableResourceArray resource_list = {resource}; |
+ parent_support().SubmitCompositorFrame( |
+ parent_id.local_surface_id(), |
+ MakeCompositorFrame({child_id}, empty_surface_ids(), 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()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id)); |
+ |
+ child_support1().SubmitCompositorFrame( |
+ child_id.local_surface_id(), |
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ // Verify that the child CompositorFrame activates immediately. |
+ EXPECT_TRUE(child_surface1()->HasActiveFrame()); |
+ EXPECT_FALSE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); |
+ |
+ // Verify that the parent has activated. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); |
+ |
+ ReturnedResourceArray 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_ids()}, |
+ TransferableResourceArray())); |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); |
+} |
+ |
+// The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. |
+// child_support1 evicts its blocked Surface. The parent surface should |
+// activate. |
+TEST_F(SurfaceSynchronizationTest, EvictSurfaceWithPendingFrame) { |
+ const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); |
+ const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); |
+ const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); |
+ |
+ // Submit a CompositorFrame that depends on |child_id1|. |
+ parent_support().SubmitCompositorFrame( |
+ parent_id1.local_surface_id(), |
+ MakeCompositorFrame({child_id1}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ // Verify that the CompositorFrame is blocked on |child_id1|. |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id1)); |
+ |
+ // Submit a CompositorFrame that depends on |child_id2|. |
+ child_support1().SubmitCompositorFrame( |
+ child_id1.local_surface_id(), |
+ MakeCompositorFrame({child_id2}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ // Verify that the CompositorFrame is blocked on |child_id2|. |
+ EXPECT_FALSE(child_surface1()->HasActiveFrame()); |
+ EXPECT_TRUE(child_surface1()->HasPendingFrame()); |
+ EXPECT_THAT(child_surface1()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id2)); |
+ |
+ // Evict child_support1's current Surface. |
+ // TODO(fsamuel): EvictCurrentSurface => EvictCurrentSurface. |
+ child_support1().EvictCurrentSurface(); |
+ |
+ // The parent Surface should immediately activate. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+} |
+ |
+// 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // Verify that the CompositorFrame is blocked on |child_id|. |
+ EXPECT_FALSE(parent_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), |
+ 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(), |
+ MakeCompositorFrame()); |
+ 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()->blocking_surfaces(), IsEmpty()); |
+ |
+ // Verify that the parent Surface has activated. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); |
+ |
+ // Submit a new parent CompositorFrame to add a reference. |
+ parent_support().SubmitCompositorFrame( |
+ parent_id.local_surface_id(), |
+ MakeCompositorFrame(empty_surface_ids(), {child_id1}, |
+ TransferableResourceArray())); |
+ |
+ // Verify that the parent Surface has activated. |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_THAT(parent_surface()->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ 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()->blocking_surfaces(), |
+ UnorderedElementsAre(child_id2)); |
+ EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); |
+ |
+ child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ // 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()->blocking_surfaces(), 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()); |
+} |
+ |
+// 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::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; |
+ const int64_t latency_id1 = 234; |
+ const int64_t latency_sequence_number1 = 5645432; |
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; |
+ const int64_t latency_id2 = 31434351; |
+ const int64_t latency_sequence_number2 = 663788; |
+ |
+ // Submit a frame with latency info |
+ ui::LatencyInfo info; |
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); |
+ |
+ CompositorFrame frame = MakeCompositorFrame(); |
+ 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 and no pending frame. |
+ Surface* old_surface = surface_manager().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, latency_id2, latency_sequence_number2); |
+ |
+ CompositorFrame frame2 = MakeCompositorFrame(); |
+ frame2.metadata.latency_info.push_back(info2); |
+ |
+ 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 = surface_manager().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()); |
+ |
+ ui::LatencyInfo::LatencyComponent comp1; |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); |
+ EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, 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::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; |
+ const int64_t latency_id1 = 234; |
+ const int64_t latency_sequence_number1 = 5645432; |
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; |
+ const int64_t latency_id2 = 31434351; |
+ const int64_t latency_sequence_number2 = 663788; |
+ |
+ // Submit a frame with no unresolved dependecy. |
+ ui::LatencyInfo info; |
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); |
+ |
+ CompositorFrame frame = MakeCompositorFrame(); |
+ 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, latency_id2, latency_sequence_number2); |
+ |
+ CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), |
+ TransferableResourceArray()); |
+ 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 = surface_manager().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(), |
+ MakeCompositorFrame()); |
+ |
+ // Verify that the new surface has an active frame only. |
+ Surface* surface = surface_manager().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()); |
+ |
+ ui::LatencyInfo::LatencyComponent comp1; |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); |
+ EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, 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::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; |
+ const int64_t latency_id1 = 234; |
+ const int64_t latency_sequence_number1 = 5645432; |
+ const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; |
+ const int64_t latency_id2 = 31434351; |
+ const int64_t latency_sequence_number2 = 663788; |
+ |
+ // Submit a frame with no unresolved dependencies. |
+ ui::LatencyInfo info; |
+ info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); |
+ |
+ CompositorFrame frame = MakeCompositorFrame(); |
+ 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 = surface_manager().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, latency_id2, latency_sequence_number2); |
+ |
+ CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), |
+ TransferableResourceArray()); |
+ 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 = surface_manager().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(), |
+ MakeCompositorFrame()); |
+ 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()); |
+ |
+ ui::LatencyInfo::LatencyComponent comp1; |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); |
+ EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); |
+ EXPECT_TRUE( |
+ aggregated_latency_info.FindLatency(latency_type2, latency_id2, 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_ids(), |
+ {resource})); |
+ ReturnedResourceArray returned_resources; |
+ TransferableResource::ReturnResources({resource}, &returned_resources); |
+ EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); |
+ EXPECT_CALL(support_client_, |
+ DidReceiveCompositorFrameAck(Eq(returned_resources))); |
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+} |
+ |
+// Verifies that if a surface is marked destroyed and a new frame arrives for |
+// it, it will be recovered. |
+TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) { |
+ 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, surface_manager().GetSurfaceForId(child_id)); |
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ // Verify that the child surface is created. |
+ Surface* surface = surface_manager().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}, {child_id}, TransferableResourceArray())); |
+ |
+ // 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().EvictCurrentSurface(); |
+ surface = surface_manager().GetSurfaceForId(child_id); |
+ EXPECT_NE(nullptr, surface); |
+ EXPECT_TRUE(surface->destroyed()); |
+ |
+ // Child submits another frame to the same local surface id that is marked |
+ // destroyed. |
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ // Verify that the surface that was marked destroyed is recovered and is being |
+ // used again. |
+ Surface* surface2 = surface_manager().GetSurfaceForId(child_id); |
+ EXPECT_EQ(surface, surface2); |
+ EXPECT_FALSE(surface2->destroyed()); |
+} |
+ |
+// Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist |
+// anymore, it can still be reused for new surfaces. |
+TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) { |
+ 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(), |
+ MakeCompositorFrame()); |
+ EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); |
+ |
+ // Add a reference from parent. |
+ parent_support().SubmitCompositorFrame( |
+ parent_id.local_surface_id(), |
+ MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); |
+ |
+ // Remove the reference from parant. This allows us to destroy the surface. |
+ parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ // Destroy the surface. |
+ child_support1().EvictCurrentSurface(); |
+ EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); |
+ |
+ // Submit another frame with the same local surface id. This should work fine |
+ // and a new surface must be created. |
+ child_support1().SubmitCompositorFrame(child_id.local_surface_id(), |
+ MakeCompositorFrame()); |
+ EXPECT_NE(nullptr, surface_manager().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_ids(), |
+ TransferableResourceArray())); |
+ display_support().SubmitCompositorFrame( |
+ display_id.local_surface_id(), |
+ MakeCompositorFrame({parent_id1}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ |
+ // Advance BeginFrames to trigger a deadline. |
+ for (int i = 0; i < 3; ++i) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ } |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ |
+ 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_ids(), |
+ TransferableResourceArray())); |
+ display_support().SubmitCompositorFrame( |
+ display_id.local_surface_id(), |
+ MakeCompositorFrame({parent_id2}, empty_surface_ids(), |
+ TransferableResourceArray())); |
+ |
+ // 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(), |
+ MakeCompositorFrame()); |
+} |
+ |
+// 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ display_support().SubmitCompositorFrame( |
+ display_id.local_surface_id(), |
+ MakeCompositorFrame({parent_id1}, {parent_id1}, |
+ TransferableResourceArray())); |
+ |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ EXPECT_TRUE(display_surface()->HasPendingFrame()); |
+ EXPECT_FALSE(display_surface()->HasActiveFrame()); |
+ |
+ // Advance BeginFrames to trigger a deadline. This activates the |
+ // CompositorFrame submitted above. |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ for (int i = 0; i < 3; ++i) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ } |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_FALSE(dependency_tracker().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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ // 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) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ } |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_FALSE(dependency_tracker().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_id2.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ display_support().SubmitCompositorFrame( |
+ display_id.local_surface_id(), |
+ MakeCompositorFrame({parent_id1}, {parent_id2}, |
+ TransferableResourceArray())); |
+ |
+ EXPECT_TRUE(display_surface()->HasPendingFrame()); |
+ EXPECT_FALSE(display_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ |
+ // Verify that the display CompositorFrame will only block on |parent_id1| but |
+ // not |parent_id2|. |
+ EXPECT_THAT(display_surface()->blocking_surfaces(), |
+ UnorderedElementsAre(parent_id1)); |
+ // Verify that the display surface holds no references while its |
+ // CompositorFrame is pending. |
+ EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); |
+ |
+ // Submitting a CompositorFrame with |parent_id1| should unblock the display |
+ // CompositorFrame. |
+ parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), |
+ MakeCompositorFrame()); |
+ |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(display_surface()->HasPendingFrame()); |
+ EXPECT_TRUE(display_surface()->HasActiveFrame()); |
+ EXPECT_THAT(display_surface()->blocking_surfaces(), 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_ids(), |
+ TransferableResourceArray())); |
+ |
+ EXPECT_TRUE(display_surface()->HasPendingFrame()); |
+ EXPECT_FALSE(display_surface()->HasActiveFrame()); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ |
+ // Advance BeginFrames to trigger a deadline. This activates the |
+ // CompositorFrame submitted above. |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ for (int i = 0; i < 3; ++i) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ } |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_FALSE(dependency_tracker().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_ids(), |
+ TransferableResourceArray())); |
+ EXPECT_FALSE(dependency_tracker().has_deadline()); |
+ EXPECT_FALSE(parent_surface()->HasPendingFrame()); |
+ EXPECT_TRUE(parent_surface()->HasActiveFrame()); |
+} |
+ |
+// 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); |
+ ReturnedResourceArray returned_resources; |
+ TransferableResource::ReturnResources({resource}, &returned_resources); |
+ |
+ EXPECT_CALL(support_client_, |
+ DidReceiveCompositorFrameAck(Eq(ReturnedResourceArray()))); |
+ child_support1().SubmitCompositorFrame( |
+ child_id1.local_surface_id(), |
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), |
+ {resource})); |
+ 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}, {child_id1}, |
+ TransferableResourceArray())); |
+ EXPECT_TRUE(dependency_tracker().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); |
+ ReturnedResourceArray returned_resources2; |
+ TransferableResource::ReturnResources({resource2}, &returned_resources2); |
+ EXPECT_CALL(support_client_, |
+ DidReceiveCompositorFrameAck(Eq(returned_resources2))); |
+ child_support1().SubmitCompositorFrame( |
+ child_id1.local_surface_id(), |
+ MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), |
+ {resource2})); |
+ testing::Mock::VerifyAndClearExpectations(&support_client_); |
+ |
+ // Advance BeginFrames to trigger a deadline. This activates the |
+ // CompositorFrame submitted to the parent. |
+ BeginFrameArgs args = |
+ CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); |
+ for (int i = 0; i < 3; ++i) { |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_TRUE(dependency_tracker().has_deadline()); |
+ } |
+ begin_frame_source()->TestOnBeginFrame(args); |
+ EXPECT_FALSE(dependency_tracker().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_ids(), |
+ {resource2})); |
+ testing::Mock::VerifyAndClearExpectations(&support_client_); |
+} |
+*/ |
+ |
+} // namespace test |
+} // namespace cc |