7 #import <Metal/Metal.h>
11 #include "flutter/fml/logging.h"
50 [NSColor yellowColor],
52 [NSColor magentaColor],
54 [NSColor purpleColor],
55 [NSColor orangeColor],
58 return colors[layer % colors.count];
68 const std::vector<FlutterRect>& paintRegion) {
70 while (layer.sublayers.count > paintRegion.size()) {
71 [layer.sublayers.lastObject removeFromSuperlayer];
74 while (layer.sublayers.count < paintRegion.size()) {
75 CALayer* newLayer = [CALayer layer];
76 [layer addSublayer:newLayer];
79 for (
size_t i = 0; i < paintRegion.size(); i++) {
80 CALayer* subLayer = [layer.sublayers objectAtIndex:i];
81 const auto& rect = paintRegion[i];
82 subLayer.frame = CGRectMake(rect.left / scale, rect.top / scale,
83 (rect.right - rect.left) / scale, (rect.bottom - rect.top) / scale);
85 double width = surfaceSize.width;
86 double height = surfaceSize.height;
88 subLayer.contentsRect =
89 CGRectMake(rect.left / width, rect.top / height, (rect.right - rect.left) / width,
90 (rect.bottom - rect.top) / height);
92 if (borderColor != nil) {
94 subLayer.borderColor = borderColor.CGColor;
95 subLayer.borderWidth = 1.0;
98 subLayer.contents = (__bridge id)surface;
104 - (instancetype)initWithDevice:(
id<MTLDevice>)device
105 commandQueue:(
id<MTLCommandQueue>)commandQueue
106 layer:(CALayer*)containingLayer
108 if (
self = [super init]) {
110 _commandQueue = commandQueue;
111 _containingLayer = containingLayer;
112 _delegate = delegate;
115 _frontSurfaces = [NSMutableArray array];
116 _layers = [NSMutableArray array];
122 return _backBufferCache;
126 return _frontSurfaces;
134 FlutterSurface* surface = [_backBufferCache removeSurfaceForSize:size];
135 if (surface == nil) {
136 surface = [[
FlutterSurface alloc] initWithSize:size device:_device];
141 - (BOOL)enableSurfaceDebugInfo {
142 if (_enableSurfaceDebugInfo == nil) {
143 _enableSurfaceDebugInfo =
144 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableSurfaceDebugInfo"];
145 if (_enableSurfaceDebugInfo == nil) {
146 _enableSurfaceDebugInfo = @NO;
149 return [_enableSurfaceDebugInfo boolValue];
153 FML_DCHECK([NSThread isMainThread]);
156 [_backBufferCache returnSurfaces:_frontSurfaces];
159 [_frontSurfaces removeAllObjects];
161 [_frontSurfaces addObject:info.surface];
165 while (_layers.count > _frontSurfaces.count) {
166 [_layers.lastObject removeFromSuperlayer];
167 [_layers removeLastObject];
169 while (_layers.count < _frontSurfaces.count) {
170 CALayer* layer = [CALayer layer];
171 [_containingLayer addSublayer:layer];
172 [_layers addObject:layer];
175 bool enableSurfaceDebugInfo =
self.enableSurfaceDebugInfo;
178 for (
size_t i = 0; i < surfaces.count; ++i) {
180 CALayer* layer = _layers[i];
181 CGFloat scale = _containingLayer.contentsScale;
183 layer.frame = CGRectMake(info.
offset.x / scale, info.
offset.y / scale,
187 layer.frame = CGRectZero;
192 layer.zPosition = info.
zIndex;
195 if (enableSurfaceDebugInfo) {
196 if (_infoLayer == nil) {
197 _infoLayer = [[CATextLayer alloc] init];
198 [_containingLayer addSublayer:_infoLayer];
199 _infoLayer.fontSize = 15;
200 _infoLayer.foregroundColor = [NSColor yellowColor].CGColor;
201 _infoLayer.frame = CGRectMake(15, 15, 300, 100);
202 _infoLayer.contentsScale = _containingLayer.contentsScale;
203 _infoLayer.zPosition = 100000;
205 _infoLayer.string = [NSString stringWithFormat:@"Surface count: %li", _layers.count];
209 static CGSize GetRequiredFrameSize(NSArray<FlutterSurfacePresentInfo*>* surfaces) {
210 CGSize size = CGSizeZero;
219 atTime:(CFTimeInterval)presentationTime
220 notify:(dispatch_block_t)notify {
221 id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
222 [commandBuffer commit];
223 [commandBuffer waitUntilScheduled];
225 dispatch_block_t presentBlock = ^{
227 CGSize size = GetRequiredFrameSize(surfaces);
228 [_delegate onPresent:size
230 _lastPresentationTime = presentationTime;
231 [
self commit:surfaces];
238 if (presentationTime > 0) {
250 CFTimeInterval minPresentationTime = (presentationTime + _lastPresentationTime) / 2.0;
251 CFTimeInterval now = CACurrentMediaTime();
252 if (now < minPresentationTime) {
253 NSTimer* timer = [NSTimer timerWithTimeInterval:minPresentationTime - now
255 block:^(NSTimer* timer) {
258 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
284 - (instancetype)init {
285 if (
self = [super init]) {
286 self->_surfaces = [[NSMutableArray alloc] init];
287 self->_surfaceAge = [NSMapTable weakToStrongObjectsMapTable];
293 NSNumber* age = [_surfaceAge objectForKey:surface];
294 return age != nil ? age.intValue : 0;
298 [_surfaceAge setObject:@(age) forKey:surface];
302 @
synchronized(
self) {
304 if (_surfaces.firstObject != nil && !CGSizeEqualToSize(_surfaces.firstObject.size, size)) {
305 [_surfaces removeAllObjects];
315 (res == nil || [
self ageForSurface:res] > [
self ageForSurface:surface])) {
320 [_surfaces removeObject:res];
327 @
synchronized(
self) {
329 [
self setAge:0 forSurface:surface];
332 [
self setAge:[
self ageForSurface:surface] + 1 forSurface:surface];
335 [_surfaces addObjectsFromArray:returnedSurfaces];
342 [_surfaces filterUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(FlutterSurface* surface,
343 NSDictionary* bindings) {
344 return [
self ageForSurface:surface] < kSurfaceEvictionAge;
349 [
self performSelectorOnMainThread:@selector(reschedule) withObject:nil waitUntilDone:NO];
353 @
synchronized(
self) {
354 return _surfaces.count;
359 @
synchronized(
self) {
360 [_surfaces removeAllObjects];
365 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];
366 [
self performSelector:@selector(onIdle) withObject:nil afterDelay:kIdleDelay];
370 [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil];