Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(136)

Side by Side Diff: content/browser/media/capture/animated_content_sampler_unittest.cc

Issue 1123623005: Tab Capture: AnimatedContentSampler subsampling and phase fixes in tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/media/capture/animated_content_sampler.h" 5 #include "content/browser/media/capture/animated_content_sampler.h"
6 6
7 #include <cstdlib> 7 #include <cmath>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/time/time.h" 13 #include "base/time/time.h"
14 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h" 15 #include "ui/gfx/geometry/rect.h"
16 16
17 namespace content { 17 namespace content {
18 18
19 namespace { 19 namespace {
20 20
21 base::TimeTicks InitialTestTimeTicks() { 21 base::TimeTicks InitialTestTimeTicks() {
22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1); 22 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
23 } 23 }
24 24
25 base::TimeDelta FpsAsPeriod(int frame_rate) {
26 return base::TimeDelta::FromSeconds(1) / frame_rate;
27 }
28
25 } // namespace 29 } // namespace
26 30
27 class AnimatedContentSamplerTest : public ::testing::Test { 31 class AnimatedContentSamplerTest : public ::testing::Test {
28 public: 32 public:
29 AnimatedContentSamplerTest() {} 33 AnimatedContentSamplerTest() {}
30 ~AnimatedContentSamplerTest() override {} 34 ~AnimatedContentSamplerTest() override {}
31 35
32 void SetUp() override { 36 void SetUp() override {
33 const base::TimeDelta since_epoch = 37 const base::TimeDelta since_epoch =
34 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch(); 38 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
(...skipping 30 matching lines...) Expand all
65 // ElectMajorityDamageRect(). 69 // ElectMajorityDamageRect().
66 void ObserveDamageRect(const gfx::Rect& damage_rect) { 70 void ObserveDamageRect(const gfx::Rect& damage_rect) {
67 sampler_->observations_.push_back( 71 sampler_->observations_.push_back(
68 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks())); 72 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
69 } 73 }
70 74
71 gfx::Rect ElectMajorityDamageRect() const { 75 gfx::Rect ElectMajorityDamageRect() const {
72 return sampler_->ElectMajorityDamageRect(); 76 return sampler_->ElectMajorityDamageRect();
73 } 77 }
74 78
79 static base::TimeDelta ComputeSamplingPeriod(
80 base::TimeDelta detected_period,
81 base::TimeDelta target_sampling_period,
82 base::TimeDelta min_capture_period) {
83 return AnimatedContentSampler::ComputeSamplingPeriod(
84 detected_period, target_sampling_period, min_capture_period);
85 }
86
75 private: 87 private:
76 // Note: Not using base::RandInt() because it is horribly slow on debug 88 // Note: Not using base::RandInt() because it is horribly slow on debug
77 // builds. The following is a very simple, deterministic LCG: 89 // builds. The following is a very simple, deterministic LCG:
78 int NextRandomInt() { 90 int NextRandomInt() {
79 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31); 91 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
80 return rand_seed_; 92 return rand_seed_;
81 } 93 }
82 94
83 int rand_seed_; 95 int rand_seed_;
84 scoped_ptr<AnimatedContentSampler> sampler_; 96 scoped_ptr<AnimatedContentSampler> sampler_;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 ObserveDamageRect(spinner_rect); 167 ObserveDamageRect(spinner_rect);
156 ObserveDamageRect(video_rect); 168 ObserveDamageRect(video_rect);
157 ObserveDamageRect(spinner_rect); 169 ObserveDamageRect(spinner_rect);
158 ObserveDamageRect(spinner_rect); 170 ObserveDamageRect(spinner_rect);
159 ObserveDamageRect(video_rect); 171 ObserveDamageRect(video_rect);
160 ObserveDamageRect(spinner_rect); 172 ObserveDamageRect(spinner_rect);
161 } 173 }
162 EXPECT_EQ(video_rect, ElectMajorityDamageRect()); 174 EXPECT_EQ(video_rect, ElectMajorityDamageRect());
163 } 175 }
164 176
177 TEST_F(AnimatedContentSamplerTest, TargetsSamplingPeriod) {
178 const base::TimeDelta min_capture_period = FpsAsPeriod(60);
179 const int target_fps = 30;
180 const base::TimeDelta target_sampling_period = FpsAsPeriod(target_fps);
181
182 for (int content_fps = 1; content_fps <= 60; ++content_fps) {
183 const base::TimeDelta content_period = FpsAsPeriod(content_fps);
184 const base::TimeDelta sampling_period =
185 ComputeSamplingPeriod(content_period,
186 target_sampling_period,
187 min_capture_period);
188 if (content_period >= target_sampling_period) {
189 ASSERT_EQ(content_period, sampling_period);
190 } else {
191 ASSERT_LE(min_capture_period, sampling_period);
192
193 // Check that the sampling rate is as close (or closer) to the target
194 // sampling rate than any integer-subsampling of the content frame rate.
195 const double absolute_diff =
196 std::abs(1.0 / sampling_period.InSecondsF() - target_fps);
197 const double fudge_for_acceptable_rounding_error = 0.005;
198 for (double divisor = 1; divisor < 4; ++divisor) {
199 SCOPED_TRACE(::testing::Message() << "content_fps=" << content_fps
200 << ", divisor=" << divisor);
201 ASSERT_GE(std::abs(content_fps / divisor - target_fps),
202 absolute_diff - fudge_for_acceptable_rounding_error);
203 }
204 }
205 }
206 }
207
165 namespace { 208 namespace {
166 209
167 // A test scenario for AnimatedContentSamplerParameterizedTest. 210 // A test scenario for AnimatedContentSamplerParameterizedTest.
168 struct Scenario { 211 struct Scenario {
169 base::TimeDelta vsync_interval; // Reflects compositor's update rate. 212 base::TimeDelta vsync_interval; // Reflects compositor's update rate.
170 base::TimeDelta min_capture_period; // Reflects maximum capture rate. 213 base::TimeDelta min_capture_period; // Reflects maximum capture rate.
171 base::TimeDelta content_period; // Reflects content animation rate. 214 base::TimeDelta content_period; // Reflects content animation rate.
215 base::TimeDelta target_sampling_period;
172 216
173 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c) 217 Scenario(int compositor_frequency,
174 : vsync_interval(v), min_capture_period(m), content_period(c) { 218 int max_frame_rate,
219 int content_frame_rate)
220 : vsync_interval(FpsAsPeriod(compositor_frequency)),
221 min_capture_period(FpsAsPeriod(max_frame_rate)),
222 content_period(FpsAsPeriod(content_frame_rate)) {
175 CHECK(content_period >= vsync_interval) 223 CHECK(content_period >= vsync_interval)
176 << "Bad test params: Impossible to animate faster than the compositor."; 224 << "Bad test params: Impossible to animate faster than the compositor.";
177 } 225 }
226
227 Scenario(int compositor_frequency,
228 int max_frame_rate,
229 int content_frame_rate,
230 int target_sampling_rate)
231 : vsync_interval(FpsAsPeriod(compositor_frequency)),
232 min_capture_period(FpsAsPeriod(max_frame_rate)),
233 content_period(FpsAsPeriod(content_frame_rate)),
234 target_sampling_period(FpsAsPeriod(target_sampling_rate)) {
235 CHECK(content_period >= vsync_interval)
236 << "Bad test params: Impossible to animate faster than the compositor.";
237 }
178 }; 238 };
179 239
180 // Value printer for Scenario. 240 // Value printer for Scenario.
181 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) { 241 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
182 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds() 242 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
183 << ", min_capture_period=" << s.min_capture_period.InMicroseconds() 243 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
184 << ", content_period=" << s.content_period.InMicroseconds() 244 << ", content_period=" << s.content_period.InMicroseconds()
185 << " }"; 245 << " }";
186 } 246 }
187 247
188 base::TimeDelta FpsAsPeriod(int frame_rate) {
189 return base::TimeDelta::FromSeconds(1) / frame_rate;
190 }
191
192 } // namespace 248 } // namespace
193 249
194 class AnimatedContentSamplerParameterizedTest 250 class AnimatedContentSamplerParameterizedTest
195 : public AnimatedContentSamplerTest, 251 : public AnimatedContentSamplerTest,
196 public ::testing::WithParamInterface<Scenario> { 252 public ::testing::WithParamInterface<Scenario> {
197 public: 253 public:
198 AnimatedContentSamplerParameterizedTest() 254 AnimatedContentSamplerParameterizedTest()
199 : count_dropped_frames_(0), count_sampled_frames_(0) {} 255 : count_dropped_frames_(0), count_sampled_frames_(0) {}
200 virtual ~AnimatedContentSamplerParameterizedTest() {} 256 virtual ~AnimatedContentSamplerParameterizedTest() {}
201 257
258 void SetUp() override {
259 AnimatedContentSamplerTest::SetUp();
260 sampler()->SetTargetSamplingPeriod(GetParam().target_sampling_period);
261 }
262
202 protected: 263 protected:
203 typedef std::pair<gfx::Rect, base::TimeTicks> Event; 264 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
204 265
205 base::TimeDelta GetMinCapturePeriod() const override { 266 base::TimeDelta GetMinCapturePeriod() const override {
206 return GetParam().min_capture_period; 267 return GetParam().min_capture_period;
207 } 268 }
208 269
270 base::TimeDelta ComputeExpectedSamplingPeriod() const {
271 return AnimatedContentSamplerTest::ComputeSamplingPeriod(
272 GetParam().content_period,
273 GetParam().target_sampling_period,
274 GetParam().min_capture_period);
275 }
276
209 // Generate a sequence of events from the compositor pipeline. The event 277 // Generate a sequence of events from the compositor pipeline. The event
210 // times will all be at compositor vsync boundaries. 278 // times will all be at compositor vsync boundaries.
211 std::vector<Event> GenerateEventSequence(base::TimeTicks begin, 279 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
212 base::TimeTicks end, 280 base::TimeTicks end,
213 bool include_content_frame_events, 281 bool include_content_frame_events,
214 bool include_random_events) { 282 bool include_random_events,
283 base::TimeTicks* next_begin_time) {
215 DCHECK(GetParam().content_period >= GetParam().vsync_interval); 284 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
216 base::TimeTicks next_content_time = begin - GetParam().content_period; 285 base::TimeTicks next_content_time = begin;
217 std::vector<Event> events; 286 std::vector<Event> events;
218 for (base::TimeTicks compositor_time = begin; compositor_time < end; 287 base::TimeTicks compositor_time;
288 for (compositor_time = begin; compositor_time < end;
219 compositor_time += GetParam().vsync_interval) { 289 compositor_time += GetParam().vsync_interval) {
220 if (include_content_frame_events && next_content_time < compositor_time) { 290 if (next_content_time <= compositor_time) {
221 events.push_back(Event(GetContentDamageRect(), compositor_time));
222 next_content_time += GetParam().content_period; 291 next_content_time += GetParam().content_period;
223 } else if (include_random_events && GetRandomInRange(0, 1) == 0) { 292 if (include_content_frame_events) {
293 events.push_back(Event(GetContentDamageRect(), compositor_time));
294 continue;
295 }
296 }
297 if (include_random_events && GetRandomInRange(0, 1) == 0) {
224 events.push_back(Event(GetRandomDamageRect(), compositor_time)); 298 events.push_back(Event(GetRandomDamageRect(), compositor_time));
225 } 299 }
226 } 300 }
227 301
302 if (next_begin_time) {
303 while (compositor_time < next_content_time)
304 compositor_time += GetParam().vsync_interval;
305 *next_begin_time = compositor_time;
306 }
307
228 DCHECK(!events.empty()); 308 DCHECK(!events.empty());
229 return events; 309 return events;
230 } 310 }
231 311
232 // Feed |events| through the sampler, and detect whether the expected 312 // Feed |events| through the sampler, and detect whether the expected
233 // lock-in/out transition occurs. Also, track and measure the frame drop 313 // lock-in/out transition occurs. Also, track and measure the frame drop
234 // ratio and check it against the expected drop rate. 314 // ratio and check it against the expected drop rate.
235 void RunEventSequence(const std::vector<Event> events, 315 void RunEventSequence(const std::vector<Event> events,
236 bool was_detecting_before, 316 bool was_detecting_before,
237 bool is_detecting_after, 317 bool is_detecting_after,
238 bool simulate_pipeline_back_pressure) { 318 bool simulate_pipeline_back_pressure,
319 const char* description) {
320 SCOPED_TRACE(::testing::Message() << "Description: " << description);
321
239 gfx::Rect first_detected_region; 322 gfx::Rect first_detected_region;
240 323
241 EXPECT_EQ(was_detecting_before, sampler()->HasProposal()); 324 EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
242 bool has_detection_switched = false; 325 bool has_detection_switched = false;
326 bool has_detection_flip_flopped_once = false;
243 ResetFrameCounters(); 327 ResetFrameCounters();
244 for (std::vector<Event>::const_iterator i = events.begin(); 328 for (std::vector<Event>::const_iterator i = events.begin();
245 i != events.end(); ++i) { 329 i != events.end(); ++i) {
246 sampler()->ConsiderPresentationEvent(i->first, i->second); 330 sampler()->ConsiderPresentationEvent(i->first, i->second);
247 331
248 // Detect when the sampler locks in/out, and that it stays that way for 332 // Detect when the sampler locks in/out, and that it stays that way for
249 // all further iterations of this loop. 333 // all further iterations of this loop. It is permissible for the lock-in
334 // to flip-flop once, but no more than that.
250 if (!has_detection_switched && 335 if (!has_detection_switched &&
251 was_detecting_before != sampler()->HasProposal()) { 336 was_detecting_before != sampler()->HasProposal()) {
252 has_detection_switched = true; 337 has_detection_switched = true;
338 } else if (has_detection_switched &&
339 is_detecting_after != sampler()->HasProposal()) {
340 ASSERT_FALSE(has_detection_flip_flopped_once);
341 has_detection_flip_flopped_once = true;
342 has_detection_switched = false;
253 } 343 }
254 ASSERT_EQ( 344 ASSERT_EQ(
255 has_detection_switched ? is_detecting_after : was_detecting_before, 345 has_detection_switched ? is_detecting_after : was_detecting_before,
256 sampler()->HasProposal()); 346 sampler()->HasProposal());
257 347
258 if (sampler()->HasProposal()) { 348 if (sampler()->HasProposal()) {
259 // Make sure the sampler doesn't flip-flop and keep proposing sampling 349 // Make sure the sampler doesn't flip-flop and keep proposing sampling
260 // based on locking into different regions. 350 // based on locking into different regions.
261 if (first_detected_region.IsEmpty()) { 351 if (first_detected_region.IsEmpty()) {
262 first_detected_region = sampler()->detected_region(); 352 first_detected_region = sampler()->detected_region();
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
307 } 397 }
308 } 398 }
309 399
310 // Confirm the AnimatedContentSampler is not dropping more frames than 400 // Confirm the AnimatedContentSampler is not dropping more frames than
311 // expected, given current test parameters. 401 // expected, given current test parameters.
312 void ExpectFrameDropRatioIsCorrect() { 402 void ExpectFrameDropRatioIsCorrect() {
313 if (count_sampled_frames_ == 0) { 403 if (count_sampled_frames_ == 0) {
314 EXPECT_EQ(0, count_dropped_frames_); 404 EXPECT_EQ(0, count_dropped_frames_);
315 return; 405 return;
316 } 406 }
317 const double content_framerate = 407 const double expected_sampling_ratio =
318 1000000.0 / GetParam().content_period.InMicroseconds(); 408 GetParam().content_period.InSecondsF() /
319 const double capture_framerate = 409 ComputeExpectedSamplingPeriod().InSecondsF();
320 1000000.0 / GetParam().min_capture_period.InMicroseconds(); 410 const int total_frames = count_dropped_frames_ + count_sampled_frames_;
321 const double expected_drop_rate = std::max( 411 EXPECT_NEAR(total_frames * expected_sampling_ratio,
322 0.0, (content_framerate - capture_framerate) / capture_framerate); 412 count_sampled_frames_,
323 const double actual_drop_rate = 413 1.5);
324 static_cast<double>(count_dropped_frames_) / count_sampled_frames_; 414 EXPECT_NEAR(total_frames * (1.0 - expected_sampling_ratio),
325 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015); 415 count_dropped_frames_,
416 1.5);
326 } 417 }
327 418
328 private: 419 private:
329 // These counters only include the frames with the desired content. 420 // These counters only include the frames with the desired content.
330 int count_dropped_frames_; 421 int count_dropped_frames_;
331 int count_sampled_frames_; 422 int count_sampled_frames_;
332 }; 423 };
333 424
334 // Tests that the implementation locks in/out of frames containing stable 425 // Tests that the implementation locks in/out of frames containing stable
335 // animated content, whether or not random events are also simultaneously 426 // animated content, whether or not random events are also simultaneously
336 // present. 427 // present.
337 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) { 428 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
338 // |begin| refers to the start of an event sequence in terms of the 429 // |begin| refers to the start of an event sequence in terms of the
339 // Compositor's clock. 430 // Compositor's clock.
340 base::TimeTicks begin = InitialTestTimeTicks(); 431 base::TimeTicks begin = InitialTestTimeTicks();
341 432
342 // Provide random events and expect no lock-in. 433 // Provide random events and expect no lock-in.
343 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5); 434 RunEventSequence(
344 RunEventSequence(GenerateEventSequence(begin, end, false, true), 435 GenerateEventSequence(begin,
345 false, 436 begin + base::TimeDelta::FromSeconds(5),
346 false, 437 false,
347 false); 438 true,
348 begin = end; 439 &begin),
440 false,
441 false,
442 false,
443 "Provide random events and expect no lock-in.");
444 if (HasFailure())
445 return;
349 446
350 // Provide content frame events with some random events mixed-in, and expect 447 // Provide content frame events with some random events mixed-in, and expect
351 // the sampler to lock-in. 448 // the sampler to lock-in.
352 end = begin + base::TimeDelta::FromSeconds(5); 449 RunEventSequence(
353 RunEventSequence(GenerateEventSequence(begin, end, true, true), 450 GenerateEventSequence(begin,
354 false, 451 begin + base::TimeDelta::FromSeconds(5),
355 true, 452 true,
356 false); 453 true,
357 begin = end; 454 &begin),
455 false,
456 true,
457 false,
458 "Provide content frame events with some random events mixed-in, and "
459 "expect the sampler to lock-in.");
460 if (HasFailure())
461 return;
358 462
359 // Continue providing content frame events without the random events mixed-in 463 // Continue providing content frame events without the random events mixed-in
360 // and expect the lock-in to hold. 464 // and expect the lock-in to hold.
361 end = begin + base::TimeDelta::FromSeconds(5); 465 RunEventSequence(
362 RunEventSequence(GenerateEventSequence(begin, end, true, false), 466 GenerateEventSequence(begin,
363 true, 467 begin + base::TimeDelta::FromSeconds(5),
364 true, 468 true,
365 false); 469 false,
366 begin = end; 470 &begin),
471 true,
472 true,
473 false,
474 "Continue providing content frame events without the random events "
475 "mixed-in and expect the lock-in to hold.");
476 if (HasFailure())
477 return;
367 478
368 // Continue providing just content frame events and expect the lock-in to 479 // Continue providing just content frame events and expect the lock-in to
369 // hold. Also simulate the capture pipeline experiencing back pressure. 480 // hold. Also simulate the capture pipeline experiencing back pressure.
370 end = begin + base::TimeDelta::FromSeconds(20); 481 RunEventSequence(
371 RunEventSequence(GenerateEventSequence(begin, end, true, false), 482 GenerateEventSequence(begin,
372 true, 483 begin + base::TimeDelta::FromSeconds(20),
373 true, 484 true,
374 true); 485 false,
375 begin = end; 486 &begin),
487 true,
488 true,
489 true,
490 "Continue providing just content frame events and expect the lock-in to "
491 "hold. Also simulate the capture pipeline experiencing back pressure.");
492 if (HasFailure())
493 return;
494
376 495
377 // Provide a half-second of random events only, and expect the lock-in to be 496 // Provide a half-second of random events only, and expect the lock-in to be
378 // broken. 497 // broken.
379 end = begin + base::TimeDelta::FromMilliseconds(500); 498 RunEventSequence(
380 RunEventSequence(GenerateEventSequence(begin, end, false, true), 499 GenerateEventSequence(begin,
381 true, 500 begin + base::TimeDelta::FromMilliseconds(500),
382 false, 501 false,
383 false); 502 true,
384 begin = end; 503 &begin),
504 true,
505 false,
506 false,
507 "Provide a half-second of random events only, and expect the lock-in to "
508 "be broken.");
509 if (HasFailure())
510 return;
385 511
386 // Now, go back to providing content frame events, and expect the sampler to 512 // Now, go back to providing content frame events, and expect the sampler to
387 // lock-in once again. 513 // lock-in once again.
388 end = begin + base::TimeDelta::FromSeconds(5); 514 RunEventSequence(
389 RunEventSequence(GenerateEventSequence(begin, end, true, false), 515 GenerateEventSequence(begin,
390 false, 516 begin + base::TimeDelta::FromSeconds(5),
391 true, 517 true,
392 false); 518 false,
393 begin = end; 519 &begin),
520 false,
521 true,
522 false,
523 "Now, go back to providing content frame events, and expect the sampler "
524 "to lock-in once again.");
394 } 525 }
395 526
396 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between, 527 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
397 // two animations of the same pixel change rate. VideoCaptureOracle should 528 // two animations of the same pixel change rate. VideoCaptureOracle should
398 // revert to using the SmoothEventSampler for these kinds of situations, as 529 // revert to using the SmoothEventSampler for these kinds of situations, as
399 // there is no "right answer" as to which animation to lock into. 530 // there is no "right answer" as to which animation to lock into.
400 TEST_P(AnimatedContentSamplerParameterizedTest, 531 TEST_P(AnimatedContentSamplerParameterizedTest,
401 DoesNotLockInToTwoCompetingAnimations) { 532 DoesNotLockInToTwoCompetingAnimations) {
402 // Don't test when the event stream cannot indicate two separate content 533 // Don't test when the event stream cannot indicate two separate content
403 // animations under the current test parameters. 534 // animations under the current test parameters.
404 if (GetParam().content_period < 2 * GetParam().vsync_interval) 535 if (GetParam().content_period < 2 * GetParam().vsync_interval)
405 return; 536 return;
406 537
407 // Start the first animation and run for a bit, and expect the sampler to 538 // Start the first animation and run for a bit, and expect the sampler to
408 // lock-in. 539 // lock-in.
409 base::TimeTicks begin = InitialTestTimeTicks(); 540 base::TimeTicks begin = InitialTestTimeTicks();
410 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5); 541 RunEventSequence(
411 RunEventSequence(GenerateEventSequence(begin, end, true, false), 542 GenerateEventSequence(begin,
412 false, 543 begin + base::TimeDelta::FromSeconds(5),
413 true, 544 true,
414 false); 545 false,
415 begin = end; 546 &begin),
547 false,
548 true,
549 false,
550 "Start the first animation and run for a bit, and expect the sampler to "
551 "lock-in.");
552 if (HasFailure())
553 return;
416 554
417 // Now, keep the first animation and blend in an second animation of the same 555 // Now, keep the first animation and blend in a second animation of the same
418 // size and frame rate, but at a different position. This will should cause 556 // size and frame rate, but at a different position. This will should cause
419 // the sampler to enter an "undetected" state since it's unclear which 557 // the sampler to enter an "undetected" state since it's unclear which
420 // animation should be locked into. 558 // animation should be locked into.
421 end = begin + base::TimeDelta::FromSeconds(20);
422 std::vector<Event> first_animation_events = 559 std::vector<Event> first_animation_events =
423 GenerateEventSequence(begin, end, true, false); 560 GenerateEventSequence(begin,
561 begin + base::TimeDelta::FromSeconds(20),
562 true,
563 false,
564 &begin);
424 gfx::Rect second_animation_rect( 565 gfx::Rect second_animation_rect(
425 gfx::Point(0, GetContentDamageRect().height()), 566 gfx::Point(0, GetContentDamageRect().height()),
426 GetContentDamageRect().size()); 567 GetContentDamageRect().size());
427 std::vector<Event> both_animations_events; 568 std::vector<Event> both_animations_events;
428 base::TimeDelta second_animation_offset = GetParam().vsync_interval; 569 base::TimeDelta second_animation_offset = GetParam().vsync_interval;
429 for (std::vector<Event>::const_iterator i = first_animation_events.begin(); 570 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
430 i != first_animation_events.end(); ++i) { 571 i != first_animation_events.end(); ++i) {
431 both_animations_events.push_back(*i); 572 both_animations_events.push_back(*i);
432 both_animations_events.push_back( 573 both_animations_events.push_back(
433 Event(second_animation_rect, i->second + second_animation_offset)); 574 Event(second_animation_rect, i->second + second_animation_offset));
434 } 575 }
435 RunEventSequence(both_animations_events, true, false, false); 576 RunEventSequence(
436 begin = end; 577 both_animations_events, true, false, false,
578 "Now, blend-in a second animation of the same size and frame rate, but "
579 "at a different position.");
580 if (HasFailure())
581 return;
437 582
438 // Now, run just the first animation, and expect the sampler to lock-in once 583 // Now, run just the first animation, and expect the sampler to lock-in once
439 // again. 584 // again.
440 end = begin + base::TimeDelta::FromSeconds(5); 585 RunEventSequence(
441 RunEventSequence(GenerateEventSequence(begin, end, true, false), 586 GenerateEventSequence(begin,
442 false, 587 begin + base::TimeDelta::FromSeconds(5),
443 true, 588 true,
444 false); 589 false,
445 begin = end; 590 &begin),
591 false,
592 true,
593 false,
594 "Now, run just the first animation, and expect the sampler to lock-in "
595 "once again.");
596 if (HasFailure())
597 return;
446 598
447 // Now, blend in the second animation again, but it has half the frame rate of 599 // Now, blend in the second animation again, but it has half the frame rate of
448 // the first animation and damage Rects with twice the area. This will should 600 // the first animation and damage Rects with twice the area. This will should
449 // cause the sampler to enter an "undetected" state again. This tests that 601 // cause the sampler to enter an "undetected" state again. This tests that
450 // pixel-weighting is being accounted for in the sampler's logic. 602 // pixel-weighting is being accounted for in the sampler's logic.
451 end = begin + base::TimeDelta::FromSeconds(20); 603 first_animation_events =
452 first_animation_events = GenerateEventSequence(begin, end, true, false); 604 GenerateEventSequence(begin,
605 begin + base::TimeDelta::FromSeconds(20),
606 true,
607 false,
608 &begin);
453 second_animation_rect.set_width(second_animation_rect.width() * 2); 609 second_animation_rect.set_width(second_animation_rect.width() * 2);
454 both_animations_events.clear(); 610 both_animations_events.clear();
455 bool include_second_animation_frame = true; 611 bool include_second_animation_frame = true;
456 for (std::vector<Event>::const_iterator i = first_animation_events.begin(); 612 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
457 i != first_animation_events.end(); ++i) { 613 i != first_animation_events.end(); ++i) {
458 both_animations_events.push_back(*i); 614 both_animations_events.push_back(*i);
459 if (include_second_animation_frame) { 615 if (include_second_animation_frame) {
460 both_animations_events.push_back( 616 both_animations_events.push_back(
461 Event(second_animation_rect, i->second + second_animation_offset)); 617 Event(second_animation_rect, i->second + second_animation_offset));
462 } 618 }
463 include_second_animation_frame = !include_second_animation_frame; 619 include_second_animation_frame = !include_second_animation_frame;
464 } 620 }
465 RunEventSequence(both_animations_events, true, false, false); 621 RunEventSequence(
466 begin = end; 622 both_animations_events, true, false, false,
623 "Now, blend in the second animation again, but it has half the frame "
624 "rate of the first animation and damage Rects with twice the area.");
467 } 625 }
468 626
469 // Tests that the frame timestamps are smooth; meaning, that when run through a 627 // Tests that the frame timestamps are smooth; meaning, that when run through a
470 // simulated compositor, each frame is held displayed for the right number of 628 // simulated compositor, each frame is held displayed for the right number of
471 // v-sync intervals. 629 // v-sync intervals.
472 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) { 630 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
473 // Generate 30 seconds of animated content events, run the events through 631 // Generate 30 seconds of animated content events, run the events through
474 // AnimatedContentSampler, and record all frame timestamps being proposed 632 // AnimatedContentSampler, and record all frame timestamps being proposed
475 // once lock-in is continuous. 633 // once lock-in is continuous.
476 base::TimeTicks begin = InitialTestTimeTicks(); 634 const base::TimeTicks begin = InitialTestTimeTicks();
477 std::vector<Event> events = GenerateEventSequence( 635 std::vector<Event> events = GenerateEventSequence(
478 begin, 636 begin,
479 begin + base::TimeDelta::FromSeconds(20), 637 begin + base::TimeDelta::FromSeconds(20),
480 true, 638 true,
481 false); 639 false,
640 nullptr);
482 typedef std::vector<base::TimeTicks> Timestamps; 641 typedef std::vector<base::TimeTicks> Timestamps;
483 Timestamps frame_timestamps; 642 Timestamps frame_timestamps;
484 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end(); 643 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
485 ++i) { 644 ++i) {
486 sampler()->ConsiderPresentationEvent(i->first, i->second); 645 sampler()->ConsiderPresentationEvent(i->first, i->second);
487 if (sampler()->HasProposal()) { 646 if (sampler()->HasProposal()) {
488 if (sampler()->ShouldSample()) { 647 if (sampler()->ShouldSample()) {
489 frame_timestamps.push_back(sampler()->frame_timestamp()); 648 frame_timestamps.push_back(sampler()->frame_timestamp());
490 sampler()->RecordSample(sampler()->frame_timestamp()); 649 sampler()->RecordSample(sampler()->frame_timestamp());
491 } 650 }
492 } else { 651 } else {
493 frame_timestamps.clear(); // Reset until continuous lock-in. 652 frame_timestamps.clear(); // Reset until continuous lock-in.
494 } 653 }
495 } 654 }
496 ASSERT_LE(2u, frame_timestamps.size()); 655 ASSERT_LE(2u, frame_timestamps.size());
497 656
498 // Iterate through the |frame_timestamps|, building a histogram counting the 657 // Iterate through the |frame_timestamps|, building a histogram counting the
499 // number of times each frame was displayed k times. For example, 10 frames 658 // number of times each frame was displayed k times. For example, 10 frames
500 // of 30 Hz content on a 60 Hz v-sync interval should result in 659 // of 30 Hz content on a 60 Hz v-sync interval should result in
501 // display_counts[2] == 10. Quit early if any one frame was obviously 660 // display_counts[2] == 10. Quit early if any one frame was obviously
502 // repeated too many times. 661 // repeated too many times.
503 const int64 max_expected_repeats_per_frame = 1 + 662 const int64 max_expected_repeats_per_frame = 1 +
504 std::max(GetParam().min_capture_period, GetParam().content_period) / 663 ComputeExpectedSamplingPeriod() / GetParam().vsync_interval;
505 GetParam().vsync_interval;
506 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0); 664 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
507 base::TimeTicks last_present_time = frame_timestamps.front(); 665 base::TimeTicks last_present_time = frame_timestamps.front();
508 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1; 666 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
509 i != frame_timestamps.end(); ++i) { 667 i != frame_timestamps.end(); ++i) {
510 const size_t num_vsync_intervals = static_cast<size_t>( 668 const size_t num_vsync_intervals = static_cast<size_t>(
511 (*i - last_present_time) / GetParam().vsync_interval); 669 (*i - last_present_time) / GetParam().vsync_interval);
512 ASSERT_LT(0u, num_vsync_intervals); 670 ASSERT_LT(0u, num_vsync_intervals);
513 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early. 671 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
514 ++display_counts[num_vsync_intervals]; 672 ++display_counts[num_vsync_intervals];
515 last_present_time += num_vsync_intervals * GetParam().vsync_interval; 673 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
(...skipping 18 matching lines...) Expand all
534 } 692 }
535 } 693 }
536 size_t stray_count_remaining = 694 size_t stray_count_remaining =
537 (frame_timestamps.size() - 1) - (highest_count + second_highest_count); 695 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
538 // Expect no more than 0.75% of frames fall outside the two main buckets. 696 // Expect no more than 0.75% of frames fall outside the two main buckets.
539 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining); 697 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
540 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) { 698 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
541 if (display_counts[repeats] == highest_count) { 699 if (display_counts[repeats] == highest_count) {
542 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]); 700 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
543 ++repeats; 701 ++repeats;
544 } else if (display_counts[repeats] == second_highest_count) { 702 } else if (second_highest_count > 0 &&
703 display_counts[repeats] == second_highest_count) {
545 EXPECT_EQ(highest_count, display_counts[repeats + 1]); 704 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
546 ++repeats; 705 ++repeats;
547 } else { 706 } else {
548 EXPECT_GE(stray_count_remaining, display_counts[repeats]); 707 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
549 stray_count_remaining -= display_counts[repeats]; 708 stray_count_remaining -= display_counts[repeats];
550 } 709 }
551 } 710 }
552 } 711 }
553 712
554 // Tests that frame timestamps are "lightly pushed" back towards the original 713 // Tests that frame timestamps are "lightly pushed" back towards the original
555 // presentation event times, which tells us the AnimatedContentSampler can 714 // presentation event times, which tells us the AnimatedContentSampler can
556 // account for sources of timestamp drift and correct the drift. 715 // account for sources of timestamp drift and correct the drift.
557 TEST_P(AnimatedContentSamplerParameterizedTest, 716 TEST_P(AnimatedContentSamplerParameterizedTest,
558 FrameTimestampsConvergeTowardsEventTimes) { 717 FrameTimestampsConvergeTowardsEventTimes) {
559 const int max_drift_increment_millis = 3; 718 const int max_drift_increment_millis = 3;
560 719
561 // Generate a full minute of events. 720 // Generate a full minute of events.
562 const base::TimeTicks begin = InitialTestTimeTicks(); 721 const base::TimeTicks begin = InitialTestTimeTicks();
563 const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1); 722 std::vector<Event> events =
564 std::vector<Event> events = GenerateEventSequence(begin, end, true, false); 723 GenerateEventSequence(begin,
724 begin + base::TimeDelta::FromMinutes(1),
725 true,
726 false,
727 nullptr);
565 728
566 // Modify the event sequence so that 1-3 ms of additional drift is suddenly 729 // Modify the event sequence so that 1-3 ms of additional drift is suddenly
567 // present every 100 events. This is meant to simulate that, external to 730 // present every 100 events. This is meant to simulate that, external to
568 // AnimatedContentSampler, the video hardware vsync timebase is being 731 // AnimatedContentSampler, the video hardware vsync timebase is being
569 // refreshed and is showing severe drift from the system clock. 732 // refreshed and is showing severe drift from the system clock.
570 base::TimeDelta accumulated_drift; 733 base::TimeDelta accumulated_drift;
571 for (size_t i = 1; i < events.size(); ++i) { 734 for (size_t i = 1; i < events.size(); ++i) {
572 if (i % 100 == 0) { 735 if (i % 100 == 0) {
573 accumulated_drift += base::TimeDelta::FromMilliseconds( 736 accumulated_drift += base::TimeDelta::FromMilliseconds(
574 GetRandomInRange(1, max_drift_increment_millis + 1)); 737 GetRandomInRange(1, max_drift_increment_millis + 1));
(...skipping 21 matching lines...) Expand all
596 total_error.InMicroseconds(), 759 total_error.InMicroseconds(),
597 max_acceptable_error.InMicroseconds()); 760 max_acceptable_error.InMicroseconds());
598 } 761 }
599 762
600 INSTANTIATE_TEST_CASE_P( 763 INSTANTIATE_TEST_CASE_P(
601 , 764 ,
602 AnimatedContentSamplerParameterizedTest, 765 AnimatedContentSamplerParameterizedTest,
603 ::testing::Values( 766 ::testing::Values(
604 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30 767 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
605 // Hz, and content video animates at 30, 25, or 24 Hz. 768 // Hz, and content video animates at 30, 25, or 24 Hz.
606 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)), 769 Scenario(60, 30, 30),
607 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)), 770 Scenario(60, 30, 25),
608 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)), 771 Scenario(60, 30, 24),
609 772
610 // High frame rate content that leverages the Compositor's 773 // High frame rate content that leverages the Compositor's
611 // capabilities, but capture is still at 30 Hz. 774 // capabilities, but capture is still at 30 Hz.
612 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)), 775 Scenario(60, 30, 60),
613 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)), 776 Scenario(60, 30, 50),
614 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)), 777 Scenario(60, 30, 48),
615 778
616 // High frame rate content that leverages the Compositor's 779 // High frame rate content that leverages the Compositor's
617 // capabilities, and capture is also a buttery 60 Hz. 780 // capabilities, and capture is also a buttery 60 Hz.
618 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)), 781 Scenario(60, 60, 60),
619 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)), 782 Scenario(60, 60, 50),
620 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)), 783 Scenario(60, 60, 48),
784
785 // High frame rate content that leverages the Compositor's
786 // capabilities, but the client has disabled HFR sampling.
787 Scenario(60, 60, 60, 30),
788 Scenario(60, 60, 50, 30),
789 Scenario(60, 60, 48, 30),
621 790
622 // On some platforms, the Compositor runs at 50 Hz. 791 // On some platforms, the Compositor runs at 50 Hz.
623 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)), 792 Scenario(50, 30, 30),
624 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)), 793 Scenario(50, 30, 25),
625 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)), 794 Scenario(50, 30, 24),
626 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)), 795 Scenario(50, 30, 50),
627 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)), 796 Scenario(50, 30, 48),
628 797
629 // Stable, but non-standard content frame rates. 798 // Stable, but non-standard content frame rates.
630 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)), 799 Scenario(60, 30, 16),
631 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)), 800 Scenario(60, 30, 20),
632 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)), 801 Scenario(60, 30, 23),
633 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)), 802 Scenario(60, 30, 26),
634 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)), 803 Scenario(60, 30, 27),
635 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)), 804 Scenario(60, 30, 28),
636 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)), 805 Scenario(60, 30, 29),
637 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)), 806 Scenario(60, 30, 31),
638 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)), 807 Scenario(60, 30, 32),
639 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33)))); 808 Scenario(60, 30, 33)));
640 809
641 } // namespace content 810 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698