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

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: Un-inline some methods, plus REBASE. Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/media/capture/video_capture_oracle.h" 5 #include "content/browser/media/capture/video_capture_oracle.h"
6 6
7 #include <cstdlib>
8 #include <utility>
9 #include <vector>
10
11 #include "base/logging.h"
7 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
8 #include "base/time/time.h" 13 #include "base/time/time.h"
9 #include "testing/gtest/include/gtest/gtest.h" 14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "ui/gfx/geometry/rect.h"
10 16
11 namespace content { 17 namespace content {
12 namespace { 18 namespace {
13 19
20 bool AddEventAndConsiderSampling(SmoothEventSampler* sampler,
21 base::TimeTicks event_time) {
22 sampler->ConsiderPresentationEvent(event_time);
23 return sampler->ShouldSample();
24 }
25
14 void SteadyStateSampleAndAdvance(base::TimeDelta vsync, 26 void SteadyStateSampleAndAdvance(base::TimeDelta vsync,
15 SmoothEventSampler* sampler, 27 SmoothEventSampler* sampler,
16 base::TimeTicks* t) { 28 base::TimeTicks* t) {
17 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t)); 29 ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
18 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 30 ASSERT_TRUE(sampler->HasUnrecordedEvent());
19 sampler->RecordSample(); 31 sampler->RecordSample();
20 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 32 ASSERT_FALSE(sampler->HasUnrecordedEvent());
21 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 33 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
22 *t += vsync; 34 *t += vsync;
23 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 35 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
24 } 36 }
25 37
26 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync, 38 void SteadyStateNoSampleAndAdvance(base::TimeDelta vsync,
27 SmoothEventSampler* sampler, 39 SmoothEventSampler* sampler,
28 base::TimeTicks* t) { 40 base::TimeTicks* t) {
29 ASSERT_FALSE(sampler->AddEventAndConsiderSampling(*t)); 41 ASSERT_FALSE(AddEventAndConsiderSampling(sampler, *t));
30 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 42 ASSERT_TRUE(sampler->HasUnrecordedEvent());
31 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 43 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
32 *t += vsync; 44 *t += vsync;
33 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)); 45 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t));
34 } 46 }
35 47
36 void TimeTicksFromString(const char* string, base::TimeTicks* t) { 48 base::TimeTicks InitialTestTimeTicks() {
37 base::Time time; 49 return base::TimeTicks() + base::TimeDelta::FromSeconds(1);
38 ASSERT_TRUE(base::Time::FromString(string, &time));
39 *t = base::TimeTicks::UnixEpoch() + (time - base::Time::UnixEpoch());
40 } 50 }
41 51
42 void TestRedundantCaptureStrategy(base::TimeDelta capture_period, 52 void TestRedundantCaptureStrategy(base::TimeDelta capture_period,
43 int redundant_capture_goal, 53 int redundant_capture_goal,
44 SmoothEventSampler* sampler, 54 SmoothEventSampler* sampler,
45 base::TimeTicks* t) { 55 base::TimeTicks* t) {
46 // Before any events have been considered, we're overdue for sampling. 56 // Before any events have been considered, we're overdue for sampling.
47 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t)); 57 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t));
48 58
49 // Consider the first event. We want to sample that. 59 // Consider the first event. We want to sample that.
50 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 60 ASSERT_FALSE(sampler->HasUnrecordedEvent());
51 ASSERT_TRUE(sampler->AddEventAndConsiderSampling(*t)); 61 ASSERT_TRUE(AddEventAndConsiderSampling(sampler, *t));
52 ASSERT_TRUE(sampler->HasUnrecordedEvent()); 62 ASSERT_TRUE(sampler->HasUnrecordedEvent());
53 sampler->RecordSample(); 63 sampler->RecordSample();
54 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 64 ASSERT_FALSE(sampler->HasUnrecordedEvent());
55 65
56 // After more than one capture period has passed without considering an event, 66 // 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 67 // repeatedly be overdue for sampling. However, once the redundant capture
58 // capture goal is achieved, we should no longer be overdue for sampling. 68 // goal is achieved, we should no longer be overdue for sampling.
59 *t += capture_period * 4; 69 *t += base::TimeDelta::FromMilliseconds(250);
60 for (int i = 0; i < redundant_capture_goal; i++) { 70 for (int i = 0; i < redundant_capture_goal; i++) {
61 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 71 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
62 ASSERT_FALSE(sampler->HasUnrecordedEvent()); 72 ASSERT_FALSE(sampler->HasUnrecordedEvent());
63 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t)) 73 ASSERT_TRUE(sampler->IsOverdueForSamplingAt(*t))
64 << "Should sample until redundant capture goal is hit"; 74 << "Should sample until redundant capture goal is hit";
65 sampler->RecordSample(); 75 sampler->RecordSample();
66 *t += capture_period; // Timer fires once every capture period. 76 *t += capture_period; // Timer fires once every capture period.
67 } 77 }
68 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t)) 78 ASSERT_FALSE(sampler->IsOverdueForSamplingAt(*t))
69 << "Should not be overdue once redundant capture goal achieved."; 79 << "Should not be overdue once redundant capture goal achieved.";
70 } 80 }
71 81
82 } // namespace
83
72 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains 84 // 60Hz sampled at 30Hz should produce 30Hz. In addition, this test contains
73 // much more comprehensive before/after/edge-case scenarios than the others. 85 // much more comprehensive before/after/edge-case scenarios than the others.
74 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) { 86 TEST(SmoothEventSamplerTest, Sample60HertzAt30Hertz) {
75 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 87 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
76 const int redundant_capture_goal = 200; 88 const int redundant_capture_goal = 200;
77 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60; 89 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 60;
78 90
79 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 91 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
80 base::TimeTicks t; 92 base::TimeTicks t = InitialTestTimeTicks();
81 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
82 93
83 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 94 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
84 &sampler, &t); 95 &sampler, &t);
85 96
86 // Steady state, we should capture every other vsync, indefinitely. 97 // Steady state, we should capture every other vsync, indefinitely.
87 for (int i = 0; i < 100; i++) { 98 for (int i = 0; i < 100; i++) {
88 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 99 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
89 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 100 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
90 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 101 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
91 } 102 }
92 103
93 // Now pretend we're limited by backpressure in the pipeline. In this scenario 104 // Now pretend we're limited by backpressure in the pipeline. In this scenario
94 // case we are adding events but not sampling them. 105 // case we are adding events but not sampling them.
95 for (int i = 0; i < 20; i++) { 106 for (int i = 0; i < 20; i++) {
96 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 107 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
97 ASSERT_EQ(i >= 7, sampler.IsOverdueForSamplingAt(t)); 108 ASSERT_EQ(i >= 14, sampler.IsOverdueForSamplingAt(t));
98 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 109 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
99 ASSERT_TRUE(sampler.HasUnrecordedEvent()); 110 ASSERT_TRUE(sampler.HasUnrecordedEvent());
100 t += vsync; 111 t += vsync;
101 } 112 }
102 113
103 // Now suppose we can sample again. We should be back in the steady state, 114 // Now suppose we can sample again. We should be back in the steady state,
104 // but at a different phase. 115 // but at a different phase.
105 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t)); 116 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
106 for (int i = 0; i < 100; i++) { 117 for (int i = 0; i < 100; i++) {
107 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 118 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
108 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 119 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
109 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 120 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
110 } 121 }
111 } 122 }
112 123
113 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped. 124 // 50Hz sampled at 30Hz should produce a sequence where some frames are skipped.
114 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) { 125 TEST(SmoothEventSamplerTest, Sample50HertzAt30Hertz) {
115 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 126 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
116 const int redundant_capture_goal = 2; 127 const int redundant_capture_goal = 2;
117 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50; 128 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 50;
118 129
119 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 130 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
120 base::TimeTicks t; 131 base::TimeTicks t = InitialTestTimeTicks();
121 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
122 132
123 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 133 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
124 &sampler, &t); 134 &sampler, &t);
125 135
126 // Steady state, we should capture 1st, 2nd and 4th frames out of every five 136 // Steady state, we should capture 1st, 2nd and 4th frames out of every five
127 // frames, indefinitely. 137 // frames, indefinitely.
128 for (int i = 0; i < 100; i++) { 138 for (int i = 0; i < 100; i++) {
129 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 139 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
130 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 140 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
131 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 141 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
132 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 142 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
133 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 143 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
134 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 144 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
135 } 145 }
136 146
137 // Now pretend we're limited by backpressure in the pipeline. In this scenario 147 // Now pretend we're limited by backpressure in the pipeline. In this scenario
138 // case we are adding events but not sampling them. 148 // case we are adding events but not sampling them.
139 for (int i = 0; i < 12; i++) { 149 for (int i = 0; i < 20; i++) {
140 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 150 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
141 ASSERT_EQ(i >= 5, sampler.IsOverdueForSamplingAt(t)); 151 ASSERT_EQ(i >= 11, sampler.IsOverdueForSamplingAt(t));
142 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 152 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
143 t += vsync; 153 t += vsync;
144 } 154 }
145 155
146 // Now suppose we can sample again. We should be back in the steady state 156 // Now suppose we can sample again. We should be back in the steady state
147 // again. 157 // again.
148 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t)); 158 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
149 for (int i = 0; i < 100; i++) { 159 for (int i = 0; i < 100; i++) {
150 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 160 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
151 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 161 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
152 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 162 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
153 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 163 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
154 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 164 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
155 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 165 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
156 } 166 }
157 } 167 }
158 168
159 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped. 169 // 75Hz sampled at 30Hz should produce a sequence where some frames are skipped.
160 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) { 170 TEST(SmoothEventSamplerTest, Sample75HertzAt30Hertz) {
161 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 171 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
162 const int redundant_capture_goal = 32; 172 const int redundant_capture_goal = 32;
163 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75; 173 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 75;
164 174
165 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 175 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
166 base::TimeTicks t; 176 base::TimeTicks t = InitialTestTimeTicks();
167 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
168 177
169 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 178 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
170 &sampler, &t); 179 &sampler, &t);
171 180
172 // Steady state, we should capture 1st and 3rd frames out of every five 181 // Steady state, we should capture 1st and 3rd frames out of every five
173 // frames, indefinitely. 182 // frames, indefinitely.
174 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 183 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
175 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 184 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
176 for (int i = 0; i < 100; i++) { 185 for (int i = 0; i < 100; i++) {
177 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 186 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
178 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 187 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
179 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 188 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
180 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 189 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
181 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 190 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
182 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 191 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
183 } 192 }
184 193
185 // Now pretend we're limited by backpressure in the pipeline. In this scenario 194 // Now pretend we're limited by backpressure in the pipeline. In this scenario
186 // case we are adding events but not sampling them. 195 // case we are adding events but not sampling them.
187 for (int i = 0; i < 20; i++) { 196 for (int i = 0; i < 20; i++) {
188 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 197 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
189 ASSERT_EQ(i >= 8, sampler.IsOverdueForSamplingAt(t)); 198 ASSERT_EQ(i >= 16, sampler.IsOverdueForSamplingAt(t));
190 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 199 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
191 t += vsync; 200 t += vsync;
192 } 201 }
193 202
194 // Now suppose we can sample again. We capture the next frame, and not the one 203 // 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. 204 // after that, and then we're back in the steady state again.
196 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t)); 205 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
197 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 206 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
198 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 207 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
199 for (int i = 0; i < 100; i++) { 208 for (int i = 0; i < 100; i++) {
200 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 209 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
201 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 210 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
202 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 211 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
203 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 212 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
204 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 213 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
205 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t); 214 SteadyStateNoSampleAndAdvance(vsync, &sampler, &t);
206 } 215 }
207 } 216 }
208 217
209 // 30Hz sampled at 30Hz should produce 30Hz. 218 // 30Hz sampled at 30Hz should produce 30Hz.
210 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) { 219 TEST(SmoothEventSamplerTest, Sample30HertzAt30Hertz) {
211 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 220 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
212 const int redundant_capture_goal = 1; 221 const int redundant_capture_goal = 1;
213 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30; 222 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 30;
214 223
215 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 224 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
216 base::TimeTicks t; 225 base::TimeTicks t = InitialTestTimeTicks();
217 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
218 226
219 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 227 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
220 &sampler, &t); 228 &sampler, &t);
221 229
222 // Steady state, we should capture every vsync, indefinitely. 230 // Steady state, we should capture every vsync, indefinitely.
223 for (int i = 0; i < 200; i++) { 231 for (int i = 0; i < 200; i++) {
224 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 232 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
225 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 233 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
226 } 234 }
227 235
228 // Now pretend we're limited by backpressure in the pipeline. In this scenario 236 // Now pretend we're limited by backpressure in the pipeline. In this scenario
229 // case we are adding events but not sampling them. 237 // case we are adding events but not sampling them.
230 for (int i = 0; i < 7; i++) { 238 for (int i = 0; i < 10; i++) {
231 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 239 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
232 ASSERT_EQ(i >= 3, sampler.IsOverdueForSamplingAt(t)); 240 ASSERT_EQ(i >= 7, sampler.IsOverdueForSamplingAt(t));
233 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 241 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
234 t += vsync; 242 t += vsync;
235 } 243 }
236 244
237 // Now suppose we can sample again. We should be back in the steady state. 245 // Now suppose we can sample again. We should be back in the steady state.
238 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t)); 246 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
239 for (int i = 0; i < 100; i++) { 247 for (int i = 0; i < 100; i++) {
240 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 248 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
241 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 249 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
242 } 250 }
243 } 251 }
244 252
245 // 24Hz sampled at 30Hz should produce 24Hz. 253 // 24Hz sampled at 30Hz should produce 24Hz.
246 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) { 254 TEST(SmoothEventSamplerTest, Sample24HertzAt30Hertz) {
247 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 255 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
248 const int redundant_capture_goal = 333; 256 const int redundant_capture_goal = 333;
249 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24; 257 const base::TimeDelta vsync = base::TimeDelta::FromSeconds(1) / 24;
250 258
251 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal); 259 SmoothEventSampler sampler(capture_period, true, redundant_capture_goal);
252 base::TimeTicks t; 260 base::TimeTicks t = InitialTestTimeTicks();
253 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
254 261
255 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal, 262 TestRedundantCaptureStrategy(capture_period, redundant_capture_goal,
256 &sampler, &t); 263 &sampler, &t);
257 264
258 // Steady state, we should capture every vsync, indefinitely. 265 // Steady state, we should capture every vsync, indefinitely.
259 for (int i = 0; i < 200; i++) { 266 for (int i = 0; i < 200; i++) {
260 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 267 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
261 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 268 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
262 } 269 }
263 270
264 // Now pretend we're limited by backpressure in the pipeline. In this scenario 271 // Now pretend we're limited by backpressure in the pipeline. In this scenario
265 // case we are adding events but not sampling them. 272 // case we are adding events but not sampling them.
266 for (int i = 0; i < 7; i++) { 273 for (int i = 0; i < 10; i++) {
267 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 274 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
268 ASSERT_EQ(i >= 3, sampler.IsOverdueForSamplingAt(t)); 275 ASSERT_EQ(i >= 6, sampler.IsOverdueForSamplingAt(t));
269 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 276 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
270 t += vsync; 277 t += vsync;
271 } 278 }
272 279
273 // Now suppose we can sample again. We should be back in the steady state. 280 // Now suppose we can sample again. We should be back in the steady state.
274 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t)); 281 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t));
275 for (int i = 0; i < 100; i++) { 282 for (int i = 0; i < 100; i++) {
276 SCOPED_TRACE(base::StringPrintf("Iteration %d", i)); 283 SCOPED_TRACE(base::StringPrintf("Iteration %d", i));
277 SteadyStateSampleAndAdvance(vsync, &sampler, &t); 284 SteadyStateSampleAndAdvance(vsync, &sampler, &t);
278 } 285 }
279 } 286 }
280 287
281 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) { 288 TEST(SmoothEventSamplerTest, DoubleDrawAtOneTimeStillDirties) {
282 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30; 289 const base::TimeDelta capture_period = base::TimeDelta::FromSeconds(1) / 30;
283 const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1); 290 const base::TimeDelta overdue_period = base::TimeDelta::FromSeconds(1);
284 291
285 SmoothEventSampler sampler(capture_period, true, 1); 292 SmoothEventSampler sampler(capture_period, true, 1);
286 base::TimeTicks t; 293 base::TimeTicks t = InitialTestTimeTicks();
287 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
288 294
289 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 295 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
290 sampler.RecordSample(); 296 sampler.RecordSample();
291 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t)) 297 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t))
292 << "Sampled last event; should not be dirty."; 298 << "Sampled last event; should not be dirty.";
293 t += overdue_period; 299 t += overdue_period;
294 300
295 // Now simulate 2 events with the same clock value. 301 // Now simulate 2 events with the same clock value.
296 ASSERT_TRUE(sampler.AddEventAndConsiderSampling(t)); 302 ASSERT_TRUE(AddEventAndConsiderSampling(&sampler, t));
297 sampler.RecordSample(); 303 sampler.RecordSample();
298 ASSERT_FALSE(sampler.AddEventAndConsiderSampling(t)) 304 ASSERT_FALSE(AddEventAndConsiderSampling(&sampler, t))
299 << "Two events at same time -- expected second not to be sampled."; 305 << "Two events at same time -- expected second not to be sampled.";
300 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period)) 306 ASSERT_TRUE(sampler.IsOverdueForSamplingAt(t + overdue_period))
301 << "Second event should dirty the capture state."; 307 << "Second event should dirty the capture state.";
302 sampler.RecordSample(); 308 sampler.RecordSample();
303 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period)); 309 ASSERT_FALSE(sampler.IsOverdueForSamplingAt(t + overdue_period));
304 } 310 }
305 311
306 TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) { 312 TEST(SmoothEventSamplerTest, FallbackToPollingIfUpdatesUnreliable) {
307 const base::TimeDelta timer_interval = base::TimeDelta::FromSeconds(1) / 30; 313 const base::TimeDelta timer_interval = base::TimeDelta::FromSeconds(1) / 30;
308 314
309 SmoothEventSampler should_not_poll(timer_interval, true, 1); 315 SmoothEventSampler should_not_poll(timer_interval, true, 1);
310 SmoothEventSampler should_poll(timer_interval, false, 1); 316 SmoothEventSampler should_poll(timer_interval, false, 1);
311 base::TimeTicks t; 317 base::TimeTicks t = InitialTestTimeTicks();
312 TimeTicksFromString("Sat, 23 Mar 2013 1:21:08 GMT", &t);
313 318
314 // Do one round of the "happy case" where an event was received and 319 // Do one round of the "happy case" where an event was received and
315 // RecordSample() was called by the client. 320 // RecordSample() was called by the client.
316 ASSERT_TRUE(should_not_poll.AddEventAndConsiderSampling(t)); 321 ASSERT_TRUE(AddEventAndConsiderSampling(&should_not_poll, t));
317 ASSERT_TRUE(should_poll.AddEventAndConsiderSampling(t)); 322 ASSERT_TRUE(AddEventAndConsiderSampling(&should_poll, t));
318 should_not_poll.RecordSample(); 323 should_not_poll.RecordSample();
319 should_poll.RecordSample(); 324 should_poll.RecordSample();
320 325
321 // One time period ahead, neither sampler says we're overdue. 326 // For the following time period, before 250 ms has elapsed, neither sampler
322 for (int i = 0; i < 3; i++) { 327 // says we're overdue.
328 const int non_overdue_intervals = static_cast<int>(
329 base::TimeDelta::FromMilliseconds(250) / timer_interval);
330 for (int i = 0; i < non_overdue_intervals; i++) {
323 t += timer_interval; 331 t += timer_interval;
324 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t)) 332 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
325 << "Sampled last event; should not be dirty."; 333 << "Sampled last event; should not be dirty.";
326 ASSERT_FALSE(should_poll.IsOverdueForSamplingAt(t)) 334 ASSERT_FALSE(should_poll.IsOverdueForSamplingAt(t))
327 << "Dirty interval has not elapsed yet."; 335 << "Dirty interval has not elapsed yet.";
328 } 336 }
329 337
330 // Next time period ahead, both samplers say we're overdue. The non-polling 338 // 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 339 // sampler is returning true here because it has been configured to allow one
332 // redundant capture. 340 // redundant capture.
333 t += timer_interval; 341 t += timer_interval; // Step past the 250 ms threshold.
334 ASSERT_TRUE(should_not_poll.IsOverdueForSamplingAt(t)) 342 ASSERT_TRUE(should_not_poll.IsOverdueForSamplingAt(t))
335 << "Sampled last event; is dirty one time only to meet redundancy goal."; 343 << "Sampled last event; is dirty one time only to meet redundancy goal.";
336 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t)) 344 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
337 << "If updates are unreliable, must fall back to polling when idle."; 345 << "If updates are unreliable, must fall back to polling when idle.";
338 should_not_poll.RecordSample(); 346 should_not_poll.RecordSample();
339 should_poll.RecordSample(); 347 should_poll.RecordSample();
340 348
341 // Forever more, the non-polling sampler returns false while the polling one 349 // Forever more, the non-polling sampler returns false while the polling one
342 // returns true. 350 // returns true.
343 for (int i = 0; i < 100; ++i) { 351 for (int i = 0; i < 100; ++i) {
344 t += timer_interval; 352 t += timer_interval;
345 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t)) 353 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
346 << "Sampled last event; should not be dirty."; 354 << "Sampled last event; should not be dirty.";
347 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t)) 355 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
348 << "If updates are unreliable, must fall back to polling when idle."; 356 << "If updates are unreliable, must fall back to polling when idle.";
349 should_poll.RecordSample(); 357 should_poll.RecordSample();
350 } 358 }
351 t += timer_interval / 3; 359 t += timer_interval / 3;
352 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t)) 360 ASSERT_FALSE(should_not_poll.IsOverdueForSamplingAt(t))
353 << "Sampled last event; should not be dirty."; 361 << "Sampled last event; should not be dirty.";
354 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t)) 362 ASSERT_TRUE(should_poll.IsOverdueForSamplingAt(t))
355 << "If updates are unreliable, must fall back to polling when idle."; 363 << "If updates are unreliable, must fall back to polling when idle.";
356 should_poll.RecordSample(); 364 should_poll.RecordSample();
357 } 365 }
358 366
367 namespace {
368
359 struct DataPoint { 369 struct DataPoint {
360 bool should_capture; 370 bool should_capture;
361 double increment_ms; 371 double increment_ms;
362 }; 372 };
363 373
364 void ReplayCheckingSamplerDecisions(const DataPoint* data_points, 374 void ReplayCheckingSamplerDecisions(const DataPoint* data_points,
365 size_t num_data_points, 375 size_t num_data_points,
366 SmoothEventSampler* sampler) { 376 SmoothEventSampler* sampler) {
367 base::TimeTicks t; 377 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) { 378 for (size_t i = 0; i < num_data_points; ++i) {
370 t += base::TimeDelta::FromMicroseconds( 379 t += base::TimeDelta::FromMicroseconds(
371 static_cast<int64>(data_points[i].increment_ms * 1000)); 380 static_cast<int64>(data_points[i].increment_ms * 1000));
372 ASSERT_EQ(data_points[i].should_capture, 381 ASSERT_EQ(data_points[i].should_capture,
373 sampler->AddEventAndConsiderSampling(t)) 382 AddEventAndConsiderSampling(sampler, t))
374 << "at data_points[" << i << ']'; 383 << "at data_points[" << i << ']';
375 if (data_points[i].should_capture) 384 if (data_points[i].should_capture)
376 sampler->RecordSample(); 385 sampler->RecordSample();
377 } 386 }
378 } 387 }
379 388
389 } // namespace
390
380 TEST(SmoothEventSamplerTest, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz) { 391 TEST(SmoothEventSamplerTest, DrawingAt24FpsWith60HzVsyncSampledAt30Hertz) {
381 // Actual capturing of timing data: Initial instability as a 24 FPS video was 392 // Actual capturing of timing data: Initial instability as a 24 FPS video was
382 // started from a still screen, then clearly followed by steady-state. 393 // started from a still screen, then clearly followed by steady-state.
383 static const DataPoint data_points[] = { 394 static const DataPoint data_points[] = {
384 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 }, 395 { true, 1437.93 }, { true, 150.484 }, { true, 217.362 }, { true, 50.161 },
385 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 }, 396 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 66.88 },
386 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 }, 397 { true, 50.161 }, { false, 0 }, { false, 0 }, { true, 50.16 },
387 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 }, 398 { true, 33.441 }, { true, 16.72 }, { false, 16.72 }, { true, 117.041 },
388 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 }, 399 { true, 16.72 }, { false, 16.72 }, { true, 50.161 }, { true, 50.16 },
389 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 }, 400 { true, 33.441 }, { true, 33.44 }, { true, 33.44 }, { true, 16.72 },
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
476 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 }, 487 { true, 33.441 }, { false, 0 }, { true, 33.44 }, { false, 16.72 },
477 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 }, 488 { true, 33.44 }, { false, 0 }, { true, 16.721 }, { true, 50.161 },
478 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 }, 489 { false, 0 }, { true, 16.72 }, { true, 33.44 }, { false, 0 },
479 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 } 490 { true, 33.441 }, { false, 16.72 }, { true, 16.72 }, { true, 50.16 }
480 }; 491 };
481 492
482 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3); 493 SmoothEventSampler sampler(base::TimeDelta::FromSeconds(1) / 30, true, 3);
483 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler); 494 ReplayCheckingSamplerDecisions(data_points, arraysize(data_points), &sampler);
484 } 495 }
485 496
497 class AnimatedContentSamplerTest : public ::testing::Test {
498 public:
499 AnimatedContentSamplerTest() {}
500 virtual ~AnimatedContentSamplerTest() {}
501
502 virtual void SetUp() OVERRIDE {
503 const base::TimeDelta since_epoch =
504 InitialTestTimeTicks() - base::TimeTicks::UnixEpoch();
505 rand_seed_ = abs(static_cast<int>(since_epoch.InMicroseconds()));
506 sampler_.reset(new AnimatedContentSampler(GetMinCapturePeriod()));
507 }
508
509 protected:
510 // Overridden by subclass for parameterized tests.
511 virtual base::TimeDelta GetMinCapturePeriod() const {
512 return base::TimeDelta::FromSeconds(1) / 30;
513 }
514
515 AnimatedContentSampler* sampler() const {
516 return sampler_.get();
517 }
518
519 int GetRandomInRange(int begin, int end) {
520 const int len = end - begin;
521 const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
522 return begin + rand_offset;
523 }
524
525 gfx::Rect GetRandomDamageRect() {
526 return gfx::Rect(0, 0, GetRandomInRange(1, 100), GetRandomInRange(1, 100));
527 }
528
529 gfx::Rect GetContentDamageRect() {
530 // This must be distinct from anything GetRandomDamageRect() could return.
531 return gfx::Rect(0, 0, 1280, 720);
532 }
533
534 // Directly inject an observation. Only used to test
535 // ElectMajorityDamageRect().
536 void ObserveDamageRect(const gfx::Rect& damage_rect) {
537 sampler_->observations_.push_back(
538 AnimatedContentSampler::Observation(damage_rect, base::TimeTicks()));
539 }
540
541 gfx::Rect ElectMajorityDamageRect() const {
542 return sampler_->ElectMajorityDamageRect();
543 }
544
545 private:
546 // Note: Not using base::RandInt() because it is horribly slow on debug
547 // builds. The following is a very simple, deterministic LCG:
548 int NextRandomInt() {
549 rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
550 return rand_seed_;
551 }
552
553 int rand_seed_;
554 scoped_ptr<AnimatedContentSampler> sampler_;
555 };
556
557 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromZeroDamageRects) {
558 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
559 }
560
561 TEST_F(AnimatedContentSamplerTest, ElectsMajorityFromOneDamageRect) {
562 const gfx::Rect the_one_rect(0, 0, 1, 1);
563 ObserveDamageRect(the_one_rect);
564 EXPECT_EQ(the_one_rect, ElectMajorityDamageRect());
565 }
566
567 TEST_F(AnimatedContentSamplerTest, ElectsNoneFromTwoDamageRectsOfSameArea) {
568 const gfx::Rect one_rect(0, 0, 1, 1);
569 const gfx::Rect another_rect(1, 1, 1, 1);
570 ObserveDamageRect(one_rect);
571 ObserveDamageRect(another_rect);
572 EXPECT_EQ(gfx::Rect(), ElectMajorityDamageRect());
573 }
574
575 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_1) {
576 const gfx::Rect one_rect(0, 0, 1, 1);
577 const gfx::Rect another_rect(0, 0, 2, 2);
578 ObserveDamageRect(one_rect);
579 ObserveDamageRect(another_rect);
580 EXPECT_EQ(another_rect, ElectMajorityDamageRect());
581 }
582
583 TEST_F(AnimatedContentSamplerTest, ElectsLargerOfTwoDamageRects_2) {
584 const gfx::Rect one_rect(0, 0, 2, 2);
585 const gfx::Rect another_rect(0, 0, 1, 1);
586 ObserveDamageRect(one_rect);
587 ObserveDamageRect(another_rect);
588 EXPECT_EQ(one_rect, ElectMajorityDamageRect());
589 }
590
591 TEST_F(AnimatedContentSamplerTest, ElectsSameAsMooreDemonstration) {
592 // A more complex sequence (from Moore's web site): Three different Rects with
593 // the same area, but occurring a different number of times. C should win the
594 // vote.
595 const gfx::Rect rect_a(0, 0, 1, 4);
596 const gfx::Rect rect_b(1, 1, 4, 1);
597 const gfx::Rect rect_c(2, 2, 2, 2);
598 for (int i = 0; i < 3; ++i)
599 ObserveDamageRect(rect_a);
600 for (int i = 0; i < 2; ++i)
601 ObserveDamageRect(rect_c);
602 for (int i = 0; i < 2; ++i)
603 ObserveDamageRect(rect_b);
604 for (int i = 0; i < 3; ++i)
605 ObserveDamageRect(rect_c);
606 ObserveDamageRect(rect_b);
607 for (int i = 0; i < 2; ++i)
608 ObserveDamageRect(rect_c);
609 EXPECT_EQ(rect_c, ElectMajorityDamageRect());
610 }
611
612 TEST_F(AnimatedContentSamplerTest, Elects24FpsVideoInsteadOf48FpsSpinner) {
613 // Scenario: 24 FPS 720x480 Video versus 48 FPS 96x96 "Busy Spinner"
614 const gfx::Rect video_rect(100, 100, 720, 480);
615 const gfx::Rect spinner_rect(360, 0, 96, 96);
616 for (int i = 0; i < 100; ++i) {
617 // |video_rect| occurs once for every two |spinner_rect|. Vary the order
618 // of events between the two:
619 ObserveDamageRect(video_rect);
620 ObserveDamageRect(spinner_rect);
621 ObserveDamageRect(spinner_rect);
622 ObserveDamageRect(video_rect);
623 ObserveDamageRect(spinner_rect);
624 ObserveDamageRect(spinner_rect);
625 ObserveDamageRect(spinner_rect);
626 ObserveDamageRect(video_rect);
627 ObserveDamageRect(spinner_rect);
628 ObserveDamageRect(spinner_rect);
629 ObserveDamageRect(video_rect);
630 ObserveDamageRect(spinner_rect);
631 }
632 EXPECT_EQ(video_rect, ElectMajorityDamageRect());
633 }
634
635 namespace {
636
637 // A test scenario for AnimatedContentSamplerParameterizedTest.
638 struct Scenario {
639 base::TimeDelta vsync_interval; // Reflects compositor's update rate.
640 base::TimeDelta min_capture_period; // Reflects maximum capture rate.
641 base::TimeDelta content_period; // Reflects content animation rate.
642
643 Scenario(base::TimeDelta v, base::TimeDelta m, base::TimeDelta c)
644 : vsync_interval(v), min_capture_period(m), content_period(c) {
645 CHECK(content_period >= vsync_interval)
646 << "Bad test params: Impossible to animate faster than the compositor.";
647 }
648 };
649
650 // Value printer for Scenario.
651 ::std::ostream& operator<<(::std::ostream& os, const Scenario& s) {
652 return os << "{ vsync_interval=" << s.vsync_interval.InMicroseconds()
653 << ", min_capture_period=" << s.min_capture_period.InMicroseconds()
654 << ", content_period=" << s.content_period.InMicroseconds()
655 << " }";
656 }
657
658 base::TimeDelta FpsAsPeriod(int frame_rate) {
659 return base::TimeDelta::FromSeconds(1) / frame_rate;
660 }
661
486 } // namespace 662 } // namespace
663
664 class AnimatedContentSamplerParameterizedTest
665 : public AnimatedContentSamplerTest,
666 public ::testing::WithParamInterface<Scenario> {
667 public:
668 AnimatedContentSamplerParameterizedTest()
669 : count_dropped_frames_(0), count_sampled_frames_(0) {}
670 virtual ~AnimatedContentSamplerParameterizedTest() {}
671
672 protected:
673 typedef std::pair<gfx::Rect, base::TimeTicks> Event;
674
675 virtual base::TimeDelta GetMinCapturePeriod() const OVERRIDE {
676 return GetParam().min_capture_period;
677 }
678
679 // Generate a sequence of events from the compositor pipeline. The event
680 // times will all be at compositor vsync boundaries.
681 std::vector<Event> GenerateEventSequence(base::TimeTicks begin,
682 base::TimeTicks end,
683 bool include_content_frame_events,
684 bool include_random_events) {
685 DCHECK(GetParam().content_period >= GetParam().vsync_interval);
686 base::TimeTicks next_content_time = begin - GetParam().content_period;
687 std::vector<Event> events;
688 for (base::TimeTicks compositor_time = begin; compositor_time < end;
689 compositor_time += GetParam().vsync_interval) {
690 if (include_content_frame_events && next_content_time < compositor_time) {
691 events.push_back(Event(GetContentDamageRect(), compositor_time));
692 next_content_time += GetParam().content_period;
693 } else if (include_random_events && GetRandomInRange(0, 1) == 0) {
694 events.push_back(Event(GetRandomDamageRect(), compositor_time));
695 }
696 }
697
698 DCHECK(!events.empty());
699 return events;
700 }
701
702 // Feed |events| through the sampler, and detect whether the expected
703 // lock-in/out transition occurs. Also, track and measure the frame drop
704 // ratio and check it against the expected drop rate.
705 void RunEventSequence(const std::vector<Event> events,
706 bool was_detecting_before,
707 bool is_detecting_after,
708 bool simulate_pipeline_back_pressure) {
709 gfx::Rect first_detected_region;
710
711 EXPECT_EQ(was_detecting_before, sampler()->HasProposal());
712 bool has_detection_switched = false;
713 ResetFrameCounters();
714 for (std::vector<Event>::const_iterator i = events.begin();
715 i != events.end(); ++i) {
716 sampler()->ConsiderPresentationEvent(i->first, i->second);
717
718 // Detect when the sampler locks in/out, and that it stays that way for
719 // all further iterations of this loop.
720 if (!has_detection_switched &&
721 was_detecting_before != sampler()->HasProposal()) {
722 has_detection_switched = true;
723 }
724 ASSERT_EQ(
725 has_detection_switched ? is_detecting_after : was_detecting_before,
726 sampler()->HasProposal());
727
728 if (sampler()->HasProposal()) {
729 // Make sure the sampler doesn't flip-flop and keep proposing sampling
730 // based on locking into different regions.
731 if (first_detected_region.IsEmpty()) {
732 first_detected_region = sampler()->detected_region();
733 ASSERT_FALSE(first_detected_region.IsEmpty());
734 } else {
735 EXPECT_EQ(first_detected_region, sampler()->detected_region());
736 }
737
738 if (simulate_pipeline_back_pressure && GetRandomInRange(0, 2) == 0)
739 ClientCannotSampleFrame(*i);
740 else
741 ClientDoesWhatSamplerProposes(*i);
742 } else {
743 EXPECT_FALSE(sampler()->ShouldSample());
744 if (!simulate_pipeline_back_pressure || GetRandomInRange(0, 2) == 1)
745 sampler()->RecordSample(i->second);
746 }
747 }
748 EXPECT_EQ(is_detecting_after, sampler()->HasProposal());
749 ExpectFrameDropRatioIsCorrect();
750 }
751
752 void ResetFrameCounters() {
753 count_dropped_frames_ = 0;
754 count_sampled_frames_ = 0;
755 }
756
757 // Keep track what the sampler is proposing, and call RecordSample() if it
758 // proposes sampling |event|.
759 void ClientDoesWhatSamplerProposes(const Event& event) {
760 if (sampler()->ShouldSample()) {
761 EXPECT_EQ(GetContentDamageRect(), event.first);
762 sampler()->RecordSample(sampler()->frame_timestamp());
763 ++count_sampled_frames_;
764 } else if (event.first == GetContentDamageRect()) {
765 ++count_dropped_frames_;
766 }
767 }
768
769 // RecordSample() is not called, but for testing, keep track of what the
770 // sampler is proposing for |event|.
771 void ClientCannotSampleFrame(const Event& event) {
772 if (sampler()->ShouldSample()) {
773 EXPECT_EQ(GetContentDamageRect(), event.first);
774 ++count_sampled_frames_;
775 } else if (event.first == GetContentDamageRect()) {
776 ++count_dropped_frames_;
777 }
778 }
779
780 // Confirm the AnimatedContentSampler is not dropping more frames than
781 // expected, given current test parameters.
782 void ExpectFrameDropRatioIsCorrect() {
783 if (count_sampled_frames_ == 0) {
784 EXPECT_EQ(0, count_dropped_frames_);
785 return;
786 }
787 const double content_framerate =
788 1000000.0 / GetParam().content_period.InMicroseconds();
789 const double capture_framerate =
790 1000000.0 / GetParam().min_capture_period.InMicroseconds();
791 const double expected_drop_rate = std::max(
792 0.0, (content_framerate - capture_framerate) / capture_framerate);
793 const double actual_drop_rate =
794 static_cast<double>(count_dropped_frames_) / count_sampled_frames_;
795 EXPECT_NEAR(expected_drop_rate, actual_drop_rate, 0.015);
796 }
797
798 private:
799 // These counters only include the frames with the desired content.
800 int count_dropped_frames_;
801 int count_sampled_frames_;
802 };
803
804 // Tests that the implementation locks in/out of frames containing stable
805 // animated content, whether or not random events are also simultaneously
806 // present.
807 TEST_P(AnimatedContentSamplerParameterizedTest, DetectsAnimatedContent) {
808 // |begin| refers to the start of an event sequence in terms of the
809 // Compositor's clock.
810 base::TimeTicks begin = InitialTestTimeTicks();
811
812 // Provide random events and expect no lock-in.
813 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
814 RunEventSequence(GenerateEventSequence(begin, end, false, true),
815 false,
816 false,
817 false);
818 begin = end;
819
820 // Provide content frame events with some random events mixed-in, and expect
821 // the sampler to lock-in.
822 end = begin + base::TimeDelta::FromSeconds(5);
823 RunEventSequence(GenerateEventSequence(begin, end, true, true),
824 false,
825 true,
826 false);
827 begin = end;
828
829 // Continue providing content frame events without the random events mixed-in
830 // and expect the lock-in to hold.
831 end = begin + base::TimeDelta::FromSeconds(5);
832 RunEventSequence(GenerateEventSequence(begin, end, true, false),
833 true,
834 true,
835 false);
836 begin = end;
837
838 // Continue providing just content frame events and expect the lock-in to
839 // hold. Also simulate the capture pipeline experiencing back pressure.
840 end = begin + base::TimeDelta::FromSeconds(20);
841 RunEventSequence(GenerateEventSequence(begin, end, true, false),
842 true,
843 true,
844 true);
845 begin = end;
846
847 // Provide a half-second of random events only, and expect the lock-in to be
848 // broken.
849 end = begin + base::TimeDelta::FromMilliseconds(500);
850 RunEventSequence(GenerateEventSequence(begin, end, false, true),
851 true,
852 false,
853 false);
854 begin = end;
855
856 // Now, go back to providing content frame events, and expect the sampler to
857 // lock-in once again.
858 end = begin + base::TimeDelta::FromSeconds(5);
859 RunEventSequence(GenerateEventSequence(begin, end, true, false),
860 false,
861 true,
862 false);
863 begin = end;
864 }
865
866 // Tests that AnimatedContentSampler won't lock in to, nor flip-flop between,
867 // two animations of the same pixel change rate. VideoCaptureOracle should
868 // revert to using the SmoothEventSampler for these kinds of situations, as
869 // there is no "right answer" as to which animation to lock into.
870 TEST_P(AnimatedContentSamplerParameterizedTest,
871 DoesNotLockInToTwoCompetingAnimations) {
872 // Don't test when the event stream cannot indicate two separate content
873 // animations under the current test parameters.
874 if (GetParam().content_period < 2 * GetParam().vsync_interval)
875 return;
876
877 // Start the first animation and run for a bit, and expect the sampler to
878 // lock-in.
879 base::TimeTicks begin = InitialTestTimeTicks();
880 base::TimeTicks end = begin + base::TimeDelta::FromSeconds(5);
881 RunEventSequence(GenerateEventSequence(begin, end, true, false),
882 false,
883 true,
884 false);
885 begin = end;
886
887 // Now, keep the first animation and blend in an second animation of the same
888 // size and frame rate, but at a different position. This will should cause
889 // the sampler to enter an "undetected" state since it's unclear which
890 // animation should be locked into.
891 end = begin + base::TimeDelta::FromSeconds(20);
892 std::vector<Event> first_animation_events =
893 GenerateEventSequence(begin, end, true, false);
894 gfx::Rect second_animation_rect(
895 gfx::Point(0, GetContentDamageRect().height()),
896 GetContentDamageRect().size());
897 std::vector<Event> both_animations_events;
898 base::TimeDelta second_animation_offset = GetParam().vsync_interval;
899 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
900 i != first_animation_events.end(); ++i) {
901 both_animations_events.push_back(*i);
902 both_animations_events.push_back(
903 Event(second_animation_rect, i->second + second_animation_offset));
904 }
905 RunEventSequence(both_animations_events, true, false, false);
906 begin = end;
907
908 // Now, run just the first animation, and expect the sampler to lock-in once
909 // again.
910 end = begin + base::TimeDelta::FromSeconds(5);
911 RunEventSequence(GenerateEventSequence(begin, end, true, false),
912 false,
913 true,
914 false);
915 begin = end;
916
917 // Now, blend in the second animation again, but it has half the frame rate of
918 // the first animation and damage Rects with twice the area. This will should
919 // cause the sampler to enter an "undetected" state again. This tests that
920 // pixel-weighting is being accounted for in the sampler's logic.
921 end = begin + base::TimeDelta::FromSeconds(20);
922 first_animation_events = GenerateEventSequence(begin, end, true, false);
923 second_animation_rect.set_width(second_animation_rect.width() * 2);
924 both_animations_events.clear();
925 bool include_second_animation_frame = true;
926 for (std::vector<Event>::const_iterator i = first_animation_events.begin();
927 i != first_animation_events.end(); ++i) {
928 both_animations_events.push_back(*i);
929 if (include_second_animation_frame) {
930 both_animations_events.push_back(
931 Event(second_animation_rect, i->second + second_animation_offset));
932 }
933 include_second_animation_frame = !include_second_animation_frame;
934 }
935 RunEventSequence(both_animations_events, true, false, false);
936 begin = end;
937 }
938
939 // Tests that the frame timestamps are smooth; meaning, that when run through a
940 // simulated compositor, each frame is held displayed for the right number of
941 // v-sync intervals.
942 TEST_P(AnimatedContentSamplerParameterizedTest, FrameTimestampsAreSmooth) {
943 // Generate 30 seconds of animated content events, run the events through
944 // AnimatedContentSampler, and record all frame timestamps being proposed
945 // once lock-in is continuous.
946 base::TimeTicks begin = InitialTestTimeTicks();
947 std::vector<Event> events = GenerateEventSequence(
948 begin,
949 begin + base::TimeDelta::FromSeconds(20),
950 true,
951 false);
952 typedef std::vector<base::TimeTicks> Timestamps;
953 Timestamps frame_timestamps;
954 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
955 ++i) {
956 sampler()->ConsiderPresentationEvent(i->first, i->second);
957 if (sampler()->HasProposal()) {
958 if (sampler()->ShouldSample()) {
959 frame_timestamps.push_back(sampler()->frame_timestamp());
960 sampler()->RecordSample(sampler()->frame_timestamp());
961 }
962 } else {
963 frame_timestamps.clear(); // Reset until continuous lock-in.
964 }
965 }
966 ASSERT_LE(2u, frame_timestamps.size());
967
968 // Iterate through the |frame_timestamps|, building a histogram counting the
969 // number of times each frame was displayed k times. For example, 10 frames
970 // of 30 Hz content on a 60 Hz v-sync interval should result in
971 // display_counts[2] == 10. Quit early if any one frame was obviously
972 // repeated too many times.
973 const int64 max_expected_repeats_per_frame = 1 +
974 std::max(GetParam().min_capture_period, GetParam().content_period) /
975 GetParam().vsync_interval;
976 std::vector<size_t> display_counts(max_expected_repeats_per_frame + 1, 0);
977 base::TimeTicks last_present_time = frame_timestamps.front();
978 for (Timestamps::const_iterator i = frame_timestamps.begin() + 1;
979 i != frame_timestamps.end(); ++i) {
980 const size_t num_vsync_intervals = static_cast<size_t>(
981 (*i - last_present_time) / GetParam().vsync_interval);
982 ASSERT_LT(0u, num_vsync_intervals);
983 ASSERT_GT(display_counts.size(), num_vsync_intervals); // Quit early.
984 ++display_counts[num_vsync_intervals];
985 last_present_time += num_vsync_intervals * GetParam().vsync_interval;
986 }
987
988 // Analyze the histogram for an expected result pattern. If the frame
989 // timestamps are smooth, there should only be one or two buckets with
990 // non-zero counts and they should be next to each other. Because the clock
991 // precision for the event_times provided to the sampler is very granular
992 // (i.e., the vsync_interval), it's okay if other buckets have a tiny "stray"
993 // count in this test.
994 size_t highest_count = 0;
995 size_t second_highest_count = 0;
996 for (size_t repeats = 1; repeats < display_counts.size(); ++repeats) {
997 DVLOG(1) << "display_counts[" << repeats << "] is "
998 << display_counts[repeats];
999 if (display_counts[repeats] >= highest_count) {
1000 second_highest_count = highest_count;
1001 highest_count = display_counts[repeats];
1002 } else if (display_counts[repeats] > second_highest_count) {
1003 second_highest_count = display_counts[repeats];
1004 }
1005 }
1006 size_t stray_count_remaining =
1007 (frame_timestamps.size() - 1) - (highest_count + second_highest_count);
1008 // Expect no more than 0.75% of frames fall outside the two main buckets.
1009 EXPECT_GT(frame_timestamps.size() * 75 / 10000, stray_count_remaining);
1010 for (size_t repeats = 1; repeats < display_counts.size() - 1; ++repeats) {
1011 if (display_counts[repeats] == highest_count) {
1012 EXPECT_EQ(second_highest_count, display_counts[repeats + 1]);
1013 ++repeats;
1014 } else if (display_counts[repeats] == second_highest_count) {
1015 EXPECT_EQ(highest_count, display_counts[repeats + 1]);
1016 ++repeats;
1017 } else {
1018 EXPECT_GE(stray_count_remaining, display_counts[repeats]);
1019 stray_count_remaining -= display_counts[repeats];
1020 }
1021 }
1022 }
1023
1024 // Tests that frame timestamps are "lightly pushed" back towards the original
1025 // presentation event times, which tells us the AnimatedContentSampler can
1026 // account for sources of timestamp drift and correct the drift.
1027 TEST_P(AnimatedContentSamplerParameterizedTest,
1028 FrameTimestampsConvergeTowardsEventTimes) {
1029 const int max_drift_increment_millis = 3;
1030
1031 // Generate a full minute of events.
1032 const base::TimeTicks begin = InitialTestTimeTicks();
1033 const base::TimeTicks end = begin + base::TimeDelta::FromMinutes(1);
1034 std::vector<Event> events = GenerateEventSequence(begin, end, true, false);
1035
1036 // Modify the event sequence so that 1-3 ms of additional drift is suddenly
1037 // present every 100 events. This is meant to simulate that, external to
1038 // AnimatedContentSampler, the video hardware vsync timebase is being
1039 // refreshed and is showing severe drift from the system clock.
1040 base::TimeDelta accumulated_drift;
1041 for (size_t i = 1; i < events.size(); ++i) {
1042 if (i % 100 == 0) {
1043 accumulated_drift += base::TimeDelta::FromMilliseconds(
1044 GetRandomInRange(1, max_drift_increment_millis + 1));
1045 }
1046 events[i].second += accumulated_drift;
1047 }
1048
1049 // Run all the events through the sampler and track the last rewritten frame
1050 // timestamp.
1051 base::TimeTicks last_frame_timestamp;
1052 for (std::vector<Event>::const_iterator i = events.begin(); i != events.end();
1053 ++i) {
1054 sampler()->ConsiderPresentationEvent(i->first, i->second);
1055 if (sampler()->ShouldSample())
1056 last_frame_timestamp = sampler()->frame_timestamp();
1057 }
1058
1059 // If drift was accounted for, the |last_frame_timestamp| should be close to
1060 // the last event's timestamp.
1061 const base::TimeDelta total_error =
1062 events.back().second - last_frame_timestamp;
1063 const base::TimeDelta max_acceptable_error = GetParam().min_capture_period +
1064 base::TimeDelta::FromMilliseconds(max_drift_increment_millis);
1065 EXPECT_NEAR(0.0,
1066 total_error.InMicroseconds(),
1067 max_acceptable_error.InMicroseconds());
1068 }
1069
1070 INSTANTIATE_TEST_CASE_P(
1071 ,
1072 AnimatedContentSamplerParameterizedTest,
1073 ::testing::Values(
1074 // Typical frame rate content: Compositor runs at 60 Hz, capture at 30
1075 // Hz, and content video animates at 30, 25, or 24 Hz.
1076 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(30)),
1077 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(25)),
1078 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(24)),
1079
1080 // High frame rate content that leverages the Compositor's
1081 // capabilities, but capture is still at 30 Hz.
1082 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(60)),
1083 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(50)),
1084 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(48)),
1085
1086 // High frame rate content that leverages the Compositor's
1087 // capabilities, and capture is also a buttery 60 Hz.
1088 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(60)),
1089 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(50)),
1090 Scenario(FpsAsPeriod(60), FpsAsPeriod(60), FpsAsPeriod(48)),
1091
1092 // On some platforms, the Compositor runs at 50 Hz.
1093 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(30)),
1094 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(25)),
1095 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(24)),
1096 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(50)),
1097 Scenario(FpsAsPeriod(50), FpsAsPeriod(30), FpsAsPeriod(48)),
1098
1099 // Stable, but non-standard content frame rates.
1100 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(16)),
1101 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(20)),
1102 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(23)),
1103 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(26)),
1104 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(27)),
1105 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(28)),
1106 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(29)),
1107 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(31)),
1108 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(32)),
1109 Scenario(FpsAsPeriod(60), FpsAsPeriod(30), FpsAsPeriod(33))));
1110
1111 // Tests that VideoCaptureOracle filters out events whose timestamps are
1112 // decreasing.
1113 TEST(VideoCaptureOracleTest, EnforcesEventTimeMonotonicity) {
1114 const base::TimeDelta min_capture_period =
1115 base::TimeDelta::FromSeconds(1) / 30;
1116 const gfx::Rect damage_rect(0, 0, 1280, 720);
1117 const base::TimeDelta event_increment = min_capture_period * 2;
1118
1119 VideoCaptureOracle oracle(min_capture_period, true);
1120
1121 base::TimeTicks t = InitialTestTimeTicks();
1122 for (int i = 0; i < 10; ++i) {
1123 t += event_increment;
1124 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1125 VideoCaptureOracle::kCompositorUpdate,
1126 damage_rect, t));
1127 }
1128
1129 base::TimeTicks furthest_event_time = t;
1130 for (int i = 0; i < 10; ++i) {
1131 t -= event_increment;
1132 ASSERT_FALSE(oracle.ObserveEventAndDecideCapture(
1133 VideoCaptureOracle::kCompositorUpdate,
1134 damage_rect, t));
1135 }
1136
1137 t = furthest_event_time;
1138 for (int i = 0; i < 10; ++i) {
1139 t += event_increment;
1140 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1141 VideoCaptureOracle::kCompositorUpdate,
1142 damage_rect, t));
1143 }
1144 }
1145
1146 // Tests that VideoCaptureOracle is enforcing the requirement that captured
1147 // frames are delivered in order. Otherwise, downstream consumers could be
1148 // tripped-up by out-of-order frames or frame timestamps.
1149 TEST(VideoCaptureOracleTest, EnforcesFramesDeliveredInOrder) {
1150 const base::TimeDelta min_capture_period =
1151 base::TimeDelta::FromSeconds(1) / 30;
1152 const gfx::Rect damage_rect(0, 0, 1280, 720);
1153 const base::TimeDelta event_increment = min_capture_period * 2;
1154
1155 VideoCaptureOracle oracle(min_capture_period, true);
1156
1157 // Most basic scenario: Frames delivered one at a time, with no additional
1158 // captures in-between deliveries.
1159 base::TimeTicks t = InitialTestTimeTicks();
1160 int last_frame_number;
1161 base::TimeTicks ignored;
1162 for (int i = 0; i < 10; ++i) {
1163 t += event_increment;
1164 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1165 VideoCaptureOracle::kCompositorUpdate,
1166 damage_rect, t));
1167 last_frame_number = oracle.RecordCapture();
1168 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored));
1169 }
1170
1171 // Basic pipelined scenario: More than one frame in-flight at delivery points.
1172 for (int i = 0; i < 50; ++i) {
1173 const int num_in_flight = 1 + i % 3;
1174 for (int j = 0; j < num_in_flight; ++j) {
1175 t += event_increment;
1176 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1177 VideoCaptureOracle::kCompositorUpdate,
1178 damage_rect, t));
1179 last_frame_number = oracle.RecordCapture();
1180 }
1181 for (int j = num_in_flight - 1; j >= 0; --j) {
1182 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number - j, &ignored));
1183 }
1184 }
1185
1186 // Pipelined scenario with out-of-order delivery attempts rejected.
1187 for (int i = 0; i < 50; ++i) {
1188 const int num_in_flight = 1 + i % 3;
1189 for (int j = 0; j < num_in_flight; ++j) {
1190 t += event_increment;
1191 ASSERT_TRUE(oracle.ObserveEventAndDecideCapture(
1192 VideoCaptureOracle::kCompositorUpdate,
1193 damage_rect, t));
1194 last_frame_number = oracle.RecordCapture();
1195 }
1196 ASSERT_TRUE(oracle.CompleteCapture(last_frame_number, &ignored));
1197 for (int j = 1; j < num_in_flight; ++j) {
1198 ASSERT_FALSE(oracle.CompleteCapture(last_frame_number - j, &ignored));
1199 }
1200 }
1201 }
1202
1203 // Tests that VideoCaptureOracle transitions between using its two samplers in a
1204 // way that does not introduce severe jank, pauses, etc.
1205 TEST(VideoCaptureOracleTest, TransitionsSmoothlyBetweenSamplers) {
1206 const base::TimeDelta min_capture_period =
1207 base::TimeDelta::FromSeconds(1) / 30;
1208 const gfx::Rect animation_damage_rect(0, 0, 1280, 720);
1209 const base::TimeDelta event_increment = min_capture_period * 2;
1210
1211 VideoCaptureOracle oracle(min_capture_period, true);
1212
1213 // Run sequences of animation events and non-animation events through the
1214 // oracle. As the oracle transitions between each sampler, make sure the
1215 // frame timestamps won't trip-up downstream consumers.
1216 base::TimeTicks t = InitialTestTimeTicks();
1217 base::TimeTicks last_frame_timestamp;
1218 for (int i = 0; i < 1000; ++i) {
1219 t += event_increment;
1220
1221 // For every 100 events, provide 50 that will cause the
1222 // AnimatedContentSampler to lock-in, followed by 50 that will cause it to
1223 // lock-out (i.e., the oracle will use the SmoothEventSampler instead).
1224 const bool provide_animated_content_event =
1225 (i % 100) >= 25 && (i % 100) < 75;
1226
1227 // Only the few events that trigger the lock-out transition should be
1228 // dropped, because the AnimatedContentSampler doesn't yet realize the
1229 // animation ended. Otherwise, the oracle should always decide to sample
1230 // because one of its samplers says to.
1231 const bool require_oracle_says_sample = (i % 100) < 75 || (i % 100) >= 78;
1232 const bool oracle_says_sample = oracle.ObserveEventAndDecideCapture(
1233 VideoCaptureOracle::kCompositorUpdate,
1234 provide_animated_content_event ? animation_damage_rect : gfx::Rect(),
1235 t);
1236 if (require_oracle_says_sample)
1237 ASSERT_TRUE(oracle_says_sample);
1238 if (!oracle_says_sample)
1239 continue;
1240
1241 const int frame_number = oracle.RecordCapture();
1242
1243 base::TimeTicks frame_timestamp;
1244 ASSERT_TRUE(oracle.CompleteCapture(frame_number, &frame_timestamp));
1245 ASSERT_FALSE(frame_timestamp.is_null());
1246 if (!last_frame_timestamp.is_null()) {
1247 const base::TimeDelta delta = frame_timestamp - last_frame_timestamp;
1248 EXPECT_LE(event_increment.InMicroseconds(), delta.InMicroseconds());
1249 // Right after the AnimatedContentSampler lock-out transition, there were
1250 // a few frames dropped, so allow a gap in the timestamps. Otherwise, the
1251 // delta between frame timestamps should never be more than 2X the
1252 // |event_increment|.
1253 const base::TimeDelta max_acceptable_delta = (i % 100) == 78 ?
1254 event_increment * 5 : event_increment * 2;
1255 EXPECT_GE(max_acceptable_delta.InMicroseconds(), delta.InMicroseconds());
1256 }
1257 last_frame_timestamp = frame_timestamp;
1258 }
1259 }
1260
487 } // namespace content 1261 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698