blob: 6e4873d668e19d9eab4c9becd81b8e4d3388314c [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h"
#include <mutex>
@interface FlutterResizeSynchronizer () {
// Counter to detect stale callbacks.
uint32_t _cookie;
std::mutex _mutex;
// Used to block [beginResize:].
std::condition_variable _condBlockBeginResize;
// Used to block [requestCommit].
std::condition_variable _condBlockRequestCommit;
// Whether a frame was received; the synchronizer doesn't block platform thread during resize
// until it knows that framework is running and producing frames
BOOL _receivedFirstFrame;
// If NO, requestCommit calls are ignored until shouldEnsureSurfaceForSize is called with
// proper size.
BOOL _acceptingCommit;
// Waiting for resize to finish.
BOOL _waiting;
// RequestCommit was called and [delegate commit:] must be performed on platform thread.
BOOL _pendingCommit;
// Target size for resizing.
CGSize _newSize;
// if YES prevents all synchronization
BOOL _shuttingDown;
__weak id<FlutterResizeSynchronizerDelegate> _delegate;
}
@end
@implementation FlutterResizeSynchronizer
- (instancetype)initWithDelegate:(id<FlutterResizeSynchronizerDelegate>)delegate {
if (self = [super init]) {
_acceptingCommit = YES;
_delegate = delegate;
}
return self;
}
- (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify {
std::unique_lock<std::mutex> lock(_mutex);
if (!_delegate) {
return;
}
if (!_receivedFirstFrame || _shuttingDown) {
// No blocking until framework produces at least one frame
notify();
return;
}
++_cookie;
// from now on, ignore all incoming commits until the block below gets
// scheduled on raster thread
_acceptingCommit = NO;
// let pending commits finish to unblock the raster thread
_pendingCommit = NO;
_condBlockBeginResize.notify_all();
// let the engine send resize notification
notify();
_newSize = size;
_waiting = YES;
_condBlockRequestCommit.wait(lock, [&] { return _pendingCommit || _shuttingDown; });
[_delegate resizeSynchronizerFlush:self];
[_delegate resizeSynchronizerCommit:self];
_pendingCommit = NO;
_condBlockBeginResize.notify_all();
_waiting = NO;
}
- (BOOL)shouldEnsureSurfaceForSize:(CGSize)size {
std::unique_lock<std::mutex> lock(_mutex);
if (!_receivedFirstFrame) {
return YES;
}
if (!_acceptingCommit) {
if (CGSizeEqualToSize(_newSize, size)) {
_acceptingCommit = YES;
}
}
return _acceptingCommit;
}
- (void)requestCommit {
std::unique_lock<std::mutex> lock(_mutex);
if (!_acceptingCommit || _shuttingDown) {
return;
}
_receivedFirstFrame = YES;
_pendingCommit = YES;
if (_waiting) { // BeginResize is in progress, interrupt it and schedule commit call
_condBlockRequestCommit.notify_all();
_condBlockBeginResize.wait(lock, [&]() { return !_pendingCommit || _shuttingDown; });
} else {
// No resize, schedule commit on platform thread and wait until either done
// or interrupted by incoming BeginResize
[_delegate resizeSynchronizerFlush:self];
dispatch_async(dispatch_get_main_queue(), [self, cookie = _cookie] {
std::unique_lock<std::mutex> lock(_mutex);
if (cookie == _cookie) {
if (_delegate) {
[_delegate resizeSynchronizerCommit:self];
}
_pendingCommit = NO;
_condBlockBeginResize.notify_all();
}
});
_condBlockBeginResize.wait(lock, [&]() { return !_pendingCommit || _shuttingDown; });
}
}
- (void)shutdown {
std::unique_lock<std::mutex> lock(_mutex);
_shuttingDown = YES;
_condBlockBeginResize.notify_all();
_condBlockRequestCommit.notify_all();
}
@end