Flutter macOS Embedder
FlutterVSyncWaiterTest.mm File Reference

Go to the source code of this file.

Classes

class  TestDisplayLink
 
class  FlutterVSyncWaiterTest
 

Functions

 TEST_F (FlutterVSyncWaiterTest, RequestsInitialVSync)
 
static void BusyWait (CFTimeInterval duration)
 
 TEST_F (FlutterVSyncWaiterTest, FirstVSyncIsSynthesized)
 
 TEST_F (FlutterVSyncWaiterTest, VSyncWorks)
 

Variables

static const CFTimeInterval kTimerLatencyCompensation = 0.001
 

Function Documentation

◆ BusyWait()

static void BusyWait ( CFTimeInterval  duration)
static

Definition at line 60 of file FlutterVSyncWaiterTest.mm.

60  {
61  CFTimeInterval start = CACurrentMediaTime();
62  while (CACurrentMediaTime() < start + duration) {
63  }
64 }

Referenced by TEST_F().

◆ TEST_F() [1/3]

TEST_F ( FlutterVSyncWaiterTest  ,
FirstVSyncIsSynthesized   
)

Definition at line 69 of file FlutterVSyncWaiterTest.mm.

69  {
70  TestDisplayLink* displayLink = [[TestDisplayLink alloc] init];
71  displayLink.nominalOutputRefreshPeriod = 1.0 / 60.0;
72 
73  auto test = [&](CFTimeInterval waitDuration, CFTimeInterval expectedDelay) {
74  __block CFTimeInterval timestamp = 0;
75  __block CFTimeInterval targetTimestamp = 0;
76  __block size_t baton = 0;
77  const uintptr_t kWarmUpBaton = 0xFFFFFFFF;
78  FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
79  initWithDisplayLink:displayLink
80  block:^(CFTimeInterval _timestamp, CFTimeInterval _targetTimestamp,
81  uintptr_t _baton) {
82  if (_baton == kWarmUpBaton) {
83  return;
84  }
85  timestamp = _timestamp;
86  targetTimestamp = _targetTimestamp;
87  baton = _baton;
88  EXPECT_TRUE(CACurrentMediaTime() >= _timestamp - kTimerLatencyCompensation);
89  CFRunLoopStop(CFRunLoopGetCurrent());
90  }];
91 
92  [waiter waitForVSync:kWarmUpBaton];
93 
94  // Reference vsync to setup phase.
95  CFTimeInterval now = CACurrentMediaTime();
96  // CVDisplayLink callback is called one and a half frame before the target.
97  [displayLink tickWithTimestamp:now + 0.5 * displayLink.nominalOutputRefreshPeriod
98  targetTimestamp:now + 2 * displayLink.nominalOutputRefreshPeriod];
99  EXPECT_EQ(displayLink.paused, YES);
100  // Vsync was not requested yet, block should not have been called.
101  EXPECT_EQ(timestamp, 0);
102 
103  BusyWait(waitDuration);
104 
105  // Synthesized vsync should come in 1/60th of a second after the first.
106  CFTimeInterval expectedTimestamp = now + expectedDelay;
107  [waiter waitForVSync:1];
108 
109  CFRunLoopRun();
110 
111  EXPECT_DOUBLE_EQ(timestamp, expectedTimestamp);
112  EXPECT_DOUBLE_EQ(targetTimestamp, expectedTimestamp + displayLink.nominalOutputRefreshPeriod);
113  EXPECT_EQ(baton, size_t(1));
114  };
115 
116  // First argument if the wait duration after reference vsync.
117  // Second argument is the expected delay between reference vsync and synthesized vsync.
118  test(0.005, displayLink.nominalOutputRefreshPeriod);
119  test(0.025, 2 * displayLink.nominalOutputRefreshPeriod);
120  test(0.040, 3 * displayLink.nominalOutputRefreshPeriod);
121 }
static const CFTimeInterval kTimerLatencyCompensation
static void BusyWait(CFTimeInterval duration)

References BusyWait(), TestDisplayLink::nominalOutputRefreshPeriod, FlutterDisplayLink::paused, and FlutterVSyncWaiter::waitForVSync:.

◆ TEST_F() [2/3]

TEST_F ( FlutterVSyncWaiterTest  ,
RequestsInitialVSync   
)

Definition at line 44 of file FlutterVSyncWaiterTest.mm.

44  {
45  TestDisplayLink* displayLink = [[TestDisplayLink alloc] init];
46  EXPECT_TRUE(displayLink.paused);
47  // When created waiter requests a reference vsync to determine vsync phase.
48  FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
49  initWithDisplayLink:displayLink
50  block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
51  uintptr_t baton){
52  }];
53  (void)waiter;
54  EXPECT_FALSE(displayLink.paused);
55  [displayLink tickWithTimestamp:CACurrentMediaTime()
56  targetTimestamp:CACurrentMediaTime() + 1.0 / 60.0];
57  EXPECT_TRUE(displayLink.paused);
58 }

References FlutterDisplayLink::paused.

◆ TEST_F() [3/3]

