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

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

Issue 418283003: "Buttery Smooth" Tab Capture. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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/video_capture_oracle.h" 5 #include "content/browser/media/capture/video_capture_oracle.h"
6 6
7 #include <cstdlib>
8 #include <utility>
9 #include <vector>
10
11 #include "base/logging.h"
7 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
8 #include "base/time/time.h" 13 #include "base/time/time.h"
9 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h"
10 16
11 namespace content { 17 namespace content {
12 namespace { 18 namespace {
13 19
14 void SteadyStateSampleAndAdvance(base::TimeDelta vsync, 20 void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
15 SmoothEventSampler* sampler, 21 SmoothEventSampler* sampler,
16 base::TimeTicks* t) { 22 base::TimeTicks* t) {
17 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t)); 23 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t));
18 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 24 ASSERT_TRUE(sampler->HasUnrecordedEvent());
19 sampler->RecordSample(); 25 sampler->RecordSample();
20 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 26 ASSERT_FALSE(sampler->HasUnrecordedEvent());
21 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 27 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
22 *t += vsync; 28 *t += vsync;
23 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 29 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
24 } 30 }
25 31
26 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync, 32 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
27 SmoothEventSampler* sampler, 33 SmoothEventSampler* sampler,
28 base::TimeTicks* t) { 34 base::TimeTicks* t) {
29 ASSERT_FALSE(sampler->AddEventAndConsiderSampling(*t)); 35 ASSERT_FALSE(sampler->AddEventAndConsiderSampling(*t));
30 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 36 ASSERT_TRUE(sampler->HasUnrecordedEvent());
31 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 37 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
32 *t += vsync; 38 *t += vsync;
33 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 39 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
34 } 40 }
35 41
36 void TimeTicksFromString(const char* string, base::TimeTicks* t) { 42 base::TimeTicks InitialTestTimeTicks() {
37 base::Time time; 43 base::Time time;
38 ASSERT_TRUE(base::Time::FromString(string, &time)); 44 CHECK(base::Time::FromString("Sat, 23 Mar 2013 1:21:08 GMT", &time));
39 *t = base::TimeTicks::UnixEpoch() + (time - base::Time::UnixEpoch()); 45 return base::TimeTicks::UnixEpoch() + (time - base::Time::UnixEpoch());
40 } 46 }
41 47
42 void TestRedundantCaptureStrategy(base::TimeDelta capture_period, 48 void TestRedundantCaptureStrategy(base::TimeDelta capture_period,
43 int redundant_capture_goal, 49 int redundant_capture_goal,
44 SmoothEventSampler* sampler, 50 SmoothEventSampler* sampler,
45 base::TimeTicks* t) { 51 base::TimeTicks* t) {
46 // Before any events have been considered, we're overdue for sampling. 52 // Before any events have been considered, we're overdue for sampling.
47 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t)); 53 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t));
48 54
49 // Consider the first event. We want to sample that. 55 // Consider the first event. We want to sample that.
50 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 56 ASSERT_FALSE(sampler->HasUnrecordedEvent());
51 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t)); 57 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t));
52 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 58 ASSERT_TRUE(sampler->HasUnrecordedEvent());
53 sampler->RecordSample(); 59 sampler->RecordSample();
54 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 60 ASSERT_FALSE(sampler->HasUnrecordedEvent());
55 61
56 // After more than one capture period has passed without considering an event, 62 // After more than 250 ms has passed without considering an event, we should
57 // we should repeatedly be overdue for sampling. However, once the redundant 63 // repeatedly be overdue for sampling. However, once the redundant capture
58 // capture goal is achieved, we should no longer be overdue for sampling. 64 // goal is achieved, we should no longer be overdue for sampling.
59 *t += capture_period * 4; 65 *t += base::TimeDelta::FromMilliseconds(250);
60 for (int i = 0; i < redundant_capture_goal; i++) { 66 for (int i = 0; i < redundant_capture_goal; i++) {
61 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 67 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
62 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 68 ASSERT_FALSE(sampler->HasUnrecordedEvent());
63 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t)) 69 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t))
64 << "Should sample until redundant capture goal is hit"; 70 << "Should sample until redundant capture goal is hit";
65 sampler->RecordSample(); 71 sampler->RecordSample();
66 *t += capture_period; // Timer fires once every capture period. 72 *t += capture_period; // Timer fires once every capture period.
67 } 73 }
68 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)) 74 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t))
69 << "Should not be overdue once redundant capture goal achieved."; 75 << "Should not be overdue once redundant capture goal achieved.";
70 } 76 }
71 77
72 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains 78 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains
73 // much more comprehensive before/after/edge-case scenarios than the others. 79 // much more comprehensive before/after/edge-case scenarios than the others.
74 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) { 80 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) {
75 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 81 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
76 const int redundant_capture_goal = 200; 82 const int redundant_capture_goal = 200;
77 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60; 83 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60;
78 84
79 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 85 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
80 base::TimeTicks t; 86 base::TimeTicks t = InitialTestTimeTicks();
81 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
82 87
83 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 88 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
84 &sampler, &t); 89 &sampler, &t);
85 90
86 // Steady state, we should capture every other vsync, indefinitely. 91 // Steady state, we should capture every other vsync, indefinitely.
87 for (int i = 0; i < 100; i++) { 92 for (int i = 0; i < 100; i++) {
88 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 93 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
89 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 94 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
90 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 95 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
91 } 96 }
(...skipping 18 matching lines...) Expand all
110 } 115 }
111 } 116 }
112 117
113 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped. 118 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
114 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) { 119 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) {
115 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 120 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
116 const int redundant_capture_goal = 2; 121 const int redundant_capture_goal = 2;
117 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50; 122 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50;
118 123
119 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 124 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
120 base::TimeTicks t; 125 base::TimeTicks t = InitialTestTimeTicks();
121 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
122 126
123 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 127 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
124 &sampler, &t); 128 &sampler, &t);
125 129
126 // Steady state, we should capture 1st, 2nd and 4th frames out of every five 130 // Steady state, we should capture 1st, 2nd and 4th frames out of every five
127 // frames, indefinitely. 131 // frames, indefinitely.
128 for (int i = 0; i < 100; i++) { 132 for (int i = 0; i < 100; i++) {
129 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 133 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
130 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 134 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
131 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 135 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
(...skipping 24 matching lines...) Expand all
156 } 160 }
157 } 161 }
158 162
159 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped. 163 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
160 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) { 164 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) {
161 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 165 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
162 const int redundant_capture_goal = 32; 166 const int redundant_capture_goal = 32;
163 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75; 167 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75;
164 168
165 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 169 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
166 base::TimeTicks t; 170 base::TimeTicks t = InitialTestTimeTicks();
167 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
168 171
169 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 172 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
170 &sampler, &t); 173 &sampler, &t);
171 174
172 // Steady state, we should capture 1st and 3rd frames out of every five 175 // Steady state, we should capture 1st and 3rd frames out of every five
173 // frames, indefinitely. 176 // frames, indefinitely.
174 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 177 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
175 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 178 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
176 for (int i = 0; i < 100; i++) { 179 for (int i = 0; i < 100; i++) {
177 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 180 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
(...skipping 28 matching lines...) Expand all
206 } 209 }
207 } 210 }
208 211
209 // 30Hz sampled at 30Hz should produce 30Hz. 212 // 30Hz sampled at 30Hz should produce 30Hz.
210 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) { 213 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) {
211 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 214 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
212 const int redundant_capture_goal = 1; 215 const int redundant_capture_goal = 1;
213 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30; 216 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30;
214 217
215 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 218 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
216 base::TimeTicks t; 219 base::TimeTicks t = InitialTestTimeTicks();
217 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
218 220
219 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 221 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
220 &sampler, &t); 222 &sampler, &t);
221 223
222 // Steady state, we should capture every vsync, indefinitely. 224 // Steady state, we should capture every vsync, indefinitely.
223 for (int i = 0; i < 200; i++) { 225 for (int i = 0; i < 200; i++) {
224 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 226 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
225 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 227 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
226 } 228 }
227 229
(...skipping 14 matching lines...) Expand all
242 } 244 }
243 } 245 }
244 246
245 // 24Hz sampled at 30Hz should produce 24Hz. 247 // 24Hz sampled at 30Hz should produce 24Hz.
246 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) { 248 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) {
247 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 249 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
248 const int redundant_capture_goal = 333; 250 const int redundant_capture_goal = 333;
249 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24; 251 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24;
250 252
251 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 253 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
252 base::TimeTicks t; 254 base::TimeTicks t = InitialTestTimeTicks();
253 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
254 255
255 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 256 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
256 &sampler, &t); 257 &sampler, &t);
257 258
258 // Steady state, we should capture every vsync, indefinitely. 259 // Steady state, we should capture every vsync, indefinitely.
259 for (int i = 0; i < 200; i++) { 260 for (int i = 0; i < 200; i++) {
260 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 261 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
261 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 262 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
262 } 263 }
263 264
(...skipping 12 matching lines...) Expand all
276 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 277 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
277 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 278 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
278 } 279 }
279 } 280 }
280 281
281 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) { 282 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) {
282 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 283 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
283 const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1); 284 const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1);
284 285
285 SmoothEventSampler sampler(capture_period, true, 1); 286 SmoothEventSampler sampler(capture_period, true, 1);
286 base::TimeTicks t; 287 base::TimeTicks t = InitialTestTimeTicks();
287 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
288 288
289 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 289 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t));
290 sampler.RecordSample(); 290 sampler.RecordSample();
291 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t)) 291 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t))
292 << "Sampled last event; should not be dirty."; 292 << "Sampled last event; should not be dirty.";
293 t += overdue_period; 293 t += overdue_period;
294 294
295 // Now simulate 2 events with the same clock value. 295 // Now simulate 2 events with the same clock value.
296 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 296 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t));
297 sampler.RecordSample(); 297 sampler.RecordSample();
298 ASSERT_FALSE(sampler.AddEventAndConsiderSampling(t)) 298 ASSERT_FALSE(sampler.AddEventAndConsiderSampling(t))
299 << "Two events at same time -- expected second not to be sampled."; 299 << "Two events at same time -- expected second not to be sampled.";
300 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period)) 300 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period))
301 << "Second event should dirty the capture state."; 301 << "Second event should dirty the capture state.";
302 sampler.RecordSample(); 302 sampler.RecordSample();
303 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period)); 303 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period));
304 } 304 }
305 305
306 TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) { 306 TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) {
307 const base::TimeDelta timer_interval = base::TimeDelta::FromSeconds(1) / 30; 307 const base::TimeDelta timer_interval = base::TimeDelta::FromSeconds(1) / 30;
308 308
309 SmoothEventSampler should_not_poll(timer_interval, true, 1); 309 SmoothEventSampler should_not_poll(timer_interval, true, 1);
310 SmoothEventSampler should_poll(timer_interval, false, 1); 310 SmoothEventSampler should_poll(timer_interval, false, 1);
311 base::TimeTicks t; 311 base::TimeTicks t = InitialTestTimeTicks();
312 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
313 312
314 // Do one round of the "happy case" where an event was received and 313 // Do one round of the "happy case" where an event was received and
315 // RecordSample() was called by the client. 314 // RecordSample() was called by the client.
316 ASSERT_TRUE(should_not_poll.AddEventAndConsiderSampling(t)); 315 ASSERT_TRUE(should_not_poll.AddEventAndConsiderSampling(t));
317 ASSERT_TRUE(should_poll.AddEventAndConsiderSampling(t)); 316 ASSERT_TRUE(should_poll.AddEventAndConsiderSampling(t));
318 should_not_poll.RecordSample(); 317 should_not_poll.RecordSample();
319 should_poll.RecordSample(); 318 should_poll.RecordSample();
320 319
321 // One time period ahead, neither sampler says we're overdue. 320 // For the following time period, before 250 ms has elapsed, neither sampler
322 for (int i = 0; i < 3; i++) { 321 // says we're overdue.
322 const int non_overdue_intervals = static_cast<int>(
323 base::TimeDelta::FromMilliseconds(250) / timer_interval);
324 for (int i = 0; i < non_overdue_intervals; i++) {
323 t += timer_interval; 325 t += timer_interval;
324 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t)) 326 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
325 << "Sampled last event; should not be dirty."; 327 << "Sampled last event; should not be dirty.";
326 ASSERT_FALSE(should_poll.IsOverdueForSamplingAt(t)) 328 ASSERT_FALSE(should_poll.IsOverdueForSamplingAt(t))
327 << "Dirty interval has not elapsed yet."; 329 << "Dirty interval has not elapsed yet.";
328 } 330 }
329 331
330 // Next time period ahead, both samplers say we're overdue. The non-polling 332 // Next time period ahead, both samplers say we're overdue. The non-polling
331 // sampler is returning true here because it has been configured to allow one 333 // sampler is returning true here because it has been configured to allow one
332 // redundant capture. 334 // redundant capture.
333 t += timer_interval; 335 t += timer_interval; // Step past the 250 ms threshold.
334 ASSERT_TRUE(should_not_poll.IsOverdueForSamplingAt(t)) 336 ASSERT_TRUE(should_not_poll.IsOverdueForSamplingAt(t))
335 << "Sampled last event; is dirty one time only to meet redundancy goal."; 337 << "Sampled last event; is dirty one time only to meet redundancy goal.";
336 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t)) 338 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
337 << "If updates are unreliable, must fall back to polling when idle."; 339 << "If updates are unreliable, must fall back to polling when idle.";
338 should_not_poll.RecordSample(); 340 should_not_poll.RecordSample();
339 should_poll.RecordSample(); 341 should_poll.RecordSample();
340 342
341 // Forever more, the non-polling sampler returns false while the polling one 343 // Forever more, the non-polling sampler returns false while the polling one
342 // returns true. 344 // returns true.
343 for (int i = 0; i < 100; ++i) { 345 for (int i = 0; i < 100; ++i) {
(...skipping 13 matching lines...) Expand all
357 } 359 }
358 360
359 struct DataPoint { 361 struct DataPoint {
360 bool should_capture; 362 bool should_capture;
361 double increment_ms; 363 double increment_ms;
362 }; 364 };
363 365
364 void ReplayCheckingSamplerDecisions(const DataPoint* data_points, 366 void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
365 size_t num_data_points, 367 size_t num_data_points,
366 SmoothEventSampler* sampler) { 368 SmoothEventSampler* sampler) {
367 base::TimeTicks t; 369 base::TimeTicks t = InitialTestTimeTicks();
368 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
369 for (size_t i = 0; i < num_data_points; ++i) { 370 for (size_t i = 0; i < num_data_points; ++i) {
370 t += base::TimeDelta::FromMicroseconds( 371 t += base::TimeDelta::FromMicroseconds(
371 static_cast<int64>(data_points[i].increment_ms * 1000)); 372 static_cast<int64>(data_points[i].increment_ms * 1000));
372 ASSERT_EQ(data_points[i].should_capture, 373 ASSERT_EQ(data_points[i].should_capture,
373 sampler->AddEventAndConsiderSampling(t)) 374 sampler->AddEventAndConsiderSampling(t))
374 << "at data_points[" << i << ']'; 375 << "at data_points[" << i << ']';
375 if (data_points[i].should_capture) 376 if (data_points[i].should_capture)
376 sampler->RecordSample(); 377 sampler->RecordSample();
377 } 378 }
378 } 379 }
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 }, 475 { true, 33.44 }, { false, 0 }, { true, 33.441 }, { false, 16.72 },
475 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 }, 476 { true, 16.72 }, { true, 50.16 }, { false, 0 }, { true, 16.72 },
476 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 }, 477 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
477 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 }, 478 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
478 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }, 479 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
479 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 } 480 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
480 }; 481 };
481 482
482 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3); 483 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
483 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler); 484 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
484 } 485 }
ncarter (slow) 2014/07/26 00:57:57 As I look through these I think we're missing a te
miu 2014/07/28 21:54:12 Yes. This code was all originally written back wh
485 486
487 // A test scenario for AnimatedContentSamplerTest.
488 struct Scenario {
489 base::TimeDelta vsync_interval; // Compositor's update rate.
490 base::TimeDelta min_capture_period; // Maximum capture rate.
491 base::TimeDelta content_period; // Animating content frame rate.
492
493 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c)
494 : vsync_interval(v), min_capture_period(m), content_period(c) {}
495 };
496
497 // Value printer for Scenario.
498 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
499 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
500 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
501 << ", content_period=" << s.content_period.InMicroseconds()
502 << " }";
503 }
504
505 class AnimatedContentSamplerTest : public ::testing::TestWithParam<Scenario> {
506 public:
507 AnimatedContentSamplerTest()
508 : count_dropped_frames_(0), count_sampled_frames_(0) {}
509
510 virtual void SetUp() OVERRIDE {
511 const base::TimeDelta since_epoch =
512 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
513 srand(static_cast<unsigned int>(since_epoch.InMicroseconds()));
514 sampler_.reset(new AnimatedContentSampler(GetParam().min_capture_period));
515 }
516
517 protected:
518 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
519
520 AnimatedContentSampler* sampler() const {
521 return sampler_.get();
522 }
523
524 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
525 base::TimeTicks end,
526 bool include_content_frame_events,
527 bool include_random_events) {
528 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
529 base::TimeTicks next_content_time = begin - GetParam().content_period;
530 std::vector<Event> events;
531 for (base::TimeTicks compositor_time = begin; compositor_time < end;
532 compositor_time += GetParam().vsync_interval) {
533 if (include_content_frame_events && next_content_time < compositor_time) {
534 events.push_back(Event(GetContentDamageRect(), compositor_time));
535 next_content_time += GetParam().content_period;
536 } else if (include_random_events && GetRandomInRange(0, 5) == 0) {
537 events.push_back(Event(GetRandomDamageRect(), compositor_time));
538 }
539 }
540
541 return events;
542 }
543
544 void ResetFrameCounters() {
545 count_dropped_frames_ = 0;
546 count_sampled_frames_ = 0;
547 }
548
549 // Keep track what the sampler is proposing, and call RecordSample() if it
550 // proposes sampling |event|.
551 void ClientDoesWhatSamplerProposes(const Event& event) {
552 if (sampler_->next_frame_timestamp().is_null()) {
553 if (event.first == GetContentDamageRect())
554 ++count_dropped_frames_;
555 } else {
556 EXPECT_EQ(GetContentDamageRect(), event.first);
557 sampler_->RecordSample(sampler_->next_frame_timestamp());
558 ++count_sampled_frames_;
559 }
560 }
561
562 // RecordSample() is not called, but for testing, keep track of what the
563 // sampler is proposing for |event|.
564 void ClientCannotSampleFrame(const Event& event) {
565 if (sampler_->next_frame_timestamp().is_null()) {
566 if (event.first == GetContentDamageRect())
567 ++count_dropped_frames_;
568 } else {
569 EXPECT_EQ(GetContentDamageRect(), event.first);
570 ++count_sampled_frames_;
571 }
572 }
573
574 void ExpectFrameDropRatioIsCorrect() {
575 const double content_framerate =
576 1000000.0 / GetParam().content_period.InMicroseconds();
577 const double capture_framerate =
578 1000000.0 / GetParam().min_capture_period.InMicroseconds();
579 const double expected_drop_rate = std::max(
580 0.0, (content_framerate - capture_framerate) / capture_framerate);
581 const double actual_drop_rate =
582 static_cast<double>(count_dropped_frames_) / count_sampled_frames_;
583 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.01);
584 }
585
586 static int GetRandomInRange(int begin, int end) {
587 const int len = end - begin;
588 const int rand_offset = (len == 0) ? 0 : (rand() % (end - begin));
589 return begin + rand_offset;
590 }
591
592 static gfx::Rect GetRandomDamageRect() {
593 return gfx::Rect(0, 0, GetRandomInRange(1, 600), GetRandomInRange(1, 600));
594 }
595
596 static gfx::Rect GetContentDamageRect() {
597 // This must be distinct from anything GetRandomDamageRect() could return.
598 return gfx::Rect(0, 0, 1280, 720);
599 }
600
601 private:
602 scoped_ptr<AnimatedContentSampler> sampler_;
603
604 // These counters only include the frames with the desired content.
605 int count_dropped_frames_;
606 int count_sampled_frames_;
607 };
608
609 // Tests that the implementation locks in/out of frames containing stable
610 // animated content, whether or not random events are also simultaneously
611 // present.
612 TEST_P(AnimatedContentSamplerTest, LocksIntoMajorityAnimatedContent) {
613 // |begin| refers to the start of an event sequence in terms of the
614 // Compositor's clock.
615 base::TimeTicks begin = InitialTestTimeTicks();
616
617 // Provide three minutes of random events and expect no lock-in.
618 EXPECT_TRUE(sampler()->next_frame_timestamp().is_null());
619 base::TimeTicks end = begin + base::TimeDelta::FromMinutes(3);
620 std::vector<Event> events = GenerateEventSequence(begin, end, false, true);
621 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
622 ++i) {
623 EXPECT_FALSE(sampler()->ConsiderPresentationEvent(i->first, i->second));
624 EXPECT_TRUE(sampler()->next_frame_timestamp().is_null());
625 sampler()->RecordSample(i->second);
626 }
627 begin = end;
628
629 // Provide content frame events with some random events mixed-in, and expect
630 // the sampler to lock-in once 1000 ms has elapsed, and also to remain in a
631 // continuous lock-in 1250 ms after that.
632 end = begin + base::TimeDelta::FromSeconds(10);
633 events = GenerateEventSequence(begin, end, true, true);
634 bool is_locked_in = false;
635 ResetFrameCounters();
636 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
637 ++i) {
638 const base::TimeDelta elapsed = i->second - events.begin()->second;
639 if (elapsed < base::TimeDelta::FromMilliseconds(1000)) {
640 EXPECT_FALSE(sampler()->ConsiderPresentationEvent(i->first, i->second));
641 sampler()->RecordSample(i->second);
642 } else {
643 if (sampler()->ConsiderPresentationEvent(i->first, i->second)) {
644 is_locked_in = true;
645 ClientDoesWhatSamplerProposes(*i);
646 } else {
647 if (elapsed > base::TimeDelta::FromMilliseconds(1250))
648 EXPECT_FALSE(is_locked_in);
649 EXPECT_TRUE(sampler()->next_frame_timestamp().is_null());
650 sampler()->RecordSample(i->second);
651 }
652 }
653 }
654 EXPECT_TRUE(is_locked_in);
655 ExpectFrameDropRatioIsCorrect();
656 begin = end;
657
658 // Continue providing content frame events without random events mixed-in and
659 // expect the lock-in to hold.
660 end = begin + base::TimeDelta::FromSeconds(30);
661 events = GenerateEventSequence(begin, end, true, false);
662 ResetFrameCounters();
663 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
664 ++i) {
665 EXPECT_TRUE(sampler()->ConsiderPresentationEvent(i->first, i->second));
666 ClientDoesWhatSamplerProposes(*i);
667 }
668 ExpectFrameDropRatioIsCorrect();
669 begin = end;
670
671 // Continue providing content frame events and expect the lock-in to hold.
672 // RecordSample() is only sometimes called, which simulates the capture
673 // pipeline experiencing back pressure.
674 end = begin + base::TimeDelta::FromSeconds(30);
675 events = GenerateEventSequence(begin, end, true, false);
676 ResetFrameCounters();
677 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
678 ++i) {
679 EXPECT_TRUE(sampler()->ConsiderPresentationEvent(i->first, i->second));
680 if (GetRandomInRange(0, 2) == 0)
681 ClientCannotSampleFrame(*i);
682 else
683 ClientDoesWhatSamplerProposes(*i);
684 }
685 ExpectFrameDropRatioIsCorrect();
686 begin = end;
687
688 // Provide a half-second of random events only, and expect the lock-in to be
689 // broken.
690 end = begin + base::TimeDelta::FromMilliseconds(500);
691 events = GenerateEventSequence(begin, end, false, true);
692 is_locked_in = true;
693 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
694 ++i) {
695 if (sampler()->ConsiderPresentationEvent(i->first, i->second)) {
696 EXPECT_TRUE(is_locked_in);
697 ClientDoesWhatSamplerProposes(*i);
698 } else {
699 is_locked_in = false;
700 EXPECT_TRUE(sampler()->next_frame_timestamp().is_null());
701 sampler()->RecordSample(i->second);
702 }
703 }
704 EXPECT_FALSE(is_locked_in);
705 begin = end;
706
707 // Now, go back to providing content frame events, and expect the sampler to
708 // lock-in once again.
709 end = begin + base::TimeDelta::FromSeconds(10);
710 events = GenerateEventSequence(begin, end, true, false);
711 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
712 ++i) {
713 const base::TimeDelta elapsed = i->second - events.begin()->second;
714 if (elapsed < base::TimeDelta::FromMilliseconds(1000)) {
715 EXPECT_FALSE(sampler()->ConsiderPresentationEvent(i->first, i->second));
716 sampler()->RecordSample(i->second);
717 } else {
718 if (sampler()->ConsiderPresentationEvent(i->first, i->second)) {
719 is_locked_in = true;
720 ClientDoesWhatSamplerProposes(*i);
721 } else {
722 if (elapsed > base::TimeDelta::FromMilliseconds(1250))
723 EXPECT_FALSE(is_locked_in);
724 EXPECT_TRUE(sampler()->next_frame_timestamp().is_null());
725 sampler()->RecordSample(i->second);
726 }
727 }
728 }
729 EXPECT_TRUE(is_locked_in);
730 begin = end;
731 }
732
733 // Tests that the frame timestamps are smooth; meaning, that when run through a
734 // simulated compositor, each frame is held displayed for the right number of
735 // v-sync intervals.
736 TEST_P(AnimatedContentSamplerTest, FrameTimestampsAreSmooth) {
737 // Generate 30 seconds of animated content events, run the events through
738 // AnimatedContentSampler, and record all frame timestamps being proposed
739 // once lock-in is continuous.
740 base::TimeTicks begin = InitialTestTimeTicks();
741 std::vector<Event> events = GenerateEventSequence(
742 begin,
743 begin + base::TimeDelta::FromSeconds(30),
744 true,
745 false);
746 typedef std::vector<base::TimeTicks> Timestamps;
747 Timestamps frame_timestamps;
748 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
749 ++i) {
750 if (sampler()->ConsiderPresentationEvent(i->first, i->second)) {
751 if (!sampler()->next_frame_timestamp().is_null()) {
752 frame_timestamps.push_back(sampler()->next_frame_timestamp());
753 sampler()->RecordSample(sampler()->next_frame_timestamp());
754 }
755 } else {
756 frame_timestamps.clear(); // Reset until continuous lock-in.
757 }
758 }
759 ASSERT_LE(2u, frame_timestamps.size());
760
761 // Iterate through the |frame_timestamps|, building a histogram counting the
762 // number of times each frame was displayed k times. For example, 10 frames
763 // of 30 Hz content on a 60 Hz v-sync interval should result in
764 // display_counts[2] == 10. Quit early if any one frame was obviously
765 // repeated too many times.
766 const int64 max_expected_repeats_per_frame = 1 +
767 std::max(GetParam().min_capture_period, GetParam().content_period) /
768 GetParam().vsync_interval;
769 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
770 base::TimeTicks last_present_time = frame_timestamps.front();
771 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
772 i != frame_timestamps.end(); ++i) {
773 const size_t num_vsync_intervals = static_cast<size_t>(
774 (*i - last_present_time) / GetParam().vsync_interval);
775 ASSERT_LT(0u, num_vsync_intervals);
776 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
777 ++display_counts[num_vsync_intervals];
778 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
779 }
780
781 // Analyze the histogram for an expected result pattern. If the frame
782 // timestamps are smooth, there should only be one or two buckets with
783 // non-zero counts and they should be next to each other. Because the clock
784 // precision for the event_times provided to the sampler is very granular
785 // (i.e., the vsync_interval), it's okay if other buckets have a tiny "stray"
786 // count in this test.
787 size_t highest_count = 0;
788 size_t second_highest_count = 0;
789 for (size_t repeats = 1; repeats < display_counts.size(); ++repeats) {
790 DVLOG(1) << "display_counts[" << repeats << "] is "
791 << display_counts[repeats];
792 if (display_counts[repeats] >= highest_count) {
793 second_highest_count = highest_count;
794 highest_count = display_counts[repeats];
795 } else if (display_counts[repeats] > second_highest_count) {
796 second_highest_count = display_counts[repeats];
797 }
798 }
799 size_t stray_count_remaining =
800 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
801 // Expect no more than 0.5% of frames fall outside the two main buckets.
802 EXPECT_GT(frame_timestamps.size() * 5 / 1000, stray_count_remaining);
803 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
804 if (display_counts[repeats] == highest_count) {
805 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
806 ++repeats;
807 } else if (display_counts[repeats] == second_highest_count) {
808 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
809 ++repeats;
810 } else {
811 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
812 stray_count_remaining -= display_counts[repeats];
813 }
814 }
815 }
816
817 base::TimeDelta FpsAsPeriod(int frame_rate) {
818 return base::TimeDelta::FromSeconds(1) / frame_rate;
819 }
820
821 INSTANTIATE_TEST_CASE_P(
822 ,
823 AnimatedContentSamplerTest,
824 ::testing::Values(
825 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
826 // Hz, and content video animates at 30, 25, or 24 Hz.
827 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)),
828 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)),
829 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)),
830
831 // High frame rate content that leverages the Compositor's
832 // capabilities, but capture is still at 30 Hz.
833 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)),
834 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)),
835 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)),
836
837 // High frame rate content that leverages the Compositor's
838 // capabilities, and capture is also a buttery 60 Hz.
839 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)),
840 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)),
841 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)),
842
843 // On some platforms, the Compositor runs at 50 Hz.
844 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)),
845 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)),
846 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)),
847 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)),
848 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)),
849
850 // Stable, but non-standard content frame rates.
851 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)),
852 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)),
853 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)),
854 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)),
855 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)),
856 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)),
857 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)),
858 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)),
859 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)),
860 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33))));
861
ncarter (slow) 2014/07/26 00:57:58 Given how the oracle in practice drives both sampl
miu 2014/07/28 21:54:12 Good point. I'll work on adding this.
miu 2014/07/31 01:25:32 Done.
486 } // namespace 862 } // namespace
487 } // namespace content 863 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698