7 #import <AppKit/AppKit.h>
10 #include "flutter/fml/synchronization/waitable_event.h"
11 #include "flutter/testing/testing.h"
14 void (^
_block)(CFTimeInterval timestamp, CFTimeInterval targetTimestamp);
17 - (instancetype)initWithBlock:(
void (^)(CFTimeInterval timestamp,
18 CFTimeInterval targetTimestamp))block;
23 - (instancetype)initWithBlock:(
void (^__strong)(CFTimeInterval, CFTimeInterval))block {
24 if (
self = [super init]) {
30 - (void)onDisplayLink:(CFTimeInterval)timestamp targetTimestamp:(CFTimeInterval)targetTimestamp {
31 _block(timestamp, targetTimestamp);
36 TEST(FlutterDisplayLinkTest, ViewAddedToWindowFirst) {
37 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
38 styleMask:NSWindowStyleMaskTitled
39 backing:NSBackingStoreNonretained
41 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
42 [window setContentView:view];
44 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
47 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
60 TEST(FlutterDisplayLinkTest, ViewAddedToWindowLater) {
61 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
63 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
66 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
74 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
75 styleMask:NSWindowStyleMaskTitled
76 backing:NSBackingStoreNonretained
78 [window setContentView:view];
85 TEST(FlutterDisplayLinkTest, ViewRemovedFromWindow) {
86 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
87 styleMask:NSWindowStyleMaskTitled
88 backing:NSBackingStoreNonretained
90 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
91 [window setContentView:view];
93 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
96 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
111 [window setContentView:nil];
113 EXPECT_TRUE(event->WaitWithTimeout(fml::TimeDelta::FromMilliseconds(100)));
114 EXPECT_FALSE(event->IsSignaledForTest());
119 TEST(FlutterDisplayLinkTest, WorkaroundForFB13482573) {
120 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
121 styleMask:NSWindowStyleMaskTitled
122 backing:NSBackingStoreNonretained
124 NSView* view = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)];
125 [window setContentView:view];
127 auto event = std::make_shared<fml::AutoResetWaitableEvent>();
130 initWithBlock:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp) {
142 [NSThread detachNewThreadWithBlock:^{
145 displayLink.paused = NO;
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) {
163 intervals->push_back(CACurrentMediaTime() - last);
165 last = CACurrentMediaTime();
166 if (intervals->size() == 10) {
172 CVDisplayLinkStart(link);
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);