TEST_F ( FlutterVSyncWaiterTest  ,
VSyncWorks   
)

Definition at line 123 of file FlutterVSyncWaiterTest.mm.

123  {
124  TestDisplayLink* displayLink = [[TestDisplayLink alloc] init];
125  displayLink.nominalOutputRefreshPeriod = 1.0 / 60.0;
126  const uintptr_t kWarmUpBaton = 0xFFFFFFFF;
127 
128  struct Entry {
129  CFTimeInterval timestamp;
130  CFTimeInterval targetTimestamp;
131  size_t baton;
132  };
133  __block std::vector<Entry> entries;
134 
135  FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
136  initWithDisplayLink:displayLink
137  block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
138  uintptr_t baton) {
139  entries.push_back({timestamp, targetTimestamp, baton});
140  if (baton == kWarmUpBaton) {
141  return;
142  }
143  EXPECT_TRUE(CACurrentMediaTime() >= timestamp - kTimerLatencyCompensation);
144  CFRunLoopStop(CFRunLoopGetCurrent());
145  }];
146 
147  __block CFTimeInterval expectedStartUntil;
148  // Warm up tick is scheduled immediately in a scheduled block. Schedule another
149  // block here to determine the maximum time when the warm up tick should be
150  // scheduled.
151  [waiter waitForVSync:kWarmUpBaton];
152  [[NSRunLoop currentRunLoop] performBlock:^{
153  expectedStartUntil = CACurrentMediaTime();
154  }];
155 
156  // Reference vsync to setup phase.
157  CFTimeInterval now = CACurrentMediaTime();
158  // CVDisplayLink callback is called one and a half frame before the target.
159  [displayLink tickWithTimestamp:now + 0.5 * displayLink.nominalOutputRefreshPeriod
160  targetTimestamp:now + 2 * displayLink.nominalOutputRefreshPeriod];
161  EXPECT_EQ(displayLink.paused, YES);
162 
163  [waiter waitForVSync:1];
164  CFRunLoopRun();
165 
166  [waiter waitForVSync:2];
167  [displayLink tickWithTimestamp:now + 1.5 * displayLink.nominalOutputRefreshPeriod
168  targetTimestamp:now + 3 * displayLink.nominalOutputRefreshPeriod];
169  CFRunLoopRun();
170 
171  [waiter waitForVSync:3];
172  [displayLink tickWithTimestamp:now + 2.5 * displayLink.nominalOutputRefreshPeriod
173  targetTimestamp:now + 4 * displayLink.nominalOutputRefreshPeriod];
174  CFRunLoopRun();
175 
176  EXPECT_FALSE(displayLink.paused);
177  // Vsync without baton should pause the display link.
178  [displayLink tickWithTimestamp:now + 3.5 * displayLink.nominalOutputRefreshPeriod
179  targetTimestamp:now + 5 * displayLink.nominalOutputRefreshPeriod];
180 
181  CFTimeInterval start = CACurrentMediaTime();
182  while (!displayLink.paused) {
183  // Make sure to run the timer scheduled in display link callback.
184  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.02, NO);
185  if (CACurrentMediaTime() - start > 1.0) {
186  break;
187  }
188  }
189  ASSERT_TRUE(displayLink.paused);
190 
191  EXPECT_EQ(entries.size(), size_t(4));
192 
193  // Warm up frame should be presented as soon as possible.
194  EXPECT_TRUE(entries[0].timestamp <= expectedStartUntil);
195  EXPECT_TRUE(entries[0].targetTimestamp <= expectedStartUntil);
196  EXPECT_EQ(entries[0].baton, kWarmUpBaton);
197 
198  EXPECT_DOUBLE_EQ(entries[1].timestamp, now + displayLink.nominalOutputRefreshPeriod);
199  EXPECT_DOUBLE_EQ(entries[1].targetTimestamp, now + 2 * displayLink.nominalOutputRefreshPeriod);
200  EXPECT_EQ(entries[1].baton, size_t(1));
201  EXPECT_DOUBLE_EQ(entries[2].timestamp, now + 2 * displayLink.nominalOutputRefreshPeriod);
202  EXPECT_DOUBLE_EQ(entries[2].targetTimestamp, now + 3 * displayLink.nominalOutputRefreshPeriod);
203  EXPECT_EQ(entries[2].baton, size_t(2));
204  EXPECT_DOUBLE_EQ(entries[3].timestamp, now + 3 * displayLink.nominalOutputRefreshPeriod);
205  EXPECT_DOUBLE_EQ(entries[3].targetTimestamp, now + 4 * displayLink.nominalOutputRefreshPeriod);
206  EXPECT_EQ(entries[3].baton, size_t(3));
207 }

References TestDisplayLink::nominalOutputRefreshPeriod, FlutterDisplayLink::paused, and FlutterVSyncWaiter::waitForVSync:.

Variable Documentation

◆ kTimerLatencyCompensation

const CFTimeInterval kTimerLatencyCompensation = 0.001
static

Definition at line 67 of file FlutterVSyncWaiterTest.mm.