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

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

Powered by Google App Engine
This is Rietveld 408576698