Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smooth window resizing on macOS #21525

Merged
merged 19 commits into from
Oct 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,10 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExter
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h
Expand All @@ -1056,6 +1060,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart
FILE: ../../../flutter/shell/platform/darwin/macos/framework/module.modulemap
FILE: ../../../flutter/shell/platform/embedder/assets/EmbedderInfo.plist
Expand Down
10 changes: 10 additions & 0 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterExternalTextureGL.mm",
"framework/Source/FlutterMouseCursorPlugin.h",
"framework/Source/FlutterMouseCursorPlugin.mm",
"framework/Source/FlutterResizeSynchronizer.h",
"framework/Source/FlutterResizeSynchronizer.mm",
"framework/Source/FlutterSurfaceManager.h",
"framework/Source/FlutterSurfaceManager.mm",
"framework/Source/FlutterTextInputModel.h",
"framework/Source/FlutterTextInputModel.mm",
"framework/Source/FlutterTextInputPlugin.h",
Expand All @@ -62,11 +66,15 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterView.mm",
"framework/Source/FlutterViewController.mm",
"framework/Source/FlutterViewController_Internal.h",
"framework/Source/MacOSSwitchableGLContext.h",
"framework/Source/MacOSSwitchableGLContext.mm",
]

sources += _flutter_framework_headers

deps = [
"//flutter/flow:flow",
"//flutter/fml:fml",
"//flutter/shell/platform/common/cpp:common_cpp_switches",
"//flutter/shell/platform/darwin/common:framework_shared",
"//flutter/shell/platform/embedder:embedder_as_internal_library",
Expand All @@ -81,6 +89,8 @@ source_set("flutter_framework_source") {
libs = [
"Cocoa.framework",
"CoreVideo.framework",
"IOSurface.framework",
"QuartzCore.framework",
]
}

Expand Down
19 changes: 11 additions & 8 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ static bool OnPresent(FlutterEngine* engine) {
return [engine engineCallbackOnPresent];
}

static uint32_t OnFBO(FlutterEngine* engine) {
// There is currently no case where a different FBO is used, so no need to forward.
return 0;
static uint32_t OnFBO(FlutterEngine* engine, const FlutterFrameInfo* info) {
CGSize size = CGSizeMake(info->size.width, info->size.height);
return [engine.viewController.flutterView getFrameBufferIdForSize:size];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you update this to follow the format used in all of the other functions, where it forwards directly, with no logic, to an engine method? That structure was intentional.

}

static bool OnMakeResourceCurrent(FlutterEngine* engine) {
Expand Down Expand Up @@ -248,7 +248,8 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
.open_gl.make_current = (BoolCallback)OnMakeCurrent,
.open_gl.clear_current = (BoolCallback)OnClearCurrent,
.open_gl.present = (BoolCallback)OnPresent,
.open_gl.fbo_callback = (UIntCallback)OnFBO,
.open_gl.fbo_with_frame_info_callback = (UIntFrameInfoCallback)OnFBO,
.open_gl.fbo_reset_after_present = true,
.open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent,
.open_gl.gl_external_texture_frame_callback = (TextureFrameCallback)OnAcquireExternalTexture,
};
Expand Down Expand Up @@ -297,7 +298,6 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
const FlutterCustomTaskRunners custom_task_runners = {
.struct_size = sizeof(FlutterCustomTaskRunners),
.platform_task_runner = &cocoa_task_runner_description,
.render_task_runner = &cocoa_task_runner_description,
};
flutterArguments.custom_task_runners = &custom_task_runners;

Expand All @@ -322,6 +322,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
[self sendUserLocales];
[self updateWindowMetrics];
[self updateDisplayConfig];
self.viewController.flutterView.synchronousResizing = YES;
return YES;
}

Expand Down Expand Up @@ -362,7 +363,9 @@ - (void)setViewController:(FlutterViewController*)controller {
[self shutDownEngine];
_resourceContext = nil;
}
[self updateWindowMetrics];
if (_engine) {
self.viewController.flutterView.synchronousResizing = YES;
}
}

