Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // 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.
| |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /* | |
| 6 #include "base/containers/flat_set.h" | |
| 7 #include "cc/surfaces/compositor_frame_sink_support.h" | |
| 8 #include "cc/surfaces/surface_manager.h" | |
| 9 #include "cc/test/begin_frame_args_test.h" | |
| 10 #include "cc/test/compositor_frame_helpers.h" | |
| 11 #include "cc/test/fake_external_begin_frame_source.h" | |
| 12 #include "cc/test/fake_surface_observer.h" | |
| 13 #include "cc/test/mock_compositor_frame_sink_support_client.h" | |
| 14 #include "testing/gmock/include/gmock/gmock.h" | |
| 15 */ | |
| 16 | |
| 17 #include "cc/surfaces/frame_sink_id.h" | |
| 18 #include "cc/surfaces/local_surface_id.h" | |
| 19 #include "cc/surfaces/surface_id.h" | |
| 20 #include "hittest_aggregator.h" | |
| 21 #include "testing/gtest/include/gtest/gtest.h" | |
| 22 | |
| 23 namespace viz { | |
| 24 namespace test { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 /* | |
| 29 constexpr bool kIsRoot = true; | |
| 30 constexpr bool kIsChildRoot = false; | |
| 31 constexpr bool kHandlesFrameSinkIdInvalidation = true; | |
| 32 constexpr bool kNeedsSyncPoints = true; | |
| 33 constexpr FrameSinkId kDisplayFrameSink(2, 0); | |
| 34 constexpr FrameSinkId kParentFrameSink(3, 0); | |
| 35 constexpr FrameSinkId kChildFrameSink1(65563, 0); | |
| 36 constexpr FrameSinkId kChildFrameSink2(65564, 0); | |
| 37 constexpr FrameSinkId kArbitraryFrameSink(1337, 7331); | |
| 38 | |
| 39 std::vector<SurfaceId> empty_surface_ids() { | |
| 40 return std::vector<SurfaceId>(); | |
| 41 } | |
| 42 */ | |
| 43 constexpr cc::FrameSinkId kDisplayFrameSink(2, 0); | |
| 44 // constexpr gfx::Transform identityTransform(); | |
| 45 | |
| 46 cc::SurfaceId MakeSurfaceId(const cc::FrameSinkId& frame_sink_id, | |
| 47 uint32_t local_id) { | |
| 48 return cc::SurfaceId( | |
| 49 frame_sink_id, | |
| 50 cc::LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u))); | |
| 51 } | |
| 52 | |
| 53 } // namespace | |
| 54 | |
| 55 using namespace hittest::mojom; | |
| 56 | |
| 57 class HittestAggregatorTest : public testing::Test { | |
| 58 public: | |
| 59 HittestAggregatorTest() {} | |
| 60 ~HittestAggregatorTest() override {} | |
| 61 | |
| 62 void SetUp() override {} | |
| 63 | |
| 64 void TearDown() override {} | |
| 65 | |
| 66 HittestAggregator aggregator_; | |
| 67 | |
| 68 int count() { | |
| 69 HittestAggregator::Element* end = aggregator_.current_regions_; | |
| 70 while (end->child_count_ != LAST_REGION) { | |
| 71 end++; | |
| 72 } | |
| 73 return end - aggregator_.current_regions_; | |
| 74 } | |
| 75 | |
| 76 HittestRegion RegionAtIndex(int i) { | |
| 77 return aggregator_.current_regions_[i].region_; | |
| 78 } | |
| 79 }; | |
| 80 | |
| 81 TEST_F(HittestAggregatorTest, HittestAggregation) { | |
| 82 EXPECT_TRUE(true); | |
| 83 } | |
| 84 | |
| 85 TEST(HittestAggregatorTestNoFixture, HittestDataValidation) { | |
| 86 EXPECT_TRUE(false); | |
| 87 } | |
| 88 | |
| 89 // tests brainstorm | |
| 90 // A. Validation | |
| 91 // - 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.
| |
| 92 // - Rect is within the display | |
| 93 // - flags is one of available values? | |
| 94 // B. Aggregation | |
| 95 // - happy paths: | |
| 96 // - missing surface parent | |
| 97 // - cyclic surfaces ( possible? ) | |
|
rjkroege
2017/06/02 22:45:46
probably can't/won't happen
gklassen
2017/06/05 21:32:14
Acknowledged.
| |
| 98 // C. SurfaceFinding | |
| 99 // - | |
| 100 | |
| 101 TEST_F(HittestAggregatorTest, SimplestHappyPath) { | |
|
rjkroege
2017/06/02 22:45:46
A happy path is like a happy meal for the input ta
| |
| 102 // 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.
| |
| 103 | |
| 104 EXPECT_TRUE(count() == 0); | |
| 105 | |
| 106 cc::SurfaceId display_surface_id = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 107 | |
| 108 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
| |
| 109 hittest_data->surface_id_ = display_surface_id; | |
| 110 hittest_data->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_NONE; | |
| 111 hittest_data->rect_.SetRect(0, 0, 1024, 768); | |
| 112 | |
| 113 aggregator_.SubmitHittestData(std::move(hittest_data)); | |
| 114 | |
| 115 EXPECT_TRUE(count() == 0); | |
| 116 | |
| 117 aggregator_.Aggregate(display_surface_id); | |
| 118 | |
| 119 // there should now be only one region | |
| 120 EXPECT_TRUE(count() == 1); | |
| 121 | |
| 122 HittestRegion region = RegionAtIndex(0); | |
| 123 EXPECT_TRUE(region.rect_ == gfx::Rect(0, 0, 1027, 768)); | |
| 124 EXPECT_TRUE(region.flags_ == HittestRegionFlags::HITTEST_NONE); | |
| 125 } | |
| 126 | |
| 127 TEST_F(HittestAggregatorTest, HittestDataValidation) { | |
| 128 auto hittest_data = hittest::mojom::HittestData::New(); | |
| 129 hittest_data->surface_id_ = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 130 hittest_data->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_NONE; | |
| 131 hittest_data->rect_.SetRect(0, 0, 10, 10); | |
| 132 | |
| 133 auto hittest_region = hittest::mojom::HittestRegion::New(); | |
| 134 hittest_region->surface_id_ = MakeSurfaceId(kDisplayFrameSink, 2); | |
| 135 hittest_region->flags_ = hittest::mojom::HittestRegionFlags::HITTEST_ASK; | |
| 136 hittest_region->rect_.SetRect(0, 0, 10, 10); | |
| 137 | |
| 138 // todo: confirm that transform inits to Identity | |
| 139 // DCHECK( hittest_region.transform_ ) | |
| 140 | |
| 141 hittest_data->regions_.push_back(std::move(hittest_region)); | |
| 142 | |
| 143 aggregator_.SubmitHittestData(std::move(hittest_data)); | |
| 144 } | |
| 145 | |
| 146 /* | |
|
rjkroege
2017/06/02 22:45:46
this looks copied from elsewhere. not reviewing ye
gklassen
2017/06/05 21:32:14
Acknowledged.
| |
| 147 | |
| 148 class SurfaceSynchronizationTest : public testing::Test { | |
| 149 public: | |
| 150 SurfaceSynchronizationTest() | |
| 151 : surface_manager_(SurfaceManager::LifetimeType::REFERENCES), | |
| 152 surface_observer_(false) {} | |
| 153 ~SurfaceSynchronizationTest() override {} | |
| 154 | |
| 155 CompositorFrameSinkSupport& display_support() { return *supports_[0]; } | |
| 156 Surface* display_surface() { | |
| 157 return display_support().current_surface_for_testing(); | |
| 158 } | |
| 159 | |
| 160 CompositorFrameSinkSupport& parent_support() { return *supports_[1]; } | |
| 161 Surface* parent_surface() { | |
| 162 return parent_support().current_surface_for_testing(); | |
| 163 } | |
| 164 const ReferencedSurfaceTracker& parent_reference_tracker() { | |
| 165 return parent_support().ReferenceTrackerForTesting(); | |
| 166 } | |
| 167 | |
| 168 CompositorFrameSinkSupport& child_support1() { return *supports_[2]; } | |
| 169 Surface* child_surface1() { | |
| 170 return child_support1().current_surface_for_testing(); | |
| 171 } | |
| 172 | |
| 173 CompositorFrameSinkSupport& child_support2() { return *supports_[3]; } | |
| 174 Surface* child_surface2() { | |
| 175 return child_support2().current_surface_for_testing(); | |
| 176 } | |
| 177 | |
| 178 CompositorFrameSinkSupport& support(int index) { return *supports_[index]; } | |
| 179 Surface* surface(int index) { | |
| 180 return support(index).current_surface_for_testing(); | |
| 181 } | |
| 182 | |
| 183 SurfaceManager& surface_manager() { return surface_manager_; } | |
| 184 | |
| 185 // Returns all the references where |surface_id| is the parent. | |
| 186 const base::flat_set<SurfaceId>& GetChildReferences( | |
| 187 const SurfaceId& surface_id) { | |
| 188 return surface_manager().parent_to_child_refs_[surface_id]; | |
| 189 } | |
| 190 | |
| 191 // Returns true if there is a temporary reference for |surface_id|. | |
| 192 bool HasTemporaryReference(const SurfaceId& surface_id) { | |
| 193 return surface_manager().HasTemporaryReference(surface_id); | |
| 194 } | |
| 195 | |
| 196 SurfaceDependencyTracker& dependency_tracker() { | |
| 197 return *surface_manager_.dependency_tracker(); | |
| 198 } | |
| 199 | |
| 200 FakeExternalBeginFrameSource* begin_frame_source() { | |
| 201 return begin_frame_source_.get(); | |
| 202 } | |
| 203 | |
| 204 FakeSurfaceObserver& surface_observer() { return surface_observer_; } | |
| 205 | |
| 206 // testing::Test: | |
| 207 void SetUp() override { | |
| 208 testing::Test::SetUp(); | |
| 209 | |
| 210 begin_frame_source_ = | |
| 211 base::MakeUnique<FakeExternalBeginFrameSource>(0.f, false); | |
| 212 dependency_tracker_ = base::MakeUnique<SurfaceDependencyTracker>( | |
| 213 &surface_manager_, begin_frame_source_.get()); | |
| 214 surface_manager_.SetDependencyTracker(dependency_tracker_.get()); | |
| 215 surface_manager_.AddObserver(&surface_observer_); | |
| 216 supports_.push_back(CompositorFrameSinkSupport::Create( | |
| 217 &support_client_, &surface_manager_, kDisplayFrameSink, kIsRoot, | |
| 218 kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); | |
| 219 supports_.push_back(CompositorFrameSinkSupport::Create( | |
| 220 &support_client_, &surface_manager_, kParentFrameSink, kIsChildRoot, | |
| 221 kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); | |
| 222 supports_.push_back(CompositorFrameSinkSupport::Create( | |
| 223 &support_client_, &surface_manager_, kChildFrameSink1, kIsChildRoot, | |
| 224 kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); | |
| 225 supports_.push_back(CompositorFrameSinkSupport::Create( | |
| 226 &support_client_, &surface_manager_, kChildFrameSink2, kIsChildRoot, | |
| 227 kHandlesFrameSinkIdInvalidation, kNeedsSyncPoints)); | |
| 228 | |
| 229 // Normally, the BeginFrameSource would be registered by the Display. We | |
| 230 // register it here so that BeginFrames are received by the display support, | |
| 231 // for use in the PassesOnBeginFrameAcks test. Other supports do not receive | |
| 232 // BeginFrames, since the frame sink hierarchy is not set up in this test. | |
| 233 surface_manager_.RegisterBeginFrameSource(begin_frame_source_.get(), | |
| 234 kDisplayFrameSink); | |
| 235 } | |
| 236 | |
| 237 void TearDown() override { | |
| 238 surface_manager_.RemoveObserver(&surface_observer_); | |
| 239 surface_manager_.SetDependencyTracker(nullptr); | |
| 240 surface_manager_.UnregisterBeginFrameSource(begin_frame_source_.get()); | |
| 241 | |
| 242 dependency_tracker_.reset(); | |
| 243 | |
| 244 // SurfaceDependencyTracker depends on this BeginFrameSource and so it must | |
| 245 // be destroyed AFTER the dependency tracker is destroyed. | |
| 246 begin_frame_source_.reset(); | |
| 247 | |
| 248 supports_.clear(); | |
| 249 | |
| 250 surface_observer_.Reset(); | |
| 251 } | |
| 252 | |
| 253 protected: | |
| 254 testing::NiceMock<MockCompositorFrameSinkSupportClient> support_client_; | |
| 255 | |
| 256 private: | |
| 257 SurfaceManager surface_manager_; | |
| 258 FakeSurfaceObserver surface_observer_; | |
| 259 std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_; | |
| 260 std::unique_ptr<SurfaceDependencyTracker> dependency_tracker_; | |
| 261 std::vector<std::unique_ptr<CompositorFrameSinkSupport>> supports_; | |
| 262 | |
| 263 DISALLOW_COPY_AND_ASSIGN(SurfaceSynchronizationTest); | |
| 264 }; | |
| 265 | |
| 266 // The display root surface should have a surface reference from the top-level | |
| 267 // root added/removed when a CompositorFrame is submitted with a new SurfaceId. | |
| 268 TEST_F(SurfaceSynchronizationTest, RootSurfaceReceivesReferences) { | |
| 269 const SurfaceId display_id_first = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 270 const SurfaceId display_id_second = MakeSurfaceId(kDisplayFrameSink, 2); | |
| 271 | |
| 272 // Submit a CompositorFrame for the first display root surface. | |
| 273 display_support().SubmitCompositorFrame(display_id_first.local_surface_id(), | |
| 274 MakeCompositorFrame()); | |
| 275 | |
| 276 // A surface reference from the top-level root is added and there shouldn't be | |
| 277 // a temporary reference. | |
| 278 EXPECT_FALSE(HasTemporaryReference(display_id_first)); | |
| 279 EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), | |
| 280 UnorderedElementsAre(display_id_first)); | |
| 281 | |
| 282 // Submit a CompositorFrame for the second display root surface. | |
| 283 display_support().SubmitCompositorFrame(display_id_second.local_surface_id(), | |
| 284 MakeCompositorFrame()); | |
| 285 | |
| 286 // A surface reference from the top-level root to |display_id_second| should | |
| 287 // be added and the reference to |display_root_first| removed. | |
| 288 EXPECT_FALSE(HasTemporaryReference(display_id_second)); | |
| 289 EXPECT_THAT(GetChildReferences(surface_manager().GetRootSurfaceId()), | |
| 290 UnorderedElementsAre(display_id_second)); | |
| 291 | |
| 292 // Surface |display_id_first| is unreachable and should get deleted. | |
| 293 EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(display_id_first)); | |
| 294 } | |
| 295 | |
| 296 // The parent Surface is blocked on |child_id1| and |child_id2|. | |
| 297 TEST_F(SurfaceSynchronizationTest, BlockedOnTwo) { | |
| 298 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 299 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 300 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 301 | |
| 302 parent_support().SubmitCompositorFrame( | |
| 303 parent_id.local_surface_id(), | |
| 304 MakeCompositorFrame({child_id1, child_id2}, empty_surface_ids(), | |
| 305 TransferableResourceArray())); | |
| 306 | |
| 307 // parent_support is blocked on |child_id1| and |child_id2|. | |
| 308 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 309 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 310 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 311 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 312 UnorderedElementsAre(child_id1, child_id2)); | |
| 313 | |
| 314 // Submit a CompositorFrame without any dependencies to |child_id1|. | |
| 315 // parent_support should now only be blocked on |child_id2|. | |
| 316 child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), | |
| 317 MakeCompositorFrame()); | |
| 318 | |
| 319 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 320 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 321 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 322 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 323 UnorderedElementsAre(child_id2)); | |
| 324 | |
| 325 // Submit a CompositorFrame without any dependencies to |child_id2|. | |
| 326 // parent_support should be activated. | |
| 327 child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), | |
| 328 MakeCompositorFrame()); | |
| 329 | |
| 330 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 331 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 332 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 333 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 334 } | |
| 335 | |
| 336 // The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. | |
| 337 TEST_F(SurfaceSynchronizationTest, BlockedChain) { | |
| 338 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 339 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 340 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 341 | |
| 342 parent_support().SubmitCompositorFrame( | |
| 343 parent_id.local_surface_id(), | |
| 344 MakeCompositorFrame({child_id1}, empty_surface_ids(), | |
| 345 TransferableResourceArray())); | |
| 346 | |
| 347 // parent_support is blocked on |child_id1|. | |
| 348 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 349 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 350 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 351 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 352 UnorderedElementsAre(child_id1)); | |
| 353 // The parent should not report damage until it activates. | |
| 354 EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id)); | |
| 355 | |
| 356 child_support1().SubmitCompositorFrame( | |
| 357 child_id1.local_surface_id(), | |
| 358 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 359 TransferableResourceArray())); | |
| 360 | |
| 361 // child_support1 should now be blocked on |child_id2|. | |
| 362 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 363 EXPECT_FALSE(child_surface1()->HasActiveFrame()); | |
| 364 EXPECT_TRUE(child_surface1()->HasPendingFrame()); | |
| 365 EXPECT_THAT(child_surface1()->blocking_surfaces(), | |
| 366 UnorderedElementsAre(child_id2)); | |
| 367 // The parent and child should not report damage until they activate. | |
| 368 EXPECT_FALSE(surface_observer().IsSurfaceDamaged(parent_id)); | |
| 369 EXPECT_FALSE(surface_observer().IsSurfaceDamaged(child_id1)); | |
| 370 | |
| 371 // The parent should still be blocked on |child_id1| because it's pending. | |
| 372 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 373 UnorderedElementsAre(child_id1)); | |
| 374 | |
| 375 // Submit a CompositorFrame without any dependencies to |child_id2|. | |
| 376 // parent_support should be activated. | |
| 377 child_support2().SubmitCompositorFrame( | |
| 378 child_id2.local_surface_id(), | |
| 379 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 380 TransferableResourceArray())); | |
| 381 | |
| 382 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 383 | |
| 384 // child_surface1 should now be active. | |
| 385 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 386 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 387 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 388 | |
| 389 // parent_surface should now be active. | |
| 390 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 391 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 392 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 393 | |
| 394 // All three surfaces |parent_id|, |child_id1|, and |child_id2| should | |
| 395 // now report damage. This would trigger a new display frame. | |
| 396 EXPECT_TRUE(surface_observer().IsSurfaceDamaged(parent_id)); | |
| 397 EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id1)); | |
| 398 EXPECT_TRUE(surface_observer().IsSurfaceDamaged(child_id2)); | |
| 399 } | |
| 400 | |
| 401 // parent_surface and child_surface1 are blocked on |child_id2|. | |
| 402 TEST_F(SurfaceSynchronizationTest, TwoBlockedOnOne) { | |
| 403 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 404 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 405 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 406 | |
| 407 parent_support().SubmitCompositorFrame( | |
| 408 parent_id.local_surface_id(), | |
| 409 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 410 TransferableResourceArray())); | |
| 411 | |
| 412 // parent_support is blocked on |child_id2|. | |
| 413 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 414 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 415 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 416 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 417 UnorderedElementsAre(child_id2)); | |
| 418 | |
| 419 // child_support1 should now be blocked on |child_id2|. | |
| 420 child_support1().SubmitCompositorFrame( | |
| 421 child_id1.local_surface_id(), | |
| 422 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 423 TransferableResourceArray())); | |
| 424 | |
| 425 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 426 EXPECT_FALSE(child_surface1()->HasActiveFrame()); | |
| 427 EXPECT_TRUE(child_surface1()->HasPendingFrame()); | |
| 428 EXPECT_THAT(child_surface1()->blocking_surfaces(), | |
| 429 UnorderedElementsAre(child_id2)); | |
| 430 | |
| 431 // The parent should still be blocked on |child_id2|. | |
| 432 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 433 UnorderedElementsAre(child_id2)); | |
| 434 | |
| 435 // Submit a CompositorFrame without any dependencies to |child_id2|. | |
| 436 // parent_support should be activated. | |
| 437 child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), | |
| 438 MakeCompositorFrame()); | |
| 439 | |
| 440 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 441 | |
| 442 // child_surface1 should now be active. | |
| 443 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 444 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 445 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 446 | |
| 447 // parent_surface should now be active. | |
| 448 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 449 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 450 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 451 } | |
| 452 | |
| 453 // parent_surface is blocked on |child_id1|, and child_surface2 is blocked on | |
| 454 // |child_id2| until the deadline hits. | |
| 455 TEST_F(SurfaceSynchronizationTest, DeadlineHits) { | |
| 456 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 457 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 458 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 459 | |
| 460 parent_support().SubmitCompositorFrame( | |
| 461 parent_id.local_surface_id(), | |
| 462 MakeCompositorFrame({child_id1}, empty_surface_ids(), | |
| 463 TransferableResourceArray())); | |
| 464 | |
| 465 // parent_support is blocked on |child_id1|. | |
| 466 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 467 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 468 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 469 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 470 UnorderedElementsAre(child_id1)); | |
| 471 | |
| 472 child_support1().SubmitCompositorFrame( | |
| 473 child_id1.local_surface_id(), | |
| 474 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 475 TransferableResourceArray())); | |
| 476 | |
| 477 // child_support1 should now be blocked on |child_id2|. | |
| 478 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 479 EXPECT_FALSE(child_surface1()->HasActiveFrame()); | |
| 480 EXPECT_TRUE(child_surface1()->HasPendingFrame()); | |
| 481 EXPECT_THAT(child_surface1()->blocking_surfaces(), | |
| 482 UnorderedElementsAre(child_id2)); | |
| 483 | |
| 484 // The parent should still be blocked on |child_id1| because it's pending. | |
| 485 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 486 UnorderedElementsAre(child_id1)); | |
| 487 | |
| 488 BeginFrameArgs args = | |
| 489 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 490 | |
| 491 for (int i = 0; i < 3; ++i) { | |
| 492 begin_frame_source()->TestOnBeginFrame(args); | |
| 493 // There is still a looming deadline! Eeek! | |
| 494 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 495 | |
| 496 // parent_support is still blocked on |child_id1|. | |
| 497 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 498 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 499 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 500 UnorderedElementsAre(child_id1)); | |
| 501 | |
| 502 // child_support1 is still blocked on |child_id2|. | |
| 503 EXPECT_FALSE(child_surface1()->HasActiveFrame()); | |
| 504 EXPECT_TRUE(child_surface1()->HasPendingFrame()); | |
| 505 EXPECT_THAT(child_surface1()->blocking_surfaces(), | |
| 506 UnorderedElementsAre(child_id2)); | |
| 507 } | |
| 508 | |
| 509 begin_frame_source()->TestOnBeginFrame(args); | |
| 510 | |
| 511 // The deadline has passed. | |
| 512 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 513 | |
| 514 // parent_surface has been activated. | |
| 515 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 516 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 517 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 518 | |
| 519 // child_surface1 has been activated. | |
| 520 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 521 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 522 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 523 } | |
| 524 | |
| 525 // Verifies that the deadline does not reset if we submit CompositorFrames | |
| 526 // to new Surfaces with unresolved dependencies. | |
| 527 TEST_F(SurfaceSynchronizationTest, FramesSubmittedAfterDeadlineSet) { | |
| 528 const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); | |
| 529 BeginFrameArgs args = | |
| 530 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 531 for (int i = 0; i < 3; ++i) { | |
| 532 LocalSurfaceId local_surface_id(1, base::UnguessableToken::Create()); | |
| 533 support(i).SubmitCompositorFrame( | |
| 534 local_surface_id, | |
| 535 MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), | |
| 536 TransferableResourceArray())); | |
| 537 // The deadline has been set. | |
| 538 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 539 | |
| 540 // support(i) should be blocked on arbitrary_id. | |
| 541 EXPECT_FALSE(surface(i)->HasActiveFrame()); | |
| 542 EXPECT_TRUE(surface(i)->HasPendingFrame()); | |
| 543 EXPECT_THAT(surface(i)->blocking_surfaces(), | |
| 544 UnorderedElementsAre(arbitrary_id)); | |
| 545 | |
| 546 // Issue a BeginFrame to get closer to the deadline. | |
| 547 begin_frame_source()->TestOnBeginFrame(args); | |
| 548 } | |
| 549 | |
| 550 // The deadline hits and all the Surfaces should activate. | |
| 551 begin_frame_source()->TestOnBeginFrame(args); | |
| 552 for (int i = 0; i < 3; ++i) { | |
| 553 EXPECT_TRUE(surface(i)->HasActiveFrame()); | |
| 554 EXPECT_FALSE(surface(i)->HasPendingFrame()); | |
| 555 EXPECT_THAT(surface(i)->blocking_surfaces(), IsEmpty()); | |
| 556 } | |
| 557 } | |
| 558 | |
| 559 // This test verifies at the Surface activates once a CompositorFrame is | |
| 560 // submitted that has no unresolved dependencies. | |
| 561 TEST_F(SurfaceSynchronizationTest, NewFrameOverridesOldDependencies) { | |
| 562 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 563 const SurfaceId arbitrary_id = MakeSurfaceId(kArbitraryFrameSink, 1); | |
| 564 | |
| 565 // Submit a CompositorFrame that depends on |arbitrary_id|. | |
| 566 parent_support().SubmitCompositorFrame( | |
| 567 parent_id.local_surface_id(), | |
| 568 MakeCompositorFrame({arbitrary_id}, empty_surface_ids(), | |
| 569 TransferableResourceArray())); | |
| 570 | |
| 571 // Verify that the CompositorFrame is blocked on |arbitrary_id|. | |
| 572 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 573 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 574 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 575 UnorderedElementsAre(arbitrary_id)); | |
| 576 | |
| 577 // Submit a CompositorFrame that has no dependencies. | |
| 578 parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), | |
| 579 MakeCompositorFrame()); | |
| 580 | |
| 581 // Verify that the CompositorFrame has been activated. | |
| 582 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 583 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 584 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 585 } | |
| 586 | |
| 587 // This test verifies that a pending CompositorFrame does not affect surface | |
| 588 // references. A new surface from a child will continue to exist as a temporary | |
| 589 // reference until the parent's frame activates. | |
| 590 TEST_F(SurfaceSynchronizationTest, OnlyActiveFramesAffectSurfaceReferences) { | |
| 591 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 592 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 593 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 594 | |
| 595 // child_support1 submits a CompositorFrame without any dependencies. | |
| 596 // DidReceiveCompositorFrameAck should call on immediate activation. | |
| 597 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(1); | |
| 598 child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), | |
| 599 MakeCompositorFrame()); | |
| 600 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 601 | |
| 602 // Verify that the child surface is not blocked. | |
| 603 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 604 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 605 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 606 | |
| 607 // Verify that there's a temporary reference for |child_id1|. | |
| 608 EXPECT_TRUE(HasTemporaryReference(child_id1)); | |
| 609 | |
| 610 // parent_support submits a CompositorFrame that depends on |child_id1| | |
| 611 // (which is already active) and |child_id2|. Thus, the parent should not | |
| 612 // activate immediately. DidReceiveCompositorFrameAck should not be called | |
| 613 // immediately because the parent CompositorFrame is also blocked on | |
| 614 // |child_id2|. | |
| 615 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); | |
| 616 parent_support().SubmitCompositorFrame( | |
| 617 parent_id.local_surface_id(), | |
| 618 MakeCompositorFrame({child_id2}, {child_id1}, | |
| 619 TransferableResourceArray())); | |
| 620 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 621 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 622 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 623 UnorderedElementsAre(child_id2)); | |
| 624 EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); | |
| 625 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 626 | |
| 627 // Verify that there's a temporary reference for |child_id1| that still | |
| 628 // exists. | |
| 629 EXPECT_TRUE(HasTemporaryReference(child_id1)); | |
| 630 | |
| 631 // child_support2 submits a CompositorFrame without any dependencies. | |
| 632 // Both the child and the parent should immediately ACK CompositorFrames | |
| 633 // on activation. | |
| 634 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); | |
| 635 child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), | |
| 636 MakeCompositorFrame()); | |
| 637 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 638 | |
| 639 // Verify that the child surface is not blocked. | |
| 640 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 641 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 642 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 643 | |
| 644 // Verify that the parent surface's CompositorFrame has activated and that the | |
| 645 // temporary reference has been replaced by a permanent one. | |
| 646 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 647 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 648 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 649 EXPECT_FALSE(HasTemporaryReference(child_id1)); | |
| 650 EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); | |
| 651 } | |
| 652 | |
| 653 // This test verifies that we do not double count returned resources when a | |
| 654 // CompositorFrame starts out as pending, then becomes active, and then is | |
| 655 // replaced with another active CompositorFrame. | |
| 656 TEST_F(SurfaceSynchronizationTest, ResourcesOnlyReturnedOnce) { | |
| 657 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 658 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); | |
| 659 | |
| 660 // The parent submits a CompositorFrame that depends on |child_id| before the | |
| 661 // child submits a CompositorFrame. The CompositorFrame also has resources in | |
| 662 // its resource list. | |
| 663 TransferableResource resource; | |
| 664 resource.id = 1337; | |
| 665 resource.format = ALPHA_8; | |
| 666 resource.filter = 1234; | |
| 667 resource.size = gfx::Size(1234, 5678); | |
| 668 TransferableResourceArray resource_list = {resource}; | |
| 669 parent_support().SubmitCompositorFrame( | |
| 670 parent_id.local_surface_id(), | |
| 671 MakeCompositorFrame({child_id}, empty_surface_ids(), resource_list)); | |
| 672 | |
| 673 // Verify that the CompositorFrame is blocked on |child_id|. | |
| 674 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 675 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 676 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 677 UnorderedElementsAre(child_id)); | |
| 678 | |
| 679 child_support1().SubmitCompositorFrame( | |
| 680 child_id.local_surface_id(), | |
| 681 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 682 TransferableResourceArray())); | |
| 683 | |
| 684 // Verify that the child CompositorFrame activates immediately. | |
| 685 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 686 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 687 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 688 | |
| 689 // Verify that the parent has activated. | |
| 690 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 691 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 692 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 693 | |
| 694 ReturnedResourceArray returned_resources = {resource.ToReturnedResource()}; | |
| 695 EXPECT_CALL(support_client_, | |
| 696 DidReceiveCompositorFrameAck(returned_resources)); | |
| 697 | |
| 698 // The parent submits a CompositorFrame without any dependencies. That frame | |
| 699 // should activate immediately, replacing the earlier frame. The resource from | |
| 700 // the earlier frame should be returned to the client. | |
| 701 parent_support().SubmitCompositorFrame( | |
| 702 parent_id.local_surface_id(), | |
| 703 MakeCompositorFrame({empty_surface_ids()}, {empty_surface_ids()}, | |
| 704 TransferableResourceArray())); | |
| 705 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 706 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 707 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 708 } | |
| 709 | |
| 710 // The parent Surface is blocked on |child_id2| which is blocked on |child_id3|. | |
| 711 // child_support1 evicts its blocked Surface. The parent surface should | |
| 712 // activate. | |
| 713 TEST_F(SurfaceSynchronizationTest, EvictSurfaceWithPendingFrame) { | |
| 714 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 715 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 716 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 717 | |
| 718 // Submit a CompositorFrame that depends on |child_id1|. | |
| 719 parent_support().SubmitCompositorFrame( | |
| 720 parent_id1.local_surface_id(), | |
| 721 MakeCompositorFrame({child_id1}, empty_surface_ids(), | |
| 722 TransferableResourceArray())); | |
| 723 | |
| 724 // Verify that the CompositorFrame is blocked on |child_id1|. | |
| 725 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 726 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 727 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 728 UnorderedElementsAre(child_id1)); | |
| 729 | |
| 730 // Submit a CompositorFrame that depends on |child_id2|. | |
| 731 child_support1().SubmitCompositorFrame( | |
| 732 child_id1.local_surface_id(), | |
| 733 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 734 TransferableResourceArray())); | |
| 735 | |
| 736 // Verify that the CompositorFrame is blocked on |child_id2|. | |
| 737 EXPECT_FALSE(child_surface1()->HasActiveFrame()); | |
| 738 EXPECT_TRUE(child_surface1()->HasPendingFrame()); | |
| 739 EXPECT_THAT(child_surface1()->blocking_surfaces(), | |
| 740 UnorderedElementsAre(child_id2)); | |
| 741 | |
| 742 // Evict child_support1's current Surface. | |
| 743 // TODO(fsamuel): EvictCurrentSurface => EvictCurrentSurface. | |
| 744 child_support1().EvictCurrentSurface(); | |
| 745 | |
| 746 // The parent Surface should immediately activate. | |
| 747 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 748 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 749 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 750 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 751 } | |
| 752 | |
| 753 // This test verifies that if a surface has both a pending and active | |
| 754 // CompositorFrame and the pending CompositorFrame activates, replacing the | |
| 755 // existing active CompositorFrame, then the surface reference hierarchy will be | |
| 756 // updated allowing garbage collection of surfaces that are no longer | |
| 757 // referenced. | |
| 758 TEST_F(SurfaceSynchronizationTest, DropStaleReferencesAfterActivation) { | |
| 759 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 760 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 761 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink2, 1); | |
| 762 | |
| 763 // The parent submits a CompositorFrame that depends on |child_id1| before the | |
| 764 // child submits a CompositorFrame. | |
| 765 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); | |
| 766 parent_support().SubmitCompositorFrame( | |
| 767 parent_id.local_surface_id(), | |
| 768 MakeCompositorFrame({child_id1}, empty_surface_ids(), | |
| 769 TransferableResourceArray())); | |
| 770 | |
| 771 // Verify that the CompositorFrame is blocked on |child_id|. | |
| 772 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 773 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 774 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 775 UnorderedElementsAre(child_id1)); | |
| 776 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 777 | |
| 778 // Verify that no references are added while the CompositorFrame is pending. | |
| 779 EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); | |
| 780 | |
| 781 // DidReceiveCompositorFrameAck should get called twice: once for the child | |
| 782 // and once for the now active parent CompositorFrame. | |
| 783 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(2); | |
| 784 child_support1().SubmitCompositorFrame(child_id1.local_surface_id(), | |
| 785 MakeCompositorFrame()); | |
| 786 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 787 | |
| 788 // Verify that the child CompositorFrame activates immediately. | |
| 789 EXPECT_TRUE(child_surface1()->HasActiveFrame()); | |
| 790 EXPECT_FALSE(child_surface1()->HasPendingFrame()); | |
| 791 EXPECT_THAT(child_surface1()->blocking_surfaces(), IsEmpty()); | |
| 792 | |
| 793 // Verify that the parent Surface has activated. | |
| 794 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 795 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 796 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 797 | |
| 798 // Submit a new parent CompositorFrame to add a reference. | |
| 799 parent_support().SubmitCompositorFrame( | |
| 800 parent_id.local_surface_id(), | |
| 801 MakeCompositorFrame(empty_surface_ids(), {child_id1}, | |
| 802 TransferableResourceArray())); | |
| 803 | |
| 804 // Verify that the parent Surface has activated. | |
| 805 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 806 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 807 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 808 | |
| 809 // Verify that there is no temporary reference for the child and that | |
| 810 // the reference from the parent to the child still exists. | |
| 811 EXPECT_FALSE(HasTemporaryReference(child_id1)); | |
| 812 EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); | |
| 813 | |
| 814 // The parent submits another CompositorFrame that depends on |child_id2|. | |
| 815 // Submitting a pending CompositorFrame will not trigger a CompositorFrameAck. | |
| 816 EXPECT_CALL(support_client_, DidReceiveCompositorFrameAck(_)).Times(0); | |
| 817 parent_support().SubmitCompositorFrame( | |
| 818 parent_id.local_surface_id(), | |
| 819 MakeCompositorFrame({child_id2}, empty_surface_ids(), | |
| 820 TransferableResourceArray())); | |
| 821 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 822 | |
| 823 // The parent surface should now have both a pending and activate | |
| 824 // CompositorFrame. Verify that the set of child references from | |
| 825 // |parent_id| are only from the active CompositorFrame. | |
| 826 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 827 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 828 EXPECT_THAT(parent_surface()->blocking_surfaces(), | |
| 829 UnorderedElementsAre(child_id2)); | |
| 830 EXPECT_THAT(GetChildReferences(parent_id), UnorderedElementsAre(child_id1)); | |
| 831 | |
| 832 child_support2().SubmitCompositorFrame(child_id2.local_surface_id(), | |
| 833 MakeCompositorFrame()); | |
| 834 | |
| 835 // Verify that the parent Surface has activated and no longer has a pending | |
| 836 // CompositorFrame. Also verify that |child_id1| is no longer a child | |
| 837 // reference of |parent_id|. | |
| 838 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 839 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 840 EXPECT_THAT(parent_surface()->blocking_surfaces(), IsEmpty()); | |
| 841 // The parent will not immediately refer to the child until it submits a new | |
| 842 // CompositorFrame with the reference. | |
| 843 EXPECT_THAT(GetChildReferences(parent_id), IsEmpty()); | |
| 844 } | |
| 845 | |
| 846 // Checks whether the latency info are moved to the new surface from the old | |
| 847 // one when LocalSurfaceId changes. No frame has unresolved dependencies. | |
| 848 TEST_F(SurfaceSynchronizationTest, | |
| 849 LatencyInfoCarriedOverOnResize_NoUnresolvedDependencies) { | |
| 850 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 851 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 852 const ui::LatencyComponentType latency_type1 = | |
| 853 ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; | |
| 854 const int64_t latency_id1 = 234; | |
| 855 const int64_t latency_sequence_number1 = 5645432; | |
| 856 const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; | |
| 857 const int64_t latency_id2 = 31434351; | |
| 858 const int64_t latency_sequence_number2 = 663788; | |
| 859 | |
| 860 // Submit a frame with latency info | |
| 861 ui::LatencyInfo info; | |
| 862 info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); | |
| 863 | |
| 864 CompositorFrame frame = MakeCompositorFrame(); | |
| 865 frame.metadata.latency_info.push_back(info); | |
| 866 | |
| 867 parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), | |
| 868 std::move(frame)); | |
| 869 | |
| 870 // Verify that the old surface has an active frame and no pending frame. | |
| 871 Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); | |
| 872 ASSERT_NE(nullptr, old_surface); | |
| 873 EXPECT_TRUE(old_surface->HasActiveFrame()); | |
| 874 EXPECT_FALSE(old_surface->HasPendingFrame()); | |
| 875 | |
| 876 // Submit another frame with some other latency info and a different | |
| 877 // LocalSurfaceId. | |
| 878 ui::LatencyInfo info2; | |
| 879 info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); | |
| 880 | |
| 881 CompositorFrame frame2 = MakeCompositorFrame(); | |
| 882 frame2.metadata.latency_info.push_back(info2); | |
| 883 | |
| 884 parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), | |
| 885 std::move(frame2)); | |
| 886 | |
| 887 // Verify that the new surface has an active frame and no pending frames. | |
| 888 Surface* surface = surface_manager().GetSurfaceForId(parent_id2); | |
| 889 ASSERT_NE(nullptr, surface); | |
| 890 EXPECT_TRUE(surface->HasActiveFrame()); | |
| 891 EXPECT_FALSE(surface->HasPendingFrame()); | |
| 892 | |
| 893 // Verify that the new surface has both latency info elements. | |
| 894 std::vector<ui::LatencyInfo> info_list; | |
| 895 surface->TakeLatencyInfo(&info_list); | |
| 896 EXPECT_EQ(2u, info_list.size()); | |
| 897 | |
| 898 ui::LatencyInfo aggregated_latency_info = info_list[0]; | |
| 899 aggregated_latency_info.AddNewLatencyFrom(info_list[1]); | |
| 900 | |
| 901 // Two components are the original ones, and the third one is | |
| 902 // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame | |
| 903 // submit. | |
| 904 EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); | |
| 905 | |
| 906 ui::LatencyInfo::LatencyComponent comp1; | |
| 907 EXPECT_TRUE( | |
| 908 aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); | |
| 909 EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); | |
| 910 EXPECT_TRUE( | |
| 911 aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); | |
| 912 EXPECT_TRUE(aggregated_latency_info.FindLatency( | |
| 913 ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); | |
| 914 } | |
| 915 | |
| 916 // Checks whether the latency info are moved to the new surface from the old | |
| 917 // one when LocalSurfaceId changes. Old surface has unresolved dependencies. | |
| 918 TEST_F(SurfaceSynchronizationTest, | |
| 919 LatencyInfoCarriedOverOnResize_OldSurfaceHasPendingAndActiveFrame) { | |
| 920 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 921 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 922 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); | |
| 923 | |
| 924 const ui::LatencyComponentType latency_type1 = | |
| 925 ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; | |
| 926 const int64_t latency_id1 = 234; | |
| 927 const int64_t latency_sequence_number1 = 5645432; | |
| 928 const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; | |
| 929 const int64_t latency_id2 = 31434351; | |
| 930 const int64_t latency_sequence_number2 = 663788; | |
| 931 | |
| 932 // Submit a frame with no unresolved dependecy. | |
| 933 ui::LatencyInfo info; | |
| 934 info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); | |
| 935 | |
| 936 CompositorFrame frame = MakeCompositorFrame(); | |
| 937 frame.metadata.latency_info.push_back(info); | |
| 938 | |
| 939 parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), | |
| 940 std::move(frame)); | |
| 941 | |
| 942 // Submit a frame with unresolved dependencies. | |
| 943 ui::LatencyInfo info2; | |
| 944 info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); | |
| 945 | |
| 946 CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 947 TransferableResourceArray()); | |
| 948 frame2.metadata.latency_info.push_back(info2); | |
| 949 | |
| 950 parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), | |
| 951 std::move(frame2)); | |
| 952 | |
| 953 // Verify that the old surface has both an active and a pending frame. | |
| 954 Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); | |
| 955 ASSERT_NE(nullptr, old_surface); | |
| 956 EXPECT_TRUE(old_surface->HasActiveFrame()); | |
| 957 EXPECT_TRUE(old_surface->HasPendingFrame()); | |
| 958 | |
| 959 // Submit a frame with a new local surface id. | |
| 960 parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), | |
| 961 MakeCompositorFrame()); | |
| 962 | |
| 963 // Verify that the new surface has an active frame only. | |
| 964 Surface* surface = surface_manager().GetSurfaceForId(parent_id2); | |
| 965 ASSERT_NE(nullptr, surface); | |
| 966 EXPECT_TRUE(surface->HasActiveFrame()); | |
| 967 EXPECT_FALSE(surface->HasPendingFrame()); | |
| 968 | |
| 969 // Verify that the new surface has latency info from both active and pending | |
| 970 // frame of the old surface. | |
| 971 std::vector<ui::LatencyInfo> info_list; | |
| 972 surface->TakeLatencyInfo(&info_list); | |
| 973 EXPECT_EQ(2u, info_list.size()); | |
| 974 | |
| 975 ui::LatencyInfo aggregated_latency_info = info_list[0]; | |
| 976 aggregated_latency_info.AddNewLatencyFrom(info_list[1]); | |
| 977 | |
| 978 // Two components are the original ones, and the third one is | |
| 979 // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame | |
| 980 // submit. | |
| 981 EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); | |
| 982 | |
| 983 ui::LatencyInfo::LatencyComponent comp1; | |
| 984 EXPECT_TRUE( | |
| 985 aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); | |
| 986 EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); | |
| 987 EXPECT_TRUE( | |
| 988 aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); | |
| 989 EXPECT_TRUE(aggregated_latency_info.FindLatency( | |
| 990 ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); | |
| 991 } | |
| 992 | |
| 993 // Checks whether the latency info are moved to the new surface from the old | |
| 994 // one when LocalSurfaceId changes. The new surface has unresolved dependencies. | |
| 995 TEST_F(SurfaceSynchronizationTest, | |
| 996 LatencyInfoCarriedOverOnResize_NewSurfaceHasPendingFrame) { | |
| 997 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 998 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 999 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); | |
| 1000 | |
| 1001 const ui::LatencyComponentType latency_type1 = | |
| 1002 ui::BROWSER_SNAPSHOT_FRAME_NUMBER_COMPONENT; | |
| 1003 const int64_t latency_id1 = 234; | |
| 1004 const int64_t latency_sequence_number1 = 5645432; | |
| 1005 const ui::LatencyComponentType latency_type2 = ui::TAB_SHOW_COMPONENT; | |
| 1006 const int64_t latency_id2 = 31434351; | |
| 1007 const int64_t latency_sequence_number2 = 663788; | |
| 1008 | |
| 1009 // Submit a frame with no unresolved dependencies. | |
| 1010 ui::LatencyInfo info; | |
| 1011 info.AddLatencyNumber(latency_type1, latency_id1, latency_sequence_number1); | |
| 1012 | |
| 1013 CompositorFrame frame = MakeCompositorFrame(); | |
| 1014 frame.metadata.latency_info.push_back(info); | |
| 1015 | |
| 1016 parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), | |
| 1017 std::move(frame)); | |
| 1018 | |
| 1019 // Verify that the old surface has an active frame only. | |
| 1020 Surface* old_surface = surface_manager().GetSurfaceForId(parent_id1); | |
| 1021 ASSERT_NE(nullptr, old_surface); | |
| 1022 EXPECT_TRUE(old_surface->HasActiveFrame()); | |
| 1023 EXPECT_FALSE(old_surface->HasPendingFrame()); | |
| 1024 | |
| 1025 // Submit a frame with a new local surface id and with unresolved | |
| 1026 // dependencies. | |
| 1027 ui::LatencyInfo info2; | |
| 1028 info2.AddLatencyNumber(latency_type2, latency_id2, latency_sequence_number2); | |
| 1029 | |
| 1030 CompositorFrame frame2 = MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 1031 TransferableResourceArray()); | |
| 1032 frame2.metadata.latency_info.push_back(info2); | |
| 1033 | |
| 1034 parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), | |
| 1035 std::move(frame2)); | |
| 1036 | |
| 1037 // Verify that the new surface has a pending frame and no active frame. | |
| 1038 Surface* surface = surface_manager().GetSurfaceForId(parent_id2); | |
| 1039 ASSERT_NE(nullptr, surface); | |
| 1040 EXPECT_TRUE(surface->HasPendingFrame()); | |
| 1041 EXPECT_FALSE(surface->HasActiveFrame()); | |
| 1042 | |
| 1043 // Resolve the dependencies. The frame in parent's surface must become active. | |
| 1044 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1045 MakeCompositorFrame()); | |
| 1046 EXPECT_FALSE(surface->HasPendingFrame()); | |
| 1047 EXPECT_TRUE(surface->HasActiveFrame()); | |
| 1048 | |
| 1049 // Both latency info elements must exist in the now-activated frame of the | |
| 1050 // new surface. | |
| 1051 std::vector<ui::LatencyInfo> info_list; | |
| 1052 surface->TakeLatencyInfo(&info_list); | |
| 1053 EXPECT_EQ(2u, info_list.size()); | |
| 1054 | |
| 1055 ui::LatencyInfo aggregated_latency_info = info_list[0]; | |
| 1056 aggregated_latency_info.AddNewLatencyFrom(info_list[1]); | |
| 1057 | |
| 1058 // Two components are the original ones, and the third one is | |
| 1059 // DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, logged on compositor frame | |
| 1060 // submit. | |
| 1061 EXPECT_EQ(3u, aggregated_latency_info.latency_components().size()); | |
| 1062 | |
| 1063 ui::LatencyInfo::LatencyComponent comp1; | |
| 1064 EXPECT_TRUE( | |
| 1065 aggregated_latency_info.FindLatency(latency_type1, latency_id1, &comp1)); | |
| 1066 EXPECT_EQ(latency_sequence_number1, comp1.sequence_number); | |
| 1067 EXPECT_TRUE( | |
| 1068 aggregated_latency_info.FindLatency(latency_type2, latency_id2, nullptr)); | |
| 1069 EXPECT_TRUE(aggregated_latency_info.FindLatency( | |
| 1070 ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, nullptr)); | |
| 1071 } | |
| 1072 | |
| 1073 // Checks that resources and ack are sent together if possible. | |
| 1074 TEST_F(SurfaceSynchronizationTest, ReturnResourcesWithAck) { | |
| 1075 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 1076 TransferableResource resource; | |
| 1077 resource.id = 1234; | |
| 1078 parent_support().SubmitCompositorFrame( | |
| 1079 parent_id.local_surface_id(), | |
| 1080 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 1081 {resource})); | |
| 1082 ReturnedResourceArray returned_resources; | |
| 1083 TransferableResource::ReturnResources({resource}, &returned_resources); | |
| 1084 EXPECT_CALL(support_client_, ReclaimResources(_)).Times(0); | |
| 1085 EXPECT_CALL(support_client_, | |
| 1086 DidReceiveCompositorFrameAck(Eq(returned_resources))); | |
| 1087 parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), | |
| 1088 MakeCompositorFrame()); | |
| 1089 } | |
| 1090 | |
| 1091 // Verifies that if a surface is marked destroyed and a new frame arrives for | |
| 1092 // it, it will be recovered. | |
| 1093 TEST_F(SurfaceSynchronizationTest, SurfaceResurrection) { | |
| 1094 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 1095 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); | |
| 1096 | |
| 1097 // Create the child surface by submitting a frame to it. | |
| 1098 EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); | |
| 1099 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1100 MakeCompositorFrame()); | |
| 1101 | |
| 1102 // Verify that the child surface is created. | |
| 1103 Surface* surface = surface_manager().GetSurfaceForId(child_id); | |
| 1104 EXPECT_NE(nullptr, surface); | |
| 1105 | |
| 1106 // Add a reference from the parent to the child. | |
| 1107 parent_support().SubmitCompositorFrame( | |
| 1108 parent_id.local_surface_id(), | |
| 1109 MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); | |
| 1110 | |
| 1111 // Attempt to destroy the child surface. The surface must still exist since | |
| 1112 // the parent needs it but it will be marked as destroyed. | |
| 1113 child_support1().EvictCurrentSurface(); | |
| 1114 surface = surface_manager().GetSurfaceForId(child_id); | |
| 1115 EXPECT_NE(nullptr, surface); | |
| 1116 EXPECT_TRUE(surface->destroyed()); | |
| 1117 | |
| 1118 // Child submits another frame to the same local surface id that is marked | |
| 1119 // destroyed. | |
| 1120 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1121 MakeCompositorFrame()); | |
| 1122 | |
| 1123 // Verify that the surface that was marked destroyed is recovered and is being | |
| 1124 // used again. | |
| 1125 Surface* surface2 = surface_manager().GetSurfaceForId(child_id); | |
| 1126 EXPECT_EQ(surface, surface2); | |
| 1127 EXPECT_FALSE(surface2->destroyed()); | |
| 1128 } | |
| 1129 | |
| 1130 // Verifies that if a LocalSurfaceId belonged to a surface that doesn't exist | |
| 1131 // anymore, it can still be reused for new surfaces. | |
| 1132 TEST_F(SurfaceSynchronizationTest, LocalSurfaceIdIsReusable) { | |
| 1133 const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1); | |
| 1134 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 3); | |
| 1135 | |
| 1136 // Submit the first frame. Creates the surface. | |
| 1137 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1138 MakeCompositorFrame()); | |
| 1139 EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); | |
| 1140 | |
| 1141 // Add a reference from parent. | |
| 1142 parent_support().SubmitCompositorFrame( | |
| 1143 parent_id.local_surface_id(), | |
| 1144 MakeCompositorFrame({child_id}, {child_id}, TransferableResourceArray())); | |
| 1145 | |
| 1146 // Remove the reference from parant. This allows us to destroy the surface. | |
| 1147 parent_support().SubmitCompositorFrame(parent_id.local_surface_id(), | |
| 1148 MakeCompositorFrame()); | |
| 1149 | |
| 1150 // Destroy the surface. | |
| 1151 child_support1().EvictCurrentSurface(); | |
| 1152 EXPECT_EQ(nullptr, surface_manager().GetSurfaceForId(child_id)); | |
| 1153 | |
| 1154 // Submit another frame with the same local surface id. This should work fine | |
| 1155 // and a new surface must be created. | |
| 1156 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1157 MakeCompositorFrame()); | |
| 1158 EXPECT_NE(nullptr, surface_manager().GetSurfaceForId(child_id)); | |
| 1159 } | |
| 1160 | |
| 1161 // This test verifies that a crash does not occur if garbage collection is | |
| 1162 // triggered during surface dependency resolution. This test triggers garbage | |
| 1163 // collection during surface resolution, by causing an activation to remove | |
| 1164 // a surface subtree from the root. Both the old subtree and the new | |
| 1165 // activated subtree refer to the same dependency. The old subtree was activated | |
| 1166 // by deadline, and the new subtree was activated by a dependency finally | |
| 1167 // resolving. | |
| 1168 TEST_F(SurfaceSynchronizationTest, DependencyTrackingGarbageCollection) { | |
| 1169 const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 1170 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 1171 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 1172 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); | |
| 1173 | |
| 1174 parent_support().SubmitCompositorFrame( | |
| 1175 parent_id1.local_surface_id(), | |
| 1176 MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 1177 TransferableResourceArray())); | |
| 1178 display_support().SubmitCompositorFrame( | |
| 1179 display_id.local_surface_id(), | |
| 1180 MakeCompositorFrame({parent_id1}, empty_surface_ids(), | |
| 1181 TransferableResourceArray())); | |
| 1182 | |
| 1183 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1184 | |
| 1185 BeginFrameArgs args = | |
| 1186 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 1187 | |
| 1188 // Advance BeginFrames to trigger a deadline. | |
| 1189 for (int i = 0; i < 3; ++i) { | |
| 1190 begin_frame_source()->TestOnBeginFrame(args); | |
| 1191 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1192 } | |
| 1193 begin_frame_source()->TestOnBeginFrame(args); | |
| 1194 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1195 | |
| 1196 EXPECT_TRUE(display_surface()->HasActiveFrame()); | |
| 1197 EXPECT_FALSE(display_surface()->HasPendingFrame()); | |
| 1198 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 1199 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 1200 | |
| 1201 parent_support().SubmitCompositorFrame( | |
| 1202 parent_id2.local_surface_id(), | |
| 1203 MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 1204 TransferableResourceArray())); | |
| 1205 display_support().SubmitCompositorFrame( | |
| 1206 display_id.local_surface_id(), | |
| 1207 MakeCompositorFrame({parent_id2}, empty_surface_ids(), | |
| 1208 TransferableResourceArray())); | |
| 1209 | |
| 1210 // The display surface now has two CompositorFrames. One that is pending, | |
| 1211 // indirectly blocked on child_id and one that is active, also indirectly | |
| 1212 // referring to child_id, but activated due to the deadline above. | |
| 1213 EXPECT_TRUE(display_surface()->HasActiveFrame()); | |
| 1214 EXPECT_TRUE(display_surface()->HasPendingFrame()); | |
| 1215 | |
| 1216 // Submitting a CompositorFrame will trigger garbage collection of the | |
| 1217 // |parent_id1| subtree. This should not crash. | |
| 1218 child_support1().SubmitCompositorFrame(child_id.local_surface_id(), | |
| 1219 MakeCompositorFrame()); | |
| 1220 } | |
| 1221 | |
| 1222 // This test verifies that a crash does not occur if garbage collection is | |
| 1223 // triggered when a deadline forces frame activation. This test triggers garbage | |
| 1224 // collection during deadline activation by causing the activation of a display | |
| 1225 // frame to replace a previously activated display frame that was referring to | |
| 1226 // a now-unreachable surface subtree. That subtree gets garbage collected during | |
| 1227 // deadline activation. SurfaceDependencyTracker is also tracking a surface | |
| 1228 // from that subtree due to an unresolved dependency. This test verifies that | |
| 1229 // this dependency resolution does not crash. | |
| 1230 TEST_F(SurfaceSynchronizationTest, GarbageCollectionOnDeadline) { | |
| 1231 const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 1232 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 1233 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 1234 const SurfaceId child_id = MakeSurfaceId(kChildFrameSink1, 1); | |
| 1235 | |
| 1236 // |parent_id1| is blocked on |child_id|. | |
| 1237 parent_support().SubmitCompositorFrame( | |
| 1238 parent_id1.local_surface_id(), | |
| 1239 MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 1240 TransferableResourceArray())); | |
| 1241 | |
| 1242 display_support().SubmitCompositorFrame( | |
| 1243 display_id.local_surface_id(), | |
| 1244 MakeCompositorFrame({parent_id1}, {parent_id1}, | |
| 1245 TransferableResourceArray())); | |
| 1246 | |
| 1247 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1248 EXPECT_TRUE(display_surface()->HasPendingFrame()); | |
| 1249 EXPECT_FALSE(display_surface()->HasActiveFrame()); | |
| 1250 | |
| 1251 // Advance BeginFrames to trigger a deadline. This activates the | |
| 1252 // CompositorFrame submitted above. | |
| 1253 BeginFrameArgs args = | |
| 1254 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 1255 for (int i = 0; i < 3; ++i) { | |
| 1256 begin_frame_source()->TestOnBeginFrame(args); | |
| 1257 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1258 } | |
| 1259 begin_frame_source()->TestOnBeginFrame(args); | |
| 1260 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1261 EXPECT_FALSE(display_surface()->HasPendingFrame()); | |
| 1262 EXPECT_TRUE(display_surface()->HasActiveFrame()); | |
| 1263 | |
| 1264 // By submitting a display CompositorFrame, and replacing the parent's | |
| 1265 // CompositorFrame with another surface ID, parent_id1 becomes unreachable and | |
| 1266 // a candidate for garbage collection. | |
| 1267 display_support().SubmitCompositorFrame( | |
| 1268 display_id.local_surface_id(), | |
| 1269 MakeCompositorFrame({parent_id2}, empty_surface_ids(), | |
| 1270 TransferableResourceArray())); | |
| 1271 | |
| 1272 // Now |parent_id1| is only kept alive by the active |display_id| frame. | |
| 1273 parent_support().SubmitCompositorFrame( | |
| 1274 parent_id2.local_surface_id(), | |
| 1275 MakeCompositorFrame({child_id}, empty_surface_ids(), | |
| 1276 TransferableResourceArray())); | |
| 1277 | |
| 1278 // SurfaceDependencyTracker should now be tracking |display_id|, |parent_id1| | |
| 1279 // and |parent_id2|. By activating the pending |display_id| frame by deadline, | |
| 1280 // |parent_id1| becomes unreachable and is garbage collected while | |
| 1281 // SurfaceDependencyTracker is in the process of activating surfaces. This | |
| 1282 // should not cause a crash or use-after-free. | |
| 1283 for (int i = 0; i < 3; ++i) { | |
| 1284 begin_frame_source()->TestOnBeginFrame(args); | |
| 1285 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1286 } | |
| 1287 begin_frame_source()->TestOnBeginFrame(args); | |
| 1288 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1289 } | |
| 1290 | |
| 1291 // This test verifies that a CompositorFrame will only blocked on embedded | |
| 1292 // surfaces but not on other retained surface IDs in the CompositorFrame. | |
| 1293 TEST_F(SurfaceSynchronizationTest, OnlyBlockOnEmbeddedSurfaces) { | |
| 1294 const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 1295 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 1296 const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2); | |
| 1297 | |
| 1298 // Submitting a CompositorFrame with |parent_id2| so that the display | |
| 1299 // CompositorFrame can hold a reference to it. | |
| 1300 parent_support().SubmitCompositorFrame(parent_id2.local_surface_id(), | |
| 1301 MakeCompositorFrame()); | |
| 1302 | |
| 1303 display_support().SubmitCompositorFrame( | |
| 1304 display_id.local_surface_id(), | |
| 1305 MakeCompositorFrame({parent_id1}, {parent_id2}, | |
| 1306 TransferableResourceArray())); | |
| 1307 | |
| 1308 EXPECT_TRUE(display_surface()->HasPendingFrame()); | |
| 1309 EXPECT_FALSE(display_surface()->HasActiveFrame()); | |
| 1310 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1311 | |
| 1312 // Verify that the display CompositorFrame will only block on |parent_id1| but | |
| 1313 // not |parent_id2|. | |
| 1314 EXPECT_THAT(display_surface()->blocking_surfaces(), | |
| 1315 UnorderedElementsAre(parent_id1)); | |
| 1316 // Verify that the display surface holds no references while its | |
| 1317 // CompositorFrame is pending. | |
| 1318 EXPECT_THAT(GetChildReferences(display_id), IsEmpty()); | |
| 1319 | |
| 1320 // Submitting a CompositorFrame with |parent_id1| should unblock the display | |
| 1321 // CompositorFrame. | |
| 1322 parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(), | |
| 1323 MakeCompositorFrame()); | |
| 1324 | |
| 1325 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1326 EXPECT_FALSE(display_surface()->HasPendingFrame()); | |
| 1327 EXPECT_TRUE(display_surface()->HasActiveFrame()); | |
| 1328 EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty()); | |
| 1329 } | |
| 1330 | |
| 1331 // This test verifies that a late arriving CompositorFrame activates immediately | |
| 1332 // and does not trigger a new deadline. | |
| 1333 TEST_F(SurfaceSynchronizationTest, LateArrivingDependency) { | |
| 1334 const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1); | |
| 1335 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 1336 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 1337 | |
| 1338 display_support().SubmitCompositorFrame( | |
| 1339 display_id.local_surface_id(), | |
| 1340 MakeCompositorFrame({parent_id1}, empty_surface_ids(), | |
| 1341 TransferableResourceArray())); | |
| 1342 | |
| 1343 EXPECT_TRUE(display_surface()->HasPendingFrame()); | |
| 1344 EXPECT_FALSE(display_surface()->HasActiveFrame()); | |
| 1345 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1346 | |
| 1347 // Advance BeginFrames to trigger a deadline. This activates the | |
| 1348 // CompositorFrame submitted above. | |
| 1349 BeginFrameArgs args = | |
| 1350 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 1351 for (int i = 0; i < 3; ++i) { | |
| 1352 begin_frame_source()->TestOnBeginFrame(args); | |
| 1353 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1354 } | |
| 1355 begin_frame_source()->TestOnBeginFrame(args); | |
| 1356 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1357 EXPECT_FALSE(display_surface()->HasPendingFrame()); | |
| 1358 EXPECT_TRUE(display_surface()->HasActiveFrame()); | |
| 1359 | |
| 1360 // A late arriving CompositorFrame should activate immediately without | |
| 1361 // scheduling a deadline and without waiting for dependencies to resolve. | |
| 1362 parent_support().SubmitCompositorFrame( | |
| 1363 parent_id1.local_surface_id(), | |
| 1364 MakeCompositorFrame({child_id1}, empty_surface_ids(), | |
| 1365 TransferableResourceArray())); | |
| 1366 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1367 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 1368 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 1369 } | |
| 1370 | |
| 1371 // This test verifies that CompositorFrames submitted to a surface referenced | |
| 1372 // by a parent CompositorFrame as a fallback will be rejected and ACK'ed | |
| 1373 // immediately. | |
| 1374 TEST_F(SurfaceSynchronizationTest, FallbackSurfacesClosed) { | |
| 1375 const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1); | |
| 1376 // This is the fallback child surface that the parent holds a reference to. | |
| 1377 const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1); | |
| 1378 // This is the primary child surface that the parent wants to block on. | |
| 1379 const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 2); | |
| 1380 | |
| 1381 // child_support1 submits a CompositorFrame without any dependencies. | |
| 1382 // DidReceiveCompositorFrameAck should call on immediate activation. | |
| 1383 // However, resources will not be returned because this frame is a candidate | |
| 1384 // for display. | |
| 1385 TransferableResource resource; | |
| 1386 resource.id = 1337; | |
| 1387 resource.format = ALPHA_8; | |
| 1388 resource.filter = 1234; | |
| 1389 resource.size = gfx::Size(1234, 5678); | |
| 1390 ReturnedResourceArray returned_resources; | |
| 1391 TransferableResource::ReturnResources({resource}, &returned_resources); | |
| 1392 | |
| 1393 EXPECT_CALL(support_client_, | |
| 1394 DidReceiveCompositorFrameAck(Eq(ReturnedResourceArray()))); | |
| 1395 child_support1().SubmitCompositorFrame( | |
| 1396 child_id1.local_surface_id(), | |
| 1397 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 1398 {resource})); | |
| 1399 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 1400 | |
| 1401 // The parent is blocked on |child_id2| and references |child_id1|. The | |
| 1402 // surface corresponding to |child_id1| will not accept new CompositorFrames | |
| 1403 // while the parent CompositorFrame is blocked. | |
| 1404 parent_support().SubmitCompositorFrame( | |
| 1405 parent_id1.local_surface_id(), | |
| 1406 MakeCompositorFrame({child_id2}, {child_id1}, | |
| 1407 TransferableResourceArray())); | |
| 1408 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1409 EXPECT_TRUE(parent_surface()->HasPendingFrame()); | |
| 1410 EXPECT_FALSE(parent_surface()->HasActiveFrame()); | |
| 1411 | |
| 1412 // Resources will be returned immediately because |child_id1|'s surface is | |
| 1413 // closed. | |
| 1414 TransferableResource resource2; | |
| 1415 resource2.id = 1246; | |
| 1416 resource2.format = ALPHA_8; | |
| 1417 resource2.filter = 1357; | |
| 1418 resource2.size = gfx::Size(8765, 4321); | |
| 1419 ReturnedResourceArray returned_resources2; | |
| 1420 TransferableResource::ReturnResources({resource2}, &returned_resources2); | |
| 1421 EXPECT_CALL(support_client_, | |
| 1422 DidReceiveCompositorFrameAck(Eq(returned_resources2))); | |
| 1423 child_support1().SubmitCompositorFrame( | |
| 1424 child_id1.local_surface_id(), | |
| 1425 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 1426 {resource2})); | |
| 1427 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 1428 | |
| 1429 // Advance BeginFrames to trigger a deadline. This activates the | |
| 1430 // CompositorFrame submitted to the parent. | |
| 1431 BeginFrameArgs args = | |
| 1432 CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); | |
| 1433 for (int i = 0; i < 3; ++i) { | |
| 1434 begin_frame_source()->TestOnBeginFrame(args); | |
| 1435 EXPECT_TRUE(dependency_tracker().has_deadline()); | |
| 1436 } | |
| 1437 begin_frame_source()->TestOnBeginFrame(args); | |
| 1438 EXPECT_FALSE(dependency_tracker().has_deadline()); | |
| 1439 EXPECT_FALSE(parent_surface()->HasPendingFrame()); | |
| 1440 EXPECT_TRUE(parent_surface()->HasActiveFrame()); | |
| 1441 | |
| 1442 // Resources will be returned immediately because |child_id1|'s surface is | |
| 1443 // closed forever. | |
| 1444 EXPECT_CALL(support_client_, | |
| 1445 DidReceiveCompositorFrameAck(Eq(returned_resources2))); | |
| 1446 child_support1().SubmitCompositorFrame( | |
| 1447 child_id1.local_surface_id(), | |
| 1448 MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(), | |
| 1449 {resource2})); | |
| 1450 testing::Mock::VerifyAndClearExpectations(&support_client_); | |
| 1451 } | |
| 1452 */ | |
| 1453 | |
| 1454 } // namespace test | |
| 1455 } // namespace cc | |
| OLD | NEW |