Flutter macOS Embedder
FlutterDisplayLinkTest.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
7 
8 #import <AppKit/AppKit.h>
9 #include <numeric>
10 
11 #include "flutter/fml/synchronization/waitable_event.h"
12 #include "flutter/testing/testing.h"
13 
15  void (^_block)(CFTimeInterval timestamp, CFTimeInterval targetTimestamp);
16 }
17 
18 - (instancetype)initWithBlock:(void (^)(CFTimeInterval timestamp,
19  CFTimeInterval targetTimestamp))block;
20 
21 @end
22 
23 @implementation TestDisplayLinkDelegate
24 - (instancetype)initWithBlock:(void (^__strong)(CFTimeInterval, CFTimeInterval))block {
25  if (self = [super init]) {
26  _block = block;
27  }
28  return self;
29 }
30 
31 - (void)onDisplayLink:(CFTimeInterval)timestamp targetTimestamp:(CFTimeInterval)targetTimestamp {
32  _block(timestamp, targetTimestamp);
33 }
34 
35 @end
36 
37 class FlutterDisplayLinkTest : public testing::Test {
38  public:
40 };
41 
42 TEST_F(FlutterDisplayLinkTest, ViewAddedToWindowFirst) {
43  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
44  styleMask:NSWindowStyleMaskTitled
45  backing:NSBackingStoreNonretained
46  defer:NO];
47  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
48  [window setContentView:view];
49 
50  __block BOOL signalled = NO;
51 
53  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
54  signalled = YES;
55  }];
56 
58  displayLink.delegate = delegate;
59  displayLink.paused = NO;
60 
61  while (!signalled) {
63  }
64 
65  [displayLink invalidate];
66 }
67 
68 TEST_F(FlutterDisplayLinkTest, ViewAddedToWindowLater) {
69  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
70 
71  __block BOOL signalled = NO;
72 
74  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
75  signalled = YES;
76  }];
77 
79  displayLink.delegate = delegate;
80  displayLink.paused = NO;
81 
82  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
83  styleMask:NSWindowStyleMaskTitled
84  backing:NSBackingStoreNonretained
85  defer:NO];
86  [window setContentView:view];
87 
88  while (!signalled) {
90  }
91 
92  [displayLink invalidate];
93 }
94 
95 TEST_F(FlutterDisplayLinkTest, ViewRemovedFromWindow) {
96  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
97  styleMask:NSWindowStyleMaskTitled
98  backing:NSBackingStoreNonretained
99  defer:NO];
100  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
101  [window setContentView:view];
102 
103  __block BOOL signalled = NO;
104 
106  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
107  signalled = YES;
108  }];
109 
111  displayLink.delegate = delegate;
112  displayLink.paused = NO;
113 
114  while (!signalled) {
116  }
117  displayLink.paused = YES;
118 
119  signalled = false;
120 
121  displayLink.paused = NO;
122 
123  [window setContentView:nil];
124 
125  CFTimeInterval start = CACurrentMediaTime();
126  while (CACurrentMediaTime() < start + 0.1) {
128  }
129 
130  EXPECT_FALSE(signalled);
131 
132  [displayLink invalidate];
133 }
134 
135 TEST_F(FlutterDisplayLinkTest, CVDisplayLinkInterval) {
136  CVDisplayLinkRef link;
137  CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &link);
138  __block CFTimeInterval last = 0;
139  auto intervals = std::make_shared<std::vector<CFTimeInterval>>();
140  auto event = std::make_shared<fml::AutoResetWaitableEvent>();
141  CVDisplayLinkSetOutputHandler(
142  link, ^(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow,
143  const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut) {
144  if (last != 0) {
145  intervals->push_back(CACurrentMediaTime() - last);
146  }
147  last = CACurrentMediaTime();
148  if (intervals->size() == 10) {
149  event->Signal();
150  }
151  return 0;
152  });
153 
154  CVDisplayLinkStart(link);
155  event->Wait();
156  CVDisplayLinkStop(link);
157  CVDisplayLinkRelease(link);
158  CFTimeInterval average = std::reduce(intervals->begin(), intervals->end()) / intervals->size();
159  CFTimeInterval max = *std::max_element(intervals->begin(), intervals->end());
160  CFTimeInterval min = *std::min_element(intervals->begin(), intervals->end());
161  NSLog(@"CVDisplayLink Interval: Average: %fs, Max: %fs, Min: %fs", average, max, min);
162 }
void ensureMainLoopInitialized()
void pollFlutterMessagesOnce()