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 
11 
12 #include "flutter/testing/testing.h"
13 #include "gtest/gtest.h"
14 
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 
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 
53  FlutterSurface* surface,
54  CGPoint offset = CGPointZero,
55  size_t index = 0,
56  const std::vector<FlutterRect>& paintRegion = {}) {
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