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 
6 
7 #import <Metal/Metal.h>
8 
9 #include <algorithm>
10 
11 #include "flutter/fml/logging.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.
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.
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 
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