Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |