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 
6 
7 #import <AppKit/AppKit.h>
8 
9 #include <numeric>
10 
11 #include "flutter/fml/synchronization/waitable_event.h"
12 #import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
13 #include "flutter/testing/testing.h"
14 
16  void (^_block)(CFTimeInterval timestamp, CFTimeInterval targetTimestamp);
17 }
18 
19 - (instancetype)initWithBlock:(void (^)(CFTimeInterval timestamp,
20  CFTimeInterval targetTimestamp))block;
21 
22 @end
23 
24 @implementation TestDisplayLinkDelegate
25 - (instancetype)initWithBlock:(void (^__strong)(CFTimeInterval, CFTimeInterval))block {
26  if (self = [super init]) {
27  _block = block;
28  }
29  return self;
30 }
31 
32 - (void)onDisplayLink:(CFTimeInterval)timestamp targetTimestamp:(CFTimeInterval)targetTimestamp {
33  _block(timestamp, targetTimestamp);
34 }
35 
36 @end
37 
38 class FlutterDisplayLinkTest : public testing::Test {
39  public:
40  void SetUp() override { [FlutterRunLoop ensureMainLoopInitialized]; }
41 };
42 
43 TEST_F(FlutterDisplayLinkTest, ViewAddedToWindowFirst) {
44  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
45  styleMask:NSWindowStyleMaskTitled
46  backing:NSBackingStoreNonretained
47  defer:NO];
48  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
49  [window setContentView:view];
50 
51  __block BOOL signalled = NO;
52 
54  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
55  signalled = YES;
56  }];
57 
59  displayLink.delegate = delegate;
60  displayLink.paused = NO;
61 
62  while (!signalled) {
63  [FlutterRunLoop.mainRunLoop pollFlutterMessagesOnce];
64  }
65 
66  [displayLink invalidate];
67 }
68 
69 TEST_F(FlutterDisplayLinkTest, ViewAddedToWindowLater) {
70  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
71 
72  __block BOOL signalled = NO;
73 
75  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
76  signalled = YES;
77  }];
78 
80  displayLink.delegate = delegate;
81  displayLink.paused = NO;
82 
83  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
84  styleMask:NSWindowStyleMaskTitled
85  backing:NSBackingStoreNonretained
86  defer:NO];
87  [window setContentView:view];
88 
89  while (!signalled) {
90  [FlutterRunLoop.mainRunLoop pollFlutterMessagesOnce];
91  }
92 
93  [displayLink invalidate];
94 }
95 
96 TEST_F(FlutterDisplayLinkTest, ViewRemovedFromWindow) {
97  NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
98  styleMask:NSWindowStyleMaskTitled
99  backing:NSBackingStoreNonretained
100  defer:NO];
101  NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
102  [window setContentView:view];
103 
104  __block BOOL signalled = NO;
105 
107  initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
108  signalled = YES;
109  }];
110 
112  displayLink.delegate = delegate;
113  displayLink.paused = NO;
114 
115  while (!signalled) {
116  [FlutterRunLoop.mainRunLoop pollFlutterMessagesOnce];
117  }
118  displayLink.paused = YES;
119 
120  signalled = false;
121 
122  displayLink.paused = NO;
123 
124  [window setContentView:nil];
125 
126  CFTimeInterval start = CACurrentMediaTime();
127  while (CACurrentMediaTime() < start + 0.1) {
128  [FlutterRunLoop.mainRunLoop pollFlutterMessagesOnce];
129  }
130 
131  EXPECT_FALSE(signalled);
132 
133  [displayLink invalidate];
134 }
135 
136 TEST_F(FlutterDisplayLinkTest, CVDisplayLinkInterval) {
137  CVDisplayLinkRef link;
138  CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &link);
139  __block CFTimeInterval last = 0;
140  auto intervals = std::make_shared<std::vector<CFTimeInterval>>();
141  auto event = std::make_shared<fml::AutoResetWaitableEvent>();
142  CVDisplayLinkSetOutputHandler(
143  link, ^(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow,
144  const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut) {
145  if (last != 0) {
146  intervals->push_back(CACurrentMediaTime() - last);
147  }
148  last = CACurrentMediaTime();
149  if (intervals->size() == 10) {
150  event->Signal();
151  }
152  return 0;
153  });
154 
155  CVDisplayLinkStart(link);
156  event->Wait();
157  CVDisplayLinkStop(link);
158  CVDisplayLinkRelease(link);
159  CFTimeInterval average = std::reduce(intervals->begin(), intervals->end()) / intervals->size();
160  CFTimeInterval max = *std::max_element(intervals->begin(), intervals->end());
161  CFTimeInterval min = *std::min_element(intervals->begin(), intervals->end());
162  NSLog(@"CVDisplayLink Interval: Average: %fs, Max: %fs, Min: %fs", average, max, min);
163 }