7 #import <Metal/Metal.h>
11 #include "flutter/fml/logging.h"
51 [NSColor yellowColor],
53 [NSColor magentaColor],
55 [NSColor purpleColor],
56 [NSColor orangeColor],
59 return colors[layer % colors.count];
69 const std::vector<FlutterRect>& paintRegion) {
71 while (layer.sublayers.count > paintRegion.size()) {
72 [layer.sublayers.lastObject removeFromSuperlayer];
75 while (layer.sublayers.count < paintRegion.size()) {
76 CALayer* newLayer = [CALayer layer];
77 [layer addSublayer:newLayer];
80 for (
size_t i = 0; i < paintRegion.size(); i++) {
81 CALayer* subLayer = [layer.sublayers objectAtIndex:i];
82 const auto& rect = paintRegion[i];
83 subLayer.frame = CGRectMake(rect.left / scale, rect.top / scale,
84 (rect.right - rect.left) / scale, (rect.bottom - rect.top) / scale);
86 double width = surfaceSize.width;
87 double height = surfaceSize.height;
89 subLayer.contentsRect =
90 CGRectMake(rect.left / width, rect.top / height, (rect.right - rect.left) / width,
91 (rect.bottom - rect.top) / height);
93 if (borderColor != nil) {
95 subLayer.borderColor = borderColor.CGColor;
96 subLayer.borderWidth = 1.0;
99 subLayer.contents = (__bridge id)surface;
105 - (instancetype)initWithDevice:(
id<MTLDevice>)device
106 commandQueue:(
id<MTLCommandQueue>)commandQueue
107 layer:(CALayer*)containingLayer
109 wideGamut:(BOOL)wideGamut {
110 if (
self = [super init]) {
112 _commandQueue = commandQueue;
113 _containingLayer = containingLayer;
114 _delegate = delegate;
115 _wideGamut = wideGamut;
117 _frontSurfaces = [NSMutableArray array];
118 _layers = [NSMutableArray array];
123 - (void)setEnableWideGamut:(BOOL)enableWideGamut {
124 FML_DCHECK([NSThread isMainThread]);
125 if (_wideGamut == enableWideGamut) {
128 _wideGamut = enableWideGamut;
131 [_backBufferCache flush];
134 [_frontSurfaces removeAllObjects];
138 return _backBufferCache;
142 return _frontSurfaces;
150 FlutterSurface* surface = [_backBufferCache removeSurfaceForSize:size];
151 if (surface == nil) {
152 surface = [[
FlutterSurface alloc] initWithSize:size device:_device enableWideGamut:_wideGamut];
157 - (BOOL)enableSurfaceDebugInfo {
158 if (_enableSurfaceDebugInfo == nil) {
159 _enableSurfaceDebugInfo =
160 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableSurfaceDebugInfo"];
161 if (_enableSurfaceDebugInfo == nil) {
162 _enableSurfaceDebugInfo = @NO;
165 return [_enableSurfaceDebugInfo boolValue];
169 FML_DCHECK([NSThread isMainThread]);
174 if (info.surface.isWideGamut != _wideGamut) {
180 [_backBufferCache returnSurfaces:_frontSurfaces];
183 [_frontSurfaces removeAllObjects];
185 [_frontSurfaces addObject:info.surface];
189 while (_layers.count > _frontSurfaces.count) {
190 [_layers.lastObject removeFromSuperlayer];
191 [_layers removeLastObject];
193 while (_layers.count < _frontSurfaces.count) {
194 CALayer* layer = [CALayer layer];
195 [_containingLayer addSublayer:layer];
196 [_layers addObject:layer];
199 bool enableSurfaceDebugInfo =
self.enableSurfaceDebugInfo;
202 for (
size_t i = 0; i < surfaces.count; ++i) {
204 CALayer* layer = _layers[i];
205 CGFloat scale = _containingLayer.contentsScale;
207 layer.frame = CGRectMake(info.
offset.x / scale, info.
offset.y / scale,
211 layer.frame = CGRectZero;
216 layer.zPosition = info.
zIndex;
219 if (enableSurfaceDebugInfo) {
220 if (_infoLayer == nil) {
221 _infoLayer = [[CATextLayer alloc] init];
222 [_containingLayer addSublayer:_infoLayer];
223 _infoLayer.fontSize = 15;
224 _infoLayer.foregroundColor = [NSColor yellowColor].CGColor;
225 _infoLayer.frame = CGRectMake(15, 15, 300, 100);
226 _infoLayer.contentsScale = _containingLayer.contentsScale;
227 _infoLayer.zPosition = 100000;
229 _infoLayer.string = [NSString stringWithFormat:@"Surface count: %li", _layers.count];
233 static CGSize GetRequiredFrameSize(NSArray<FlutterSurfacePresentInfo*>* surfaces) {
234 CGSize size = CGSizeZero;
243 atTime:(CFTimeInterval)presentationTime
244 notify:(dispatch_block_t)notify {
245 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
246 [commandBuffer commit];
247 [commandBuffer waitUntilScheduled];
249 CGSize size = GetRequiredFrameSize(surfaces);
251 CFTimeInterval delay = 0;
253 if (presentationTime > 0) {
265 CFTimeInterval minPresentationTime = (presentationTime + _lastPresentationTime) / 2.0;
266 CFTimeInterval now = CACurrentMediaTime();
267 delay = std::max(minPresentationTime - now, 0.0);
269 [_delegate onPresent:size
271 _lastPresentationTime = presentationTime;
272 [CATransaction begin];
273 [CATransaction setDisableActions:YES];
274 [
self commit:surfaces];
278 [CATransaction commit];
302 - (instancetype)init {
303 if (
self = [super init]) {
304 self->_surfaces = [[NSMutableArray alloc] init];
305 self->_surfaceAge = [NSMapTable weakToStrongObjectsMapTable];
311 NSNumber* age = [_surfaceAge objectForKey:surface];
312 return age != nil ? age.intValue : 0;
316 [_surfaceAge setObject:@(age) forKey:surface];
320 @
synchronized(
self) {
322 if (_surfaces.firstObject != nil && !CGSizeEqualToSize(_surfaces.firstObject.size, size)) {
323 [_surfaces removeAllObjects];
333 (res == nil || [
self ageForSurface:res] > [
self ageForSurface:surface])) {
338 [_surfaces removeObject:res];
344 - (void)returnSurfaces:(nonnull NSArray<
FlutterSurface*>*)returnedSurfaces {
345 @
synchronized(
self) {
347 [
self setAge:0 forSurface:surface];
350 [
self setAge:[
self ageForSurface:surface] + 1 forSurface:surface];
353 [_surfaces addObjectsFromArray:returnedSurfaces];
360 [_surfaces filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(FlutterSurface* surface,
361 NSDictionary* bindings) {
362 return [
self ageForSurface:surface] < kSurfaceEvictionAge;
367 [
self performSelectorOnMainThread:@selector(reschedule) withObject:nil waitUntilDone:NO];
371 @
synchronized(
self) {
372 [_surfaces removeAllObjects];
376 - (NSUInteger)
count {
377 @
synchronized(
self) {
378 return _surfaces.count;
387 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];
388 [
self performSelector:@selector(onIdle) withObject:nil afterDelay:kIdleDelay];
392 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];
static void UpdateContentSubLayers(CALayer *layer, IOSurfaceRef surface, CGFloat scale, CGSize surfaceSize, NSColor *borderColor, const std::vector< FlutterRect > &paintRegion)
static NSColor * GetBorderColorForLayer(int layer)
static const double kIdleDelay
static const int kSurfaceEvictionAge
NSMutableArray< FlutterSurface * > * _surfaces
NSMapTable< FlutterSurface *, NSNumber * > * _surfaceAge
CALayer * _containingLayer
id< MTLCommandQueue > _commandQueue
NSMutableArray< FlutterSurface * > * _frontSurfaces
__weak id< FlutterSurfaceManagerDelegate > _delegate
FlutterBackBufferCache * _backBufferCache
CFTimeInterval _lastPresentationTime
NSMutableArray< CALayer * > * _layers
NSNumber * _enableSurfaceDebugInfo
NSArray< CALayer * > * layers
FlutterBackBufferCache * backBufferCache
NSArray< FlutterSurface * > * frontSurfaces
std::vector< FlutterRect > paintRegion