| 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
5 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDisplayLink.h"
6
7 #import <AppKit/AppKit.h>
8 #include <numeric>
9
10 #include "flutter/fml/synchronization/waitable_event.h"
11 #include "flutter/testing/testing.h"
12
13 @interface TestDisplayLinkDelegate : NSObject <FlutterDisplayLinkDelegate> {
14 void (^_block)(CFTimeInterval timestamp, CFTimeInterval targetTimestamp);
15 }
16
17 - (instancetype)initWithBlock:(void (^)(CFTimeInterval timestamp,
18 CFTimeInterval targetTimestamp))block;
19
20 @end
21
22 @implementation TestDisplayLinkDelegate
23 - (instancetype)initWithBlock:(void (^__strong)(CFTimeInterval, CFTimeInterval))block {
24 if (self = [super init]) {
25 _block = block;
26 }
27 return self;
28 }
29
30 - (void)onDisplayLink:(CFTimeInterval)timestamp targetTimestamp:(CFTimeInterval)targetTimestamp {
31 _block(timestamp, targetTimestamp);
32 }
33
34 @end
35
36 TEST(FlutterDisplayLinkTest, ViewAddedToWindowFirst) {
37 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
38 styleMask:NSWindowStyleMaskTitled
39 backing:NSBackingStoreNonretained
40 defer:NO];
41 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
42 [window setContentView:view];
43
44 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
45
46 TestDisplayLinkDelegate* delegate = [[TestDisplayLinkDelegate alloc]
47 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
48 event->Signal();
49 }];
50
51 FlutterDisplayLink* displayLink = [FlutterDisplayLink displayLinkWithView:view];
52 displayLink.delegate = delegate;
53 displayLink.paused = NO;
54
55 event->Wait();
56
57 [displayLink invalidate];
58 }
59
60 TEST(FlutterDisplayLinkTest, ViewAddedToWindowLater) {
61 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
62
63 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
64
65 TestDisplayLinkDelegate* delegate = [[TestDisplayLinkDelegate alloc]
66 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
67 event->Signal();
68 }];
69
70 FlutterDisplayLink* displayLink = [FlutterDisplayLink displayLinkWithView:view];
71 displayLink.delegate = delegate;
72 displayLink.paused = NO;
73
74 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
75 styleMask:NSWindowStyleMaskTitled
76 backing:NSBackingStoreNonretained
77 defer:NO];
78 [window setContentView:view];
79
80 event->Wait();
81
82 [displayLink invalidate];
83 }
84
85 TEST(FlutterDisplayLinkTest, ViewRemovedFromWindow) {
86 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
87 styleMask:NSWindowStyleMaskTitled
88 backing:NSBackingStoreNonretained
89 defer:NO];
90 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
91 [window setContentView:view];
92
93 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
94
95 TestDisplayLinkDelegate* delegate = [[TestDisplayLinkDelegate alloc]
96 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
97 event->Signal();
98 }];
99
100 FlutterDisplayLink* displayLink = [FlutterDisplayLink displayLinkWithView:view];
101 displayLink.delegate = delegate;
102 displayLink.paused = NO;
103
104 event->Wait();
105 displayLink.paused = YES;
106
107 event->Reset();
108
109 displayLink.paused = NO;
110
111 [window setContentView:nil];
112
113 EXPECT_TRUE(event->WaitWithTimeout(fml::TimeDelta::FromMilliseconds(100)));
114 EXPECT_FALSE(event->IsSignaledForTest());
115
116 [displayLink invalidate];
117 }
118
119 TEST(FlutterDisplayLinkTest, WorkaroundForFB13482573) {
120 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
121 styleMask:NSWindowStyleMaskTitled
122 backing:NSBackingStoreNonretained
123 defer:NO];
124 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
125 [window setContentView:view];
126
127 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
128
129 TestDisplayLinkDelegate* delegate = [[TestDisplayLinkDelegate alloc]
130 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
131 event->Signal();
132 }];
133
134 FlutterDisplayLink* displayLink = [FlutterDisplayLink displayLinkWithView:view];
135 displayLink.delegate = delegate;
136 displayLink.paused = NO;
137
138 event->Wait();
139 displayLink.paused = YES;
140
141 event->Reset();
142 [NSThread detachNewThreadWithBlock:^{
143 // Here pthread_self() will be same as pthread_self inside first invocation of
144 // display link callback, causing CVDisplayLinkStart to return error.
145 displayLink.paused = NO;
146 }];
147
148 event->Wait();
149
150 [displayLink invalidate];
151 }
152
153 TEST(FlutterDisplayLinkTest, CVDisplayLinkInterval) {
154 CVDisplayLinkRef link;
155 CVDisplayLinkCreateWithCGDisplay(CGMainDisplayID(), &link);
156 __block CFTimeInterval last = 0;
157 auto intervals = std::make_shared<std::vector<CFTimeInterval>>();
158 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
159 CVDisplayLinkSetOutputHandler(
160 link, ^(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow,
161 const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut) {
162 if (last != 0) {
163 intervals->push_back(CACurrentMediaTime() - last);
164 }
165 last = CACurrentMediaTime();
166 if (intervals->size() == 10) {
167 event->Signal();
168 }
169 return 0;
170 });
171
172 CVDisplayLinkStart(link);
173 event->Wait();
174 CVDisplayLinkStop(link);
175 CVDisplayLinkRelease(link);
176 CFTimeInterval average = std::reduce(intervals->begin(), intervals->end()) / intervals->size();
177 CFTimeInterval max = *std::max_element(intervals->begin(), intervals->end());
178 CFTimeInterval min = *std::min_element(intervals->begin(), intervals->end());
179 NSLog(@"CVDisplayLink Interval: Average: %fs, Max: %fs, Min: %fs", average, max, min);
180 }
FlutterDisplayLinkDelegate-p
Definition: FlutterDisplayLink.h:6
TestDisplayLinkDelegate::_block
void(^ _block)(CFTimeInterval timestamp, CFTimeInterval targetTimestamp)
FlutterDisplayLink
Definition: FlutterDisplayLink.h:15
TEST
TEST(FlutterDisplayLinkTest, ViewAddedToWindowFirst)
Definition: FlutterDisplayLinkTest.mm:36
TestDisplayLinkDelegate
Definition: FlutterDisplayLinkTest.mm:13
FlutterDisplayLink::paused
BOOL paused
Pauses and resumes the display link. May be called from any thread.
Definition: FlutterDisplayLink.h:28
-[FlutterDisplayLink invalidate]
void invalidate()
Invalidates the display link. Must be called on the main thread.
Definition: FlutterDisplayLink.mm:354
FlutterDisplayLink.h
FlutterDisplayLink::delegate
id< FlutterDisplayLinkDelegate > delegate
Definition: FlutterDisplayLink.h:25
+[FlutterDisplayLink displayLinkWithView:]
instancetype displayLinkWithView:(NSView *view)
Definition: FlutterDisplayLink.mm:350