- (id<FlutterBinaryMessenger>)binaryMessenger {
Expand All @@ -380,7 +383,7 @@ - (BOOL)running {
- (NSOpenGLContext*)resourceContext {
if (!_resourceContext) {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADoubleBuffer, 0,
NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, 0,
};
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
_resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
Expand Down Expand Up @@ -479,7 +482,7 @@ - (bool)engineCallbackOnPresent {
if (!_mainOpenGLContext) {
return false;
}
[_mainOpenGLContext flushBuffer];
[self.viewController.flutterView present];
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#import <Cocoa/Cocoa.h>

@class FlutterResizeSynchronizer;

@protocol FlutterResizeSynchronizerDelegate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All protocols should have declaration comments explaining their role, per style guide.


// Invoked on raster thread; Delegate should flush the OpenGL context
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these comments need a . at the end, per style guide.

- (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the macOS embedding's headers should have nullability annotations.


// Invoked on platform thread; Delegate should flip the surfaces
- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer;

@end

// Encapsulates the logic for blocking platform thread during window resize as
// well as synchronizing the raster and platform thread during commit (presenting frame)
//
// Flow during window resize
//
// 1. Platform thread calls [synchronizer beginResize:notify:]
// This will hold the platform thread until we're ready to display contents.
// 2. Raster thread calls [synchronizer shouldEnsureSurfaceForSize:] with target size
// This will return false for any size other than target size
// 3. Raster thread calls [synchronizer requestCommit]
// Any commit calls before shouldEnsureSurfaceForSize: is called with the right
// size are simply ignored; There's no point rasterizing and displaying frames
// with wrong size.
// Both delegate methods (flush/commit) will be invoked before beginResize returns
//
// Flow during regular operation (no resizing)
//
// 1. Raster thread calls [synchronizer requestCommit]
// This will invoke [delegate flush:] on raster thread and
// [delegate commit:] on platform thread. The requestCommit call will be blocked
// until this is done. This is necessary to ensure that rasterizer won't start
// rasterizing next frame before we flipped the surface, which must be performed
// on platform thread
@interface FlutterResizeSynchronizer : NSObject

- (instancetype)initWithDelegate:(id<FlutterResizeSynchronizerDelegate>)delegate;

// Blocks the platform thread until
// - shouldEnsureSurfaceForSize is called with proper size and
// - requestCommit is called
// All requestCommit calls before `shouldEnsureSurfaceForSize` is called with
// expected size are ignored;
// The notify block is invoked immediately after synchronizer mutex is acquired
- (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify;

// Returns whether the view should ensure surfaces with given size;
// This will be false during resizing for any size other than size specified
// during beginResize
- (bool)shouldEnsureSurfaceForSize:(CGSize)size;

// Called from rasterizer thread, will block until delegate resizeSynchronizerCommit:
// method is called (on platform thread)
- (void)requestCommit;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h"

#import <mutex>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#include


@interface FlutterResizeSynchronizer () {
uint32_t cookie; // counter to detect stale callbacks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of these ivars are following the style guide's naming requirements. They all need to start with _

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the style used in all the rest of the macOS code and put comments on the line before the ivar, not after it.


std::mutex mutex;
std::condition_variable condBlockBeginResize; // used to block [beginResize:]
std::condition_variable condBlockRequestCommit; // used to block [requestCommit]

bool acceptingCommit; // if false, requestCommit calls are ignored until
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use BOOL, not bool in an ObjC class except when there's a compelling reason not to (e.g, it's used only in conjunction with a C++ API that uses bool).

(And please remember to change true/false to YES/NO when changing these.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. This was a c++ class in the first PR, I guess bool is a leftover.

// shouldEnsureSurfaceForSize is called with proper size
bool waiting; // waiting for resize to finish
bool pendingCommit; // requestCommit was called and [delegate commit:] must be performed on
// platform thread
CGSize newSize; // target size for resizing

__weak id<FlutterResizeSynchronizerDelegate> delegate;
}
@end

@implementation FlutterResizeSynchronizer

- (instancetype)initWithDelegate:(id<FlutterResizeSynchronizerDelegate>)delegate_ {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A parameter name should not end with an _ like this. It's both nonstandard, and very confusing in a codebase where this is the notation for C++ ivars.

(I assume this was done to distinguish from the ivar, but the correct way to handle that is to name the ivar according to the style guide.)

if (self = [super init]) {
iskakaushik marked this conversation as resolved.
Show resolved Hide resolved
acceptingCommit = true;
delegate = delegate_;
}
return self;
}

- (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify {
std::unique_lock<std::mutex> lock(mutex);
if (!delegate) {
return;
}

++cookie;

// from now on, ignore all incoming commits until the block below gets
// scheduled on raster thread
acceptingCommit = false;

// let pending commits finish to unblock the raster thread
pendingCommit = false;
condBlockBeginResize.notify_all();

// let the engine send resize notification
notify();

newSize = size;

waiting = true;

condBlockRequestCommit.wait(lock, [&] { return pendingCommit; });

[delegate resizeSynchronizerFlush:self];
[delegate resizeSynchronizerCommit:self];
pendingCommit = false;
condBlockBeginResize.notify_all();

waiting = false;
}

- (bool)shouldEnsureSurfaceForSize:(CGSize)size {
std::unique_lock<std::mutex> lock(mutex);
if (!acceptingCommit) {
if (CGSizeEqualToSize(newSize, size)) {
acceptingCommit = true;
}
}
return acceptingCommit;
}

- (void)requestCommit {
std::unique_lock<std::mutex> lock(mutex);
if (!acceptingCommit) {
return;
}

pendingCommit = true;
if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call
condBlockRequestCommit.notify_all();
condBlockBeginResize.wait(lock, [&]() { return !pendingCommit; });
} 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 = false;
condBlockBeginResize.notify_all();
}
});
condBlockBeginResize.wait(lock, [&]() { return !pendingCommit; });
}
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import <Cocoa/Cocoa.h>

// Manages the IOSurfaces for FlutterView
@interface FlutterSurfaceManager : NSObject

- (instancetype)initWithLayer:(CALayer*)layer openGLContext:(NSOpenGLContext*)opengLContext;

- (void)ensureSurfaceSize:(CGSize)size;
- (void)swapBuffers;

- (uint32_t)glFrameBufferId;

@end
Loading