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