| Flutter macOS Embedder |

FlutterSurfaceManagerTest.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 <Cocoa/Cocoa.h>
6 #import <Metal/Metal.h>
7
8 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurface.h"
9 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h"
10
11 #include "flutter/testing/testing.h"
12 #include "gtest/gtest.h"
13
14 @interface TestView : NSView <FlutterSurfaceManagerDelegate>
15
16 @property(readwrite, nonatomic) CGSize presentedFrameSize;
17 - (nonnull instancetype)init;
18
19 @end
20
21 @implementation TestView
22
23 - (instancetype)init {
24 self = [super initWithFrame:NSZeroRect];
25 if (self) {
26 [self setWantsLayer:YES];
27 self.layer.contentsScale = 2.0;
28 }
29 return self;
30 }
31
32 - (void)onPresent:(CGSize)frameSize withBlock:(nonnull dispatch_block_t)block {
33 self.presentedFrameSize = frameSize;
34 block();
35 }
36
37 @end
38
39 namespace flutter::testing {
40
41 static FlutterSurfaceManager* CreateSurfaceManager(TestView* testView) {
42 id<MTLDevice> device = MTLCreateSystemDefaultDevice();
43 id<MTLCommandQueue> commandQueue = [device newCommandQueue];
44 CALayer* layer = reinterpret_cast<CALayer*>(testView.layer);
45 return [[FlutterSurfaceManager alloc] initWithDevice:device
46 commandQueue:commandQueue
47 layer:layer
48 delegate:testView];
49 }
50
51 static FlutterSurfacePresentInfo* CreatePresentInfo(
52 FlutterSurface* surface,
53 CGPoint offset = CGPointZero,
54 size_t index = 0,
55 const std::vector<FlutterRect>& paintRegion = {}) {
56 FlutterSurfacePresentInfo* res = [[FlutterSurfacePresentInfo alloc] init];
57 res.surface = surface;
58 res.offset = offset;
59 res.zIndex = index;
60 res.paintRegion = paintRegion;
61 return res;
62 }
63
64 TEST(FlutterSurfaceManager, MetalTextureSizeMatchesSurfaceSize) {
65 TestView* testView = [[TestView alloc] init];
66 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
67
68 // Get back buffer, lookup should work for borrowed surfaces util present.
69 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
70 auto texture = surface.asFlutterMetalTexture;
71 id<MTLTexture> metalTexture = (__bridge id)texture.texture;
72 EXPECT_EQ(metalTexture.width, 100ul);
73 EXPECT_EQ(metalTexture.height, 50ul);
74 texture.destruction_callback(texture.user_data);
75 }
76
77 TEST(FlutterSurfaceManager, TestSurfaceLookupFromTexture) {
78 TestView* testView = [[TestView alloc] init];
79 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
80
81 // Get back buffer, lookup should work for borrowed surfaces util present.
82 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
83
84 // SurfaceManager should keep texture alive while borrowed.
85 auto texture = surface.asFlutterMetalTexture;
86 texture.destruction_callback(texture.user_data);
87
88 FlutterMetalTexture dummyTexture{.texture_id = 1, .texture = nullptr, .user_data = nullptr};
89 auto surface1 = [FlutterSurface fromFlutterMetalTexture:&dummyTexture];
90 EXPECT_EQ(surface1, nil);
91
92 auto surface2 = [FlutterSurface fromFlutterMetalTexture:&texture];
93 EXPECT_EQ(surface2, surface);
94 }
95
96 TEST(FlutterSurfaceManager, BackBufferCacheDoesNotLeak) {
97 TestView* testView = [[TestView alloc] init];
98 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
99 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
100
101 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
102 [surfaceManager present:@[ CreatePresentInfo(surface1) ] notify:nil];
103
104 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
105
106 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(110, 110)];
107 [surfaceManager present:@[ CreatePresentInfo(surface2) ] notify:nil];
108
109 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
110
111 auto surface3 = [surfaceManager surfaceForSize:CGSizeMake(120, 120)];
112 [surfaceManager present:@[ CreatePresentInfo(surface3) ] notify:nil];
113
114 // Cache should be cleaned during present and only contain the last visible
115 // surface(s).
116 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
117 auto surfaceFromCache = [surfaceManager surfaceForSize:CGSizeMake(110, 110)];
118 EXPECT_EQ(surfaceFromCache, surface2);
119
120 [surfaceManager present:@[] notify:nil];
121 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
122
123 [surfaceManager present:@[] notify:nil];
124 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
125 }
126
127 TEST(FlutterSurfaceManager, SurfacesAreRecycled) {
128 TestView* testView = [[TestView alloc] init];
129 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
130
131 EXPECT_EQ(surfaceManager.frontSurfaces.count, 0ul);
132
133 // Get first surface and present it.
134
135 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
136 EXPECT_TRUE(CGSizeEqualToSize(surface1.size, CGSizeMake(100, 100)));
137
138 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
139 EXPECT_EQ(surfaceManager.frontSurfaces.count, 0ul);
140
141 [surfaceManager present:@[ CreatePresentInfo(surface1) ] notify:nil];
142
143 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
144 EXPECT_EQ(surfaceManager.frontSurfaces.count, 1ul);
145 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
146
147 // Get second surface and present it.
148
149 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
150 EXPECT_TRUE(CGSizeEqualToSize(surface2.size, CGSizeMake(100, 100)));
151
152 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
153
154 [surfaceManager present:@[ CreatePresentInfo(surface2) ] notify:nil];
155
156 // Check that current front surface returns to cache.
157 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
158 EXPECT_EQ(surfaceManager.frontSurfaces.count, 1ul);
159 EXPECT_EQ(testView.layer.sublayers.count, 1ull);
160
161 // Check that surface is properly reused.
162 auto surface3 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
163 EXPECT_EQ(surface3, surface1);
164 }
165
166 inline bool operator==(const CGRect& lhs, const CGRect& rhs) {
167 return CGRectEqualToRect(lhs, rhs);
168 }
169
170 TEST(FlutterSurfaceManager, LayerManagement) {
171 TestView* testView = [[TestView alloc] init];
172 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
173
174 EXPECT_EQ(testView.layer.sublayers.count, 0ul);
175
176 auto surface1_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
177 [surfaceManager present:@[ CreatePresentInfo(surface1_1, CGPointMake(20, 10)) ] notify:nil];
178
179 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
180 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 40)));
181
182 auto surface2_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
183 auto surface2_2 = [surfaceManager surfaceForSize:CGSizeMake(20, 20)];
184 [surfaceManager present:@[
185 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
186 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
187 {
188 FlutterRect{0, 0, 20, 20},
189 FlutterRect{40, 0, 60, 20},
190 })
191 ]
192 notify:nil];
193
194 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
195 EXPECT_EQ(testView.layer.sublayers[0].zPosition, 1.0);
196 EXPECT_EQ(testView.layer.sublayers[1].zPosition, 2.0);
197 CALayer* firstOverlaySublayer;
198 {
199 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
200 EXPECT_EQ(sublayers.count, 2ul);
201 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
202 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
203 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].contentsRect, CGRectMake(0, 0, 1, 1)));
204 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].contentsRect, CGRectMake(2, 0, 1, 1)));
205 EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
206 firstOverlaySublayer = sublayers[0];
207 }
208 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 70)));
209
210 // Check second overlay sublayer is removed while first is reused and updated
211 [surfaceManager present:@[
212 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
213 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
214 {
215 FlutterRect{0, 10, 20, 20},
216 })
217 ]
218 notify:nil];
219 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
220 {
221 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
222 EXPECT_EQ(sublayers.count, 1ul);
223 EXPECT_EQ(sublayers[0], firstOverlaySublayer);
224 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 5, 10, 5)));
225 }
226
227 // Check that second overlay sublayer is added back while first is reused and updated
228 [surfaceManager present:@[
229 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
230 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
231 {
232 FlutterRect{0, 0, 20, 20},
233 FlutterRect{40, 0, 60, 20},
234 })
235 ]
236 notify:nil];
237
238 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
239 {
240 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
241 EXPECT_EQ(sublayers.count, 2ul);
242 EXPECT_EQ(sublayers[0], firstOverlaySublayer);
243 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
244 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
245 EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
246 }
247
248 auto surface3_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
249 [surfaceManager present:@[ CreatePresentInfo(surface3_1, CGPointMake(20, 10)) ] notify:nil];
250
251 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
252 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 40)));
253
254 // Check removal of all surfaces.
255 [surfaceManager present:@[] notify:nil];
256 EXPECT_EQ(testView.layer.sublayers.count, 0ul);
257 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(0, 0)));
258 }
259
260 } // namespace flutter::testing
TestView
Definition: FlutterSurfaceManagerTest.mm:14
+[FlutterSurface fromFlutterMetalTexture:]
nullable FlutterSurface * fromFlutterMetalTexture:(nonnull const FlutterMetalTexture *texture)
-[FlutterSurface asFlutterMetalTexture]
FlutterMetalTexture asFlutterMetalTexture()
Definition: FlutterSurface.mm:45
flutter::testing::CreatePresentInfo
static FlutterSurfacePresentInfo * CreatePresentInfo(FlutterSurface *surface, CGPoint offset=CGPointZero, size_t index=0, const std::vector< FlutterRect > &paintRegion={})
Definition: FlutterSurfaceManagerTest.mm:51
FlutterSurfaceManager.h
-[FlutterBackBufferCache count]
NSUInteger count()
Definition: FlutterSurfaceManager.mm:277
FlutterSurfaceManager
Definition: FlutterSurfaceManager.h:41
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
FlutterSurface.h
FlutterSurface
Definition: FlutterSurface.h:13
FlutterSurfaceManagerDelegate-p
Definition: FlutterSurfaceManager.h:24
FlutterSurfacePresentInfo::surface
FlutterSurface * surface
Definition: FlutterSurfaceManager.h:17
FlutterSurfaceManager::frontSurfaces
NSArray< FlutterSurface * > * frontSurfaces
Definition: FlutterSurfaceManager.h:102
FlutterSurfacePresentInfo::offset
CGPoint offset
Definition: FlutterSurfaceManager.h:18
FlutterSurfaceManager::backBufferCache
FlutterBackBufferCache * backBufferCache
Definition: FlutterSurfaceManager.h:101
flutter::testing::operator==
bool operator==(const CGRect &lhs, const CGRect &rhs)
Definition: FlutterSurfaceManagerTest.mm:166
FlutterSurfacePresentInfo
Definition: FlutterSurfaceManager.h:15
FlutterSurfacePresentInfo::zIndex
size_t zIndex
Definition: FlutterSurfaceManager.h:19
-[FlutterSurfaceManager surfaceForSize:]
nonnull FlutterSurface * surfaceForSize:(CGSize size)
Definition: FlutterSurfaceManager.mm:131
TestView::presentedFrameSize
CGSize presentedFrameSize
Definition: FlutterSurfaceManagerTest.mm:16
flutter::testing::CreateSurfaceManager
static FlutterSurfaceManager * CreateSurfaceManager(TestView *testView)
Definition: FlutterSurfaceManagerTest.mm:41
flutter::testing::TEST
TEST(FlutterSurfaceManager, LayerManagement)
Definition: FlutterSurfaceManagerTest.mm:170
-[FlutterSurfaceManager present:notify:]
void present:notify:(nonnull NSArray< FlutterSurfacePresentInfo * > *surfaces,[notify] nullable dispatch_block_t notify)
FlutterSurfacePresentInfo::paintRegion
std::vector< FlutterRect > paintRegion
Definition: FlutterSurfaceManager.h:20
-[TestView init]
nonnull instancetype init()
Definition: FlutterSurfaceManagerTest.mm:23