Flutter macOS Embedder
FlutterVSyncWaiterTest.mm File Reference
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDisplayLink.h"
#import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.h"
#import "flutter/testing/testing.h"

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 61 of file FlutterVSyncWaiterTest.mm.

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

Referenced by TEST_F().

◆ TEST_F() [1/3]

TEST_F ( FlutterVSyncWaiterTest  ,
FirstVSyncIsSynthesized   
)

Definition at line 70 of file FlutterVSyncWaiterTest.mm.

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

References BusyWait(), FlutterVSyncWaiter::invalidate, 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  [waiter invalidate];
59 }

References FlutterVSyncWaiter::invalidate, and FlutterDisplayLink::paused.

◆ TEST_F() [3/3]

TEST_F ( FlutterVSyncWaiterTest  ,
VSyncWorks   
)

Definition at line 126 of file FlutterVSyncWaiterTest.mm.

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

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

Variable Documentation

◆ kTimerLatencyCompensation

const CFTimeInterval kTimerLatencyCompensation = 0.001
static

Definition at line 68 of file FlutterVSyncWaiterTest.mm.