Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
10
12#include "gtest/gtest.h"
13
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
33 withBlock:(nonnull dispatch_block_t)block
34 delay:(NSTimeInterval)delay {
35 self.presentedFrameSize = frameSize;
36 block();
37}
38
39@end
40
41namespace flutter::testing {
42
43static FlutterSurfaceManager* CreateSurfaceManager(TestView* testView, BOOL enableWideGamut = NO) {
44 id<MTLDevice> device = MTLCreateSystemDefaultDevice();
45 id<MTLCommandQueue> commandQueue = [device newCommandQueue];
46 CALayer* layer = reinterpret_cast<CALayer*>(testView.layer);
47 return [[FlutterSurfaceManager alloc] initWithDevice:device
48 commandQueue:commandQueue
49 layer:layer
50 delegate:testView
51 wideGamut:enableWideGamut];
52}
53
55 FlutterSurface* surface,
56 CGPoint offset = CGPointZero,
57 size_t index = 0,
58 const std::vector<FlutterRect>& paintRegion = {}) {
60 res.surface = surface;
61 res.offset = offset;
62 res.zIndex = index;
63 res.paintRegion = paintRegion;
64 return res;
65}
66
67TEST(FlutterSurfaceManager, MetalTextureSizeMatchesSurfaceSize) {
68 TestView* testView = [[TestView alloc] init];
69 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
70
71 // Get back buffer, lookup should work for borrowed surfaces util present.
72 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
73 auto texture = surface.asFlutterMetalTexture;
74 id<MTLTexture> metalTexture = (__bridge id)texture.texture;
75 EXPECT_EQ(metalTexture.width, 100ul);
76 EXPECT_EQ(metalTexture.height, 50ul);
77 texture.destruction_callback(texture.user_data);
78}
79
80TEST(FlutterSurfaceManager, TestSurfaceLookupFromTexture) {
81 TestView* testView = [[TestView alloc] init];
82 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
83
84 // Get back buffer, lookup should work for borrowed surfaces util present.
85 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
86
87 // SurfaceManager should keep texture alive while borrowed.
88 auto texture = surface.asFlutterMetalTexture;
89 texture.destruction_callback(texture.user_data);
90
91 FlutterMetalTexture dummyTexture{.texture_id = 1, .texture = nullptr, .user_data = nullptr};
92 auto surface1 = [FlutterSurface fromFlutterMetalTexture:&dummyTexture];
93 EXPECT_EQ(surface1, nil);
94
95 auto surface2 = [FlutterSurface fromFlutterMetalTexture:&texture];
96 EXPECT_EQ(surface2, surface);
97}
98
99TEST(FlutterSurfaceManager, BackBufferCacheDoesNotLeak) {
100 TestView* testView = [[TestView alloc] init];
101 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
102 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
103
104 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
105 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
106
107 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
108
109 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(110, 110)];
110 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface2) ] atTime:0 notify:nil];
111
112 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
113
114 auto surface3 = [surfaceManager surfaceForSize:CGSizeMake(120, 120)];
115 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface3) ] atTime:0 notify:nil];
116
117 // Cache should be cleaned during present and only contain the last visible
118 // surface(s).
119 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
120 auto surfaceFromCache = [surfaceManager surfaceForSize:CGSizeMake(110, 110)];
121 EXPECT_EQ(surfaceFromCache, surface2);
122
123 // Submit empty surfaces until the one in cache gets to age >= kSurfaceEvictionAge, in which case
124 // it should be removed.
125
126 for (int i = 0; i < 30 /* kSurfaceEvictionAge */; ++i) {
127 [surfaceManager presentSurfaces:@[] atTime:0 notify:nil];
128 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
129 }
130
131 [surfaceManager presentSurfaces:@[] atTime:0 notify:nil];
132 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
133}
134
135TEST(FlutterSurfaceManager, SurfacesAreRecycled) {
136 TestView* testView = [[TestView alloc] init];
137 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
138
139 EXPECT_EQ(surfaceManager.frontSurfaces.count, 0ul);
140
141 // Get first surface and present it.
142
143 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
144 EXPECT_TRUE(CGSizeEqualToSize(surface1.size, CGSizeMake(100, 100)));
145
146 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
147 EXPECT_EQ(surfaceManager.frontSurfaces.count, 0ul);
148
149 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
150
151 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
152 EXPECT_EQ(surfaceManager.frontSurfaces.count, 1ul);
153 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
154
155 // Get second surface and present it.
156
157 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
158 EXPECT_TRUE(CGSizeEqualToSize(surface2.size, CGSizeMake(100, 100)));
159
160 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
161
162 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface2) ] atTime:0 notify:nil];
163
164 // Check that current front surface returns to cache.
165 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
166 EXPECT_EQ(surfaceManager.frontSurfaces.count, 1ul);
167 EXPECT_EQ(testView.layer.sublayers.count, 1ull);
168
169 // Check that surface is properly reused.
170 auto surface3 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
171 EXPECT_EQ(surface3, surface1);
172}
173
174TEST(FlutterSurfaceManager, BackingStoreCacheSurfaceStuckInUse) {
175 TestView* testView = [[TestView alloc] init];
176 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
177
178 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
179
180 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
181 // Pretend that compositor is holding on to the surface. The surface will be kept
182 // in cache until the age of kSurfaceEvictionAge is reached, and then evicted.
183 surface1.isInUseOverride = YES;
184
185 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
186 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface2) ] atTime:0 notify:nil];
187 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
188
189 for (int i = 0; i < 30 /* kSurfaceEvictionAge */ - 1; ++i) {
190 auto surface3 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
191 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface3) ] atTime:0 notify:nil];
192 EXPECT_EQ(surfaceManager.backBufferCache.count, 2ul);
193 }
194
195 auto surface4 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
196 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface4) ] atTime:0 notify:nil];
197 // Surface in use should bet old enough at this point to be evicted.
198 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
199}
200
201inline bool operator==(const CGRect& lhs, const CGRect& rhs) {
202 return CGRectEqualToRect(lhs, rhs);
203}
204
205TEST(FlutterSurfaceManager, LayerManagement) {
206 TestView* testView = [[TestView alloc] init];
207 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
208
209 EXPECT_EQ(testView.layer.sublayers.count, 0ul);
210
211 auto surface1_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
212 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1_1, CGPointMake(20, 10)) ]
213 atTime:0
214 notify:nil];
215
216 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
217 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 40)));
218
219 auto surface2_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
220 auto surface2_2 = [surfaceManager surfaceForSize:CGSizeMake(20, 20)];
221 [surfaceManager presentSurfaces:@[
222 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
223 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
224 {
225 FlutterRect{0, 0, 20, 20},
226 FlutterRect{40, 0, 60, 20},
227 })
228 ]
229 atTime:0
230 notify:nil];
231
232 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
233 EXPECT_EQ(testView.layer.sublayers[0].zPosition, 1.0);
234 EXPECT_EQ(testView.layer.sublayers[1].zPosition, 2.0);
235 CALayer* firstOverlaySublayer;
236 {
237 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
238 EXPECT_EQ(sublayers.count, 2ul);
239 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
240 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
241 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].contentsRect, CGRectMake(0, 0, 1, 1)));
242 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].contentsRect, CGRectMake(2, 0, 1, 1)));
243 EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
244 firstOverlaySublayer = sublayers[0];
245 }
246 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 70)));
247
248 // Check second overlay sublayer is removed while first is reused and updated
249 [surfaceManager presentSurfaces:@[
250 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
251 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
252 {
253 FlutterRect{0, 10, 20, 20},
254 })
255 ]
256 atTime:0
257 notify:nil];
258 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
259 {
260 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
261 EXPECT_EQ(sublayers.count, 1ul);
262 EXPECT_EQ(sublayers[0], firstOverlaySublayer);
263 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 5, 10, 5)));
264 }
265
266 // Check that second overlay sublayer is added back while first is reused and updated
267 [surfaceManager presentSurfaces:@[
268 CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
269 CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
270 {
271 FlutterRect{0, 0, 20, 20},
272 FlutterRect{40, 0, 60, 20},
273 })
274 ]
275 atTime:0
276 notify:nil];
277
278 EXPECT_EQ(testView.layer.sublayers.count, 2ul);
279 {
280 NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
281 EXPECT_EQ(sublayers.count, 2ul);
282 EXPECT_EQ(sublayers[0], firstOverlaySublayer);
283 EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
284 EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
285 EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
286 }
287
288 auto surface3_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
289 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface3_1, CGPointMake(20, 10)) ]
290 atTime:0
291 notify:nil];
292
293 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
294 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 40)));
295
296 // Check removal of all surfaces.
297 [surfaceManager presentSurfaces:@[] atTime:0 notify:nil];
298 EXPECT_EQ(testView.layer.sublayers.count, 0ul);
299 EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(0, 0)));
300}
301
302TEST(FlutterSurfaceManager, WideGamutSurfaceHasCorrectPixelFormat) {
303 TestView* testView = [[TestView alloc] init];
304 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
305
306 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
307 auto texture = surface.asFlutterMetalTexture;
308 id<MTLTexture> metalTexture = (__bridge id)texture.texture;
309 EXPECT_EQ(metalTexture.pixelFormat, MTLPixelFormatBGRA10_XR);
310 texture.destruction_callback(texture.user_data);
311}
312
313TEST(FlutterSurfaceManager, StandardGamutSurfaceHasCorrectPixelFormat) {
314 TestView* testView = [[TestView alloc] init];
315 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/NO);
316
317 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
318 auto texture = surface.asFlutterMetalTexture;
319 id<MTLTexture> metalTexture = (__bridge id)texture.texture;
320 EXPECT_EQ(metalTexture.pixelFormat, MTLPixelFormatBGRA8Unorm);
321 texture.destruction_callback(texture.user_data);
322}
323
324TEST(FlutterSurfaceManager, WideGamutLayerWorksWithoutExplicitContentsFormat) {
325 TestView* testView = [[TestView alloc] init];
326 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
327
328 auto surface = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
329 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface, CGPointMake(0, 0)) ]
330 atTime:0
331 notify:nil];
332
333 EXPECT_EQ(testView.layer.sublayers.count, 1ul);
334 EXPECT_NE(testView.layer.sublayers[0].contents, nil);
335}
336
337TEST(FlutterSurfaceManager, WideGamutIOSurfaceHasCorrectColorSpace) {
338 TestView* testView = [[TestView alloc] init];
339 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
340
341 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
342 IOSurfaceRef ioSurface = surface.ioSurface;
343 CFTypeRef colorSpace = IOSurfaceCopyValue(ioSurface, kIOSurfaceColorSpace);
344 if (colorSpace == nullptr) {
345 GTEST_FAIL() << "IOSurfaceCopyValue returned nullptr for kIOSurfaceColorSpace";
346 return;
347 }
348 EXPECT_TRUE(CFEqual(colorSpace, kCGColorSpaceExtendedSRGB));
349 CFRelease(colorSpace);
350}
351
352TEST(FlutterSurfaceManager, StandardGamutIOSurfaceHasCorrectColorSpace) {
353 TestView* testView = [[TestView alloc] init];
354 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/NO);
355
356 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
357 IOSurfaceRef ioSurface = surface.ioSurface;
358 CFTypeRef colorSpace = IOSurfaceCopyValue(ioSurface, kIOSurfaceColorSpace);
359 if (colorSpace == nullptr) {
360 GTEST_FAIL() << "IOSurfaceCopyValue returned nullptr for kIOSurfaceColorSpace";
361 return;
362 }
363 EXPECT_TRUE(CFEqual(colorSpace, kCGColorSpaceSRGB));
364 CFRelease(colorSpace);
365}
366
367TEST(FlutterSurfaceManager, WideGamutIOSurfaceHasCorrectPixelFormat) {
368 TestView* testView = [[TestView alloc] init];
369 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
370
371 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
372 IOSurfaceRef ioSurface = surface.ioSurface;
373 uint32_t pixelFormat = (uint32_t)IOSurfaceGetPixelFormat(ioSurface);
374 EXPECT_EQ(pixelFormat, (uint32_t)kCVPixelFormatType_40ARGBLEWideGamut);
375}
376
377TEST(FlutterSurfaceManager, StandardGamutIOSurfaceHasCorrectPixelFormat) {
378 TestView* testView = [[TestView alloc] init];
379 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/NO);
380
381 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
382 IOSurfaceRef ioSurface = surface.ioSurface;
383 uint32_t pixelFormat = (uint32_t)IOSurfaceGetPixelFormat(ioSurface);
384 EXPECT_EQ(pixelFormat, (uint32_t)kCVPixelFormatType_32BGRA);
385}
386
387TEST(FlutterSurfaceManager, WideGamutIOSurfaceHasCorrectBytesPerElement) {
388 TestView* testView = [[TestView alloc] init];
389 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
390
391 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
392 IOSurfaceRef ioSurface = surface.ioSurface;
393 size_t bytesPerElement = IOSurfaceGetBytesPerElement(ioSurface);
394 EXPECT_EQ(bytesPerElement, 8ul);
395}
396
397TEST(FlutterSurfaceManager, StandardGamutIOSurfaceHasCorrectBytesPerElement) {
398 TestView* testView = [[TestView alloc] init];
399 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/NO);
400
401 auto surface = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
402 IOSurfaceRef ioSurface = surface.ioSurface;
403 size_t bytesPerElement = IOSurfaceGetBytesPerElement(ioSurface);
404 EXPECT_EQ(bytesPerElement, 4ul);
405}
406
407TEST(FlutterSurfaceManager, SurfaceCacheDoesNotMixGamutModes) {
408 TestView* testView = [[TestView alloc] init];
409 // Create a wide gamut surface manager.
410 FlutterSurfaceManager* wideManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
411
412 auto wideSurface = [wideManager surfaceForSize:CGSizeMake(100, 100)];
413 auto wideTexture = wideSurface.asFlutterMetalTexture;
414 id<MTLTexture> wideMetalTexture = (__bridge id)wideTexture.texture;
415 EXPECT_EQ(wideMetalTexture.pixelFormat, MTLPixelFormatBGRA10_XR);
416
417 // Present and get it cached.
418 [wideManager presentSurfaces:@[ CreatePresentInfo(wideSurface) ] atTime:0 notify:nil];
419 wideTexture.destruction_callback(wideTexture.user_data);
420
421 // Request same size again - should get a recycled wide gamut surface.
422 auto recycledSurface = [wideManager surfaceForSize:CGSizeMake(100, 100)];
423 auto recycledTexture = recycledSurface.asFlutterMetalTexture;
424 id<MTLTexture> recycledMetalTexture = (__bridge id)recycledTexture.texture;
425 EXPECT_EQ(recycledMetalTexture.pixelFormat, MTLPixelFormatBGRA10_XR);
426 recycledTexture.destruction_callback(recycledTexture.user_data);
427}
428
429TEST(FlutterSurfaceManager, DynamicSwitchFromStandardToWideGamut) {
430 TestView* testView = [[TestView alloc] init];
431 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/NO);
432
433 // Start with standard gamut surface.
434 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
435 auto texture1 = surface1.asFlutterMetalTexture;
436 id<MTLTexture> metalTexture1 = (__bridge id)texture1.texture;
437 EXPECT_EQ(metalTexture1.pixelFormat, MTLPixelFormatBGRA8Unorm);
438 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
439 texture1.destruction_callback(texture1.user_data);
440
441 // Switch to wide gamut.
442 [surfaceManager setEnableWideGamut:YES];
443
444 // Verify cache was flushed.
445 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
446
447 // New surface should be wide gamut.
448 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
449 auto texture2 = surface2.asFlutterMetalTexture;
450 id<MTLTexture> metalTexture2 = (__bridge id)texture2.texture;
451 EXPECT_EQ(metalTexture2.pixelFormat, MTLPixelFormatBGRA10_XR);
452 texture2.destruction_callback(texture2.user_data);
453}
454
455TEST(FlutterSurfaceManager, DynamicSwitchFromWideToStandardGamut) {
456 TestView* testView = [[TestView alloc] init];
457 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
458
459 // Start with wide gamut surface.
460 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
461 auto texture1 = surface1.asFlutterMetalTexture;
462 id<MTLTexture> metalTexture1 = (__bridge id)texture1.texture;
463 EXPECT_EQ(metalTexture1.pixelFormat, MTLPixelFormatBGRA10_XR);
464 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
465 texture1.destruction_callback(texture1.user_data);
466
467 // Switch to standard gamut.
468 [surfaceManager setEnableWideGamut:NO];
469
470 // Verify cache was flushed.
471 EXPECT_EQ(surfaceManager.backBufferCache.count, 0ul);
472
473 // New surface should be standard gamut.
474 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 50)];
475 auto texture2 = surface2.asFlutterMetalTexture;
476 id<MTLTexture> metalTexture2 = (__bridge id)texture2.texture;
477 EXPECT_EQ(metalTexture2.pixelFormat, MTLPixelFormatBGRA8Unorm);
478 texture2.destruction_callback(texture2.user_data);
479}
480
481TEST(FlutterSurfaceManager, DynamicSwitchNoOpWhenSameValue) {
482 TestView* testView = [[TestView alloc] init];
483 FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView, /*enableWideGamut=*/YES);
484
485 // Cache a surface.
486 auto surface1 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
487 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface1) ] atTime:0 notify:nil];
488
489 auto surface2 = [surfaceManager surfaceForSize:CGSizeMake(100, 100)];
490 [surfaceManager presentSurfaces:@[ CreatePresentInfo(surface2) ] atTime:0 notify:nil];
491 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
492
493 // Set same value — cache should not be flushed.
494 [surfaceManager setEnableWideGamut:YES];
495 EXPECT_EQ(surfaceManager.backBufferCache.count, 1ul);
496}
497
498} // namespace flutter::testing
TEST(AsciiTableTest, Simple)
bool operator==(const FlutterPoint &a, const FlutterPoint &b)
VkDevice device
Definition main.cc:69
VkSurfaceKHR surface
Definition main.cc:65
nullable FlutterSurface * fromFlutterMetalTexture:(nonnull const FlutterMetalTexture *texture)
void setEnableWideGamut:(BOOL enableWideGamut)
nonnull FlutterSurface * surfaceForSize:(CGSize size)
FlutterBackBufferCache * backBufferCache
void presentSurfaces:atTime:notify:(nonnull NSArray< FlutterSurfacePresentInfo * > *surfaces,[atTime] CFTimeInterval presentationTime,[notify] nullable dispatch_block_t notify)
NSArray< FlutterSurface * > * frontSurfaces
std::vector< FlutterRect > paintRegion
nonnull instancetype init()
FlTexture * texture
static FlutterSurfacePresentInfo * CreatePresentInfo(FlutterSurface *surface, CGPoint offset=CGPointZero, size_t index=0, const std::vector< FlutterRect > &paintRegion={})
static FlutterSurfaceManager * CreateSurfaceManager(TestView *testView, BOOL enableWideGamut=NO)
A structure to represent a rectangle.
Definition embedder.h:648
const uintptr_t id
int BOOL