| Flutter macOS Embedder |

FlutterSurfaceManager.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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h"
6
7 #import <Metal/Metal.h>
8
9 #include <algorithm>
10
11 #include "flutter/fml/logging.h"
12 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurface.h"
13
14 @implementation FlutterSurfacePresentInfo
15 @end
16
17 @interface FlutterSurfaceManager () {
18 id<MTLDevice> _device;
19 id<MTLCommandQueue> _commandQueue;
20 CALayer* _containingLayer;
21 __weak id<FlutterSurfaceManagerDelegate> _delegate;
22
23 // Available (cached) back buffer surfaces. These will be cleared during
24 // present and replaced by current frong surfaces.
25 FlutterBackBufferCache* _backBufferCache;
26
27 // Surfaces currently used to back visible layers.
28 NSMutableArray<FlutterSurface*>* _frontSurfaces;
29
30 // Currently visible layers.
31 NSMutableArray<CALayer*>* _layers;
32
33 // Whether to highlight borders of overlay surfaces. Determined by
34 // FLTEnableSurfaceDebugInfo value in main bundle Info.plist.
35 NSNumber* _enableSurfaceDebugInfo;
36 CATextLayer* _infoLayer;
37 }
38
39 /**
40 * Updates underlying CALayers with the contents of the surfaces to present.
41 */
42 - (void)commit:(NSArray<FlutterSurfacePresentInfo*>*)surfaces;
43
44 @end
45
46 static NSColor* GetBorderColorForLayer(int layer) {
47 NSArray* colors = @[
48 [NSColor yellowColor],
49 [NSColor cyanColor],
50 [NSColor magentaColor],
51 [NSColor greenColor],
52 [NSColor purpleColor],
53 [NSColor orangeColor],
54 [NSColor blueColor],
55 ];
56 return colors[layer % colors.count];
57 }
58
59 /// Creates sublayers for given layer, each one displaying a portion of the
60 /// of the surface determined by a rectangle in the provided paint region.
61 static void UpdateContentSubLayers(CALayer* layer,
62 IOSurfaceRef surface,
63 CGFloat scale,
64 CGSize surfaceSize,
65 NSColor* borderColor,
66 const std::vector<FlutterRect>& paintRegion) {
67 // Adjust sublayer count to paintRegion count.
68 while (layer.sublayers.count > paintRegion.size()) {
69 [layer.sublayers.lastObject removeFromSuperlayer];
70 }
71
72 while (layer.sublayers.count < paintRegion.size()) {
73 CALayer* newLayer = [CALayer layer];
74 [layer addSublayer:newLayer];
75 }
76
77 for (size_t i = 0; i < paintRegion.size(); i++) {
78 CALayer* subLayer = [layer.sublayers objectAtIndex:i];
79 const auto& rect = paintRegion[i];
80 subLayer.frame = CGRectMake(rect.left / scale, rect.top / scale,
81 (rect.right - rect.left) / scale, (rect.bottom - rect.top) / scale);
82
83 double width = surfaceSize.width;
84 double height = surfaceSize.height;
85
86 subLayer.contentsRect =
87 CGRectMake(rect.left / width, rect.top / height, (rect.right - rect.left) / width,
88 (rect.bottom - rect.top) / height);
89
90 if (borderColor != nil) {
91 // Visualize sublayer
92 subLayer.borderColor = borderColor.CGColor;
93 subLayer.borderWidth = 1.0;
94 }
95
96 subLayer.contents = (__bridge id)surface;
97 }
98 }
99
100 @implementation FlutterSurfaceManager
101
102 - (instancetype)initWithDevice:(id<MTLDevice>)device
103 commandQueue:(id<MTLCommandQueue>)commandQueue
104 layer:(CALayer*)containingLayer
105 delegate:(__weak id<FlutterSurfaceManagerDelegate>)delegate {
106 if (self = [super init]) {
107 _device = device;
108 _commandQueue = commandQueue;
109 _containingLayer = containingLayer;
110 _delegate = delegate;
111
112 _backBufferCache = [[FlutterBackBufferCache alloc] init];
113 _frontSurfaces = [NSMutableArray array];
114 _layers = [NSMutableArray array];
115 }
116 return self;
117 }
118
119 - (FlutterBackBufferCache*)backBufferCache {
120 return _backBufferCache;
121 }
122
123 - (NSArray*)frontSurfaces {
124 return _frontSurfaces;
125 }
126
127 - (NSArray*)layers {
128 return _layers;
129 }
130
131 - (FlutterSurface*)surfaceForSize:(CGSize)size {
132 FlutterSurface* surface = [_backBufferCache removeSurfaceForSize:size];
133 if (surface == nil) {
134 surface = [[FlutterSurface alloc] initWithSize:size device:_device];
135 }
136 return surface;
137 }
138
139 - (BOOL)enableSurfaceDebugInfo {
140 if (_enableSurfaceDebugInfo == nil) {
141 _enableSurfaceDebugInfo =
142 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableSurfaceDebugInfo"];
143 if (_enableSurfaceDebugInfo == nil) {
144 _enableSurfaceDebugInfo = @NO;
145 }
146 }
147 return [_enableSurfaceDebugInfo boolValue];
148 }
149
150 - (void)commit:(NSArray<FlutterSurfacePresentInfo*>*)surfaces {
151 FML_DCHECK([NSThread isMainThread]);
152
153 // Release all unused back buffer surfaces and replace them with front surfaces.
154 [_backBufferCache replaceSurfaces:_frontSurfaces];
155
156 // Front surfaces will be replaced by currently presented surfaces.
157 [_frontSurfaces removeAllObjects];
158 for (FlutterSurfacePresentInfo* info in surfaces) {
159 [_frontSurfaces addObject:info.surface];
160 }
161
162 // Add or remove layers to match the count of surfaces to present.
163 while (_layers.count > _frontSurfaces.count) {
164 [_layers.lastObject removeFromSuperlayer];
165 [_layers removeLastObject];
166 }
167 while (_layers.count < _frontSurfaces.count) {
168 CALayer* layer = [CALayer layer];
169 [_containingLayer addSublayer:layer];
170 [_layers addObject:layer];
171 }
172
173 bool enableSurfaceDebugInfo = self.enableSurfaceDebugInfo;
174
175 // Update contents of surfaces.
176 for (size_t i = 0; i < surfaces.count; ++i) {
177 FlutterSurfacePresentInfo* info = surfaces[i];
178 CALayer* layer = _layers[i];
179 CGFloat scale = _containingLayer.contentsScale;
180 if (i == 0) {
181 layer.frame = CGRectMake(info.offset.x / scale, info.offset.y / scale,
182 info.surface.size.width / scale, info.surface.size.height / scale);
183 layer.contents = (__bridge id)info.surface.ioSurface;
184 } else {
185 layer.frame = CGRectZero;
186 NSColor* borderColor = enableSurfaceDebugInfo ? GetBorderColorForLayer(i - 1) : nil;
187 UpdateContentSubLayers(layer, info.surface.ioSurface, scale, info.surface.size, borderColor,
188 info.paintRegion);
189 }
190 layer.zPosition = info.zIndex;
191 }
192
193 if (enableSurfaceDebugInfo) {
194 if (_infoLayer == nil) {
195 _infoLayer = [[CATextLayer alloc] init];
196 [_containingLayer addSublayer:_infoLayer];
197 _infoLayer.fontSize = 15;
198 _infoLayer.foregroundColor = [NSColor yellowColor].CGColor;
199 _infoLayer.frame = CGRectMake(15, 15, 300, 100);
200 _infoLayer.contentsScale = _containingLayer.contentsScale;
201 _infoLayer.zPosition = 100000;
202 }
203 _infoLayer.string = [NSString stringWithFormat:@"Surface count: %li", _layers.count];
204 }
205 }
206
207 static CGSize GetRequiredFrameSize(NSArray<FlutterSurfacePresentInfo*>* surfaces) {
208 CGSize size = CGSizeZero;
209 for (FlutterSurfacePresentInfo* info in surfaces) {
210 size = CGSizeMake(std::max(size.width, info.offset.x + info.surface.size.width),
211 std::max(size.height, info.offset.y + info.surface.size.height));
212 }
213 return size;
214 }
215
216 - (void)present:(NSArray<FlutterSurfacePresentInfo*>*)surfaces notify:(dispatch_block_t)notify {
217 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
218 [commandBuffer commit];
219 [commandBuffer waitUntilScheduled];
220
221 // Get the actual dimensions of the frame (relevant for thread synchronizer).
222 CGSize size = GetRequiredFrameSize(surfaces);
223
224 [_delegate onPresent:size
225 withBlock:^{
226 [self commit:surfaces];
227 if (notify != nil) {
228 notify();
229 }
230 }];
231 }
232
233 @end
234
235 // Cached back buffers will be released after kIdleDelay if there is no activity.
236 static const double kIdleDelay = 1.0;
237
238 @interface FlutterBackBufferCache () {
239 NSMutableArray<FlutterSurface*>* _surfaces;
240 }
241
242 @end
243
244 @implementation FlutterBackBufferCache
245
246 - (instancetype)init {
247 if (self = [super init]) {
248 self->_surfaces = [[NSMutableArray alloc] init];
249 }
250 return self;
251 }
252
253 - (nullable FlutterSurface*)removeSurfaceForSize:(CGSize)size {
254 @synchronized(self) {
255 for (FlutterSurface* surface in _surfaces) {
256 if (CGSizeEqualToSize(surface.size, size)) {
257 // By default ARC doesn't retain enumeration iteration variables.
258 FlutterSurface* res = surface;
259 [_surfaces removeObject:surface];
260 return res;
261 }
262 }
263 return nil;
264 }
265 }
266
267 - (void)replaceSurfaces:(nonnull NSArray<FlutterSurface*>*)surfaces {
268 @synchronized(self) {
269 [_surfaces removeAllObjects];
270 [_surfaces addObjectsFromArray:surfaces];
271 }
272
273 // performSelector:withObject:afterDelay needs to be performed on RunLoop thread
274 [self performSelectorOnMainThread:@selector(reschedule) withObject:nil waitUntilDone:NO];
275 }
276
277 - (NSUInteger)count {
278 @synchronized(self) {
279 return _surfaces.count;
280 }
281 }
282
283 - (void)onIdle {
284 @synchronized(self) {
285 [_surfaces removeAllObjects];
286 }
287 }
288
289 - (void)reschedule {
290 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];
291 [self performSelector:@selector(onIdle) withObject:nil afterDelay:kIdleDelay];
292 }
293
294 - (void)dealloc {
295 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];
296 }
297
298 @end
FlutterBackBufferCache()::_surfaces
NSMutableArray< FlutterSurface * > * _surfaces
Definition: FlutterSurfaceManager.mm:239
GetBorderColorForLayer
static NSColor * GetBorderColorForLayer(int layer)
Definition: FlutterSurfaceManager.mm:46
FlutterSurfaceManager(Private)::layers
NSArray< CALayer * > * layers
Definition: FlutterSurfaceManager.h:103
FlutterSurfaceManager()::_frontSurfaces
NSMutableArray< FlutterSurface * > * _frontSurfaces
Definition: FlutterSurfaceManager.mm:28
FlutterSurfaceManager()::_layers
NSMutableArray< CALayer * > * _layers
Definition: FlutterSurfaceManager.mm:31
FlutterSurface::ioSurface
IOSurfaceRef ioSurface
Definition: FlutterSurface.h:29
FlutterSurfaceManager(Private)::frontSurfaces
NSArray< FlutterSurface * > * frontSurfaces
Definition: FlutterSurfaceManager.h:102
FlutterSurfaceManager()::_containingLayer
CALayer * _containingLayer
Definition: FlutterSurfaceManager.mm:20
FlutterSurfaceManager.h
-[FlutterBackBufferCache count]
NSUInteger count()
Definition: FlutterSurfaceManager.mm:277
FlutterSurfaceManager
Definition: FlutterSurfaceManager.h:41
FlutterSurface::size
CGSize size
Definition: FlutterSurface.h:30
FlutterSurfaceManager()::_backBufferCache
FlutterBackBufferCache * _backBufferCache
Definition: FlutterSurfaceManager.mm:25
FlutterBackBufferCache
Definition: FlutterSurfaceManager.h:77
FlutterSurfaceManager()::_commandQueue
id< MTLCommandQueue > _commandQueue
Definition: FlutterSurfaceManager.mm:19
FlutterSurfaceManager()::_infoLayer
CATextLayer * _infoLayer
Definition: FlutterSurfaceManager.mm:36
FlutterSurface.h
FlutterSurface
Definition: FlutterSurface.h:13
FlutterSurfaceManagerDelegate-p
Definition: FlutterSurfaceManager.h:24
FlutterSurfaceManager()::_delegate
__weak id< FlutterSurfaceManagerDelegate > _delegate
Definition: FlutterSurfaceManager.mm:21
FlutterSurfacePresentInfo::surface
FlutterSurface * surface
Definition: FlutterSurfaceManager.h:17
kIdleDelay
static const double kIdleDelay
Definition: FlutterSurfaceManager.mm:236
FlutterSurfacePresentInfo::offset
CGPoint offset
Definition: FlutterSurfaceManager.h:18
UpdateContentSubLayers
static void UpdateContentSubLayers(CALayer *layer, IOSurfaceRef surface, CGFloat scale, CGSize surfaceSize, NSColor *borderColor, const std::vector< FlutterRect > &paintRegion)
Definition: FlutterSurfaceManager.mm:61
FlutterSurfacePresentInfo
Definition: FlutterSurfaceManager.h:15
FlutterSurfacePresentInfo::zIndex
size_t zIndex
Definition: FlutterSurfaceManager.h:19
FlutterSurfacePresentInfo::paintRegion
std::vector< FlutterRect > paintRegion
Definition: FlutterSurfaceManager.h:20
FlutterSurfaceManager()::_device
id< MTLDevice > _device
Definition: FlutterSurfaceManager.mm:18
FlutterSurfaceManager()::_enableSurfaceDebugInfo
NSNumber * _enableSurfaceDebugInfo
Definition: FlutterSurfaceManager.mm:35
FlutterSurfaceManager(Private)::backBufferCache
FlutterBackBufferCache * backBufferCache
Definition: FlutterSurfaceManager.h:101