From 586738aadd0125ec14b6b4e40c2b1f9453524528 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Sat, 29 Aug 2020 21:37:09 +0200 Subject: [PATCH 01/19] Move raster task runner away from platform thread --- shell/platform/darwin/macos/framework/Source/FlutterEngine.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 37eb3521bc2ed..659f4b4b13941 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -297,7 +297,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; From 4f27eaf339e71abdb5223485797244db5e7558cd Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 11 Sep 2020 19:28:57 +0200 Subject: [PATCH 02/19] Smooth resizing for macOS embedder --- shell/platform/darwin/macos/BUILD.gn | 6 + .../macos/framework/Source/FlutterEngine.mm | 20 +-- .../Source/FlutterResizeSynchronizer.h | 34 +++++ .../Source/FlutterResizeSynchronizer.mm | 97 ++++++++++++++ .../framework/Source/FlutterSurfaceManager.h | 13 ++ .../framework/Source/FlutterSurfaceManager.mm | 119 ++++++++++++++++++ .../macos/framework/Source/FlutterView.h | 8 +- .../macos/framework/Source/FlutterView.mm | 82 ++++++++++-- 8 files changed, 363 insertions(+), 16 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 9800139a8a1cf..68e7ff5d14e4b 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -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", @@ -81,6 +85,8 @@ source_set("flutter_framework_source") { libs = [ "Cocoa.framework", "CoreVideo.framework", + "IOSurface.framework", + "QuartzCore.framework", ] } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 659f4b4b13941..0158e591c7820 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -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]; } static bool OnMakeResourceCurrent(FlutterEngine* engine) { @@ -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, }; @@ -319,8 +320,8 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { } [self sendUserLocales]; - [self updateWindowMetrics]; [self updateDisplayConfig]; + [self.viewController.flutterView start]; return YES; } @@ -361,7 +362,9 @@ - (void)setViewController:(FlutterViewController*)controller { [self shutDownEngine]; _resourceContext = nil; } - [self updateWindowMetrics]; + if (_engine) { + [self.viewController.flutterView start]; + } } - (id)binaryMessenger { @@ -379,7 +382,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]; @@ -412,6 +415,7 @@ - (void)updateDisplayConfig { CVDisplayLinkRelease(displayLinkRef); } +// Must be driven by FlutterView (i.e. [FlutterView start]) - (void)updateWindowMetrics { if (!_engine) { return; @@ -478,7 +482,7 @@ - (bool)engineCallbackOnPresent { if (!_mainOpenGLContext) { return false; } - [_mainOpenGLContext flushBuffer]; + [self.viewController.flutterView present]; return true; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h new file mode 100644 index 0000000000000..30a68a47b35eb --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h @@ -0,0 +1,34 @@ +#import + +@class FlutterResizeSynchronizer; + +@protocol FlutterResizeSynchronizerDelegate + +// Invoked on platform thread; Delegate should flush OpenGL context and +// flip the surfaces +- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer; + +@end + +// Encapsulates the logic for blocking platform thread during window resize +@interface FlutterResizeSynchronizer : NSObject + +- (instancetype)initWithDelegate:(id)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; +- (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 diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm new file mode 100644 index 0000000000000..b689cfe28ad7b --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm @@ -0,0 +1,97 @@ +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h" + +#import + +@interface FlutterResizeSynchronizer () { + uint32_t cookie; // counter to detect stale callbacks + std::mutex mutex; + std::condition_variable condRun; + std::condition_variable condBlock; + bool accepting; + bool waiting; + bool pendingCommit; + CGSize newSize; + __weak id delegate; +} +@end + +@implementation FlutterResizeSynchronizer + +- (instancetype)initWithDelegate:(id)delegate_ { + if (self = [super init]) { + accepting = true; + delegate = delegate_; + } + return self; +} + +- (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { + std::unique_lock lock(mutex); + if (!delegate) { + return; + } + + ++cookie; + + // from now on, ignore all incoming commits until the block below gets + // scheduled on raster thread + accepting = false; + + // let pending commits finish to unblock the raster thread + condRun.notify_all(); + + // let the engine send resize notification + notify(); + + newSize = size; + + waiting = true; + + condBlock.wait(lock); + + if (pendingCommit) { + [delegate resizeSynchronizerCommit:self]; + pendingCommit = false; + condRun.notify_all(); + } + + waiting = false; +} + +- (bool)shouldEnsureSurfaceForSize:(CGSize)size { + std::unique_lock lock(mutex); + if (!accepting) { + if (CGSizeEqualToSize(newSize, size)) { + accepting = true; + } + } + return accepting; +} + +- (void)requestCommit { + std::unique_lock lock(mutex); + if (!accepting) { + return; + } + + if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call + pendingCommit = true; + condBlock.notify_all(); + condRun.wait(lock); + } else { + // No resize, schedule commit on platform thread and wait until either done + // or interrupted by incoming BeginResize + dispatch_async(dispatch_get_main_queue(), [self, cookie_ = cookie] { + std::unique_lock lock(mutex); + if (cookie_ == cookie) { + if (delegate) { + [delegate resizeSynchronizerCommit:self]; + } + condRun.notify_all(); + } + }); + condRun.wait(lock); + } +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h new file mode 100644 index 0000000000000..470f4f9400fda --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h @@ -0,0 +1,13 @@ +#import + +// 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 diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm new file mode 100644 index 0000000000000..657b84d837eb4 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -0,0 +1,119 @@ +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" + +#include + +enum { + kFront = 0, + kBack = 1, + kBufferCount, +}; + +@interface FlutterSurfaceManager () { + CGSize surfaceSize; + CALayer* layer; + NSOpenGLContext* openGLContext; + uint32_t _frameBufferId[kBufferCount]; + uint32_t _backingTexture[kBufferCount]; + IOSurfaceRef _ioSurface[kBufferCount]; +} +@end + +@implementation FlutterSurfaceManager + +- (instancetype)initWithLayer:(CALayer*)layer_ openGLContext:(NSOpenGLContext*)opengLContext_ { + if (self = [super init]) { + layer = layer_; + openGLContext = opengLContext_; + + NSOpenGLContext* prev = [NSOpenGLContext currentContext]; + [openGLContext makeCurrentContext]; + glGenFramebuffers(2, _frameBufferId); + glGenTextures(2, _backingTexture); + + glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[0]); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[0]); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[1]); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[1]); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + if (prev) { + [prev makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + } + } + return self; +} + +- (void)ensureSurfaceSize:(CGSize)size { + if (CGSizeEqualToSize(size, surfaceSize)) { + return; + } + surfaceSize = size; + NSOpenGLContext* prev = [NSOpenGLContext currentContext]; + [openGLContext makeCurrentContext]; + + for (int i = 0; i < 2; ++i) { + if (_ioSurface[i]) { + CFRelease(_ioSurface[i]); + } + unsigned pixelFormat = 'BGRA'; + unsigned bytesPerElement = 4; + + size_t bytesPerRow = + IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.width * bytesPerElement); + size_t totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.height * bytesPerRow); + NSDictionary* options = @{ + (id)kIOSurfaceWidth : @(size.width), + (id)kIOSurfaceHeight : @(size.height), + (id)kIOSurfacePixelFormat : @(pixelFormat), + (id)kIOSurfaceBytesPerElement : @(bytesPerElement), + (id)kIOSurfaceBytesPerRow : @(bytesPerRow), + (id)kIOSurfaceAllocSize : @(totalBytes), + }; + _ioSurface[i] = IOSurfaceCreate((CFDictionaryRef)options); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[i]); + + CGLTexImageIOSurface2D(CGLGetCurrentContext(), GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, + int(size.width), int(size.height), GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + _ioSurface[i], 0 /* plane */); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[i]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, + _backingTexture[i], 0); + } + if (prev) { + [prev makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + } +} + +- (void)swapBuffers { + [layer setContents:(__bridge id)_ioSurface[kBack]]; + std::swap(_ioSurface[kBack], _ioSurface[kFront]); + std::swap(_frameBufferId[kBack], _frameBufferId[kFront]); + std::swap(_backingTexture[kBack], _backingTexture[kFront]); +} + +- (uint32_t)glFrameBufferId { + return _frameBufferId[kBack]; +} + +- (void)dealloc { + for (int i = 0; i < kBufferCount; ++i) { + if (_ioSurface[i]) { + CFRelease(_ioSurface[i]); + } + } +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index e8c7ec3bd3d93..4123d1a5fde30 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -18,7 +18,9 @@ * View capable of acting as a rendering target and input source for the Flutter * engine. */ -@interface FlutterView : NSOpenGLView +@interface FlutterView : NSView + +@property(readwrite, nonatomic, nonnull) NSOpenGLContext* openGLContext; - (nullable instancetype)initWithFrame:(NSRect)frame shareContext:(nonnull NSOpenGLContext*)shareContext @@ -35,4 +37,8 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder*)coder NS_UNAVAILABLE; - (nonnull instancetype)init NS_UNAVAILABLE; +- (void)start; +- (void)present; +- (int)getFrameBufferIdForSize:(CGSize)size; + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 1b46ac600ed9e..9db041e37eacd 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -3,11 +3,24 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" -@implementation FlutterView { +#import +#import + +@interface FlutterView () { __weak id _reshapeListener; + FlutterResizeSynchronizer* resizeSynchronizer; + FlutterSurfaceManager* surfaceManager; + BOOL active; + CALayer* contentLayer; } +@end + +@implementation FlutterView + - (instancetype)initWithShareContext:(NSOpenGLContext*)shareContext reshapeListener:(id)reshapeListener { return [self initWithFrame:NSZeroRect shareContext:shareContext reshapeListener:reshapeListener]; @@ -20,14 +33,73 @@ - (instancetype)initWithFrame:(NSRect)frame if (self) { self.openGLContext = [[NSOpenGLContext alloc] initWithFormat:shareContext.pixelFormat shareContext:shareContext]; + + [self setWantsLayer:YES]; + + // Layer for content (which will be set by surfaceManager). This is separate from + // self.layer because it needs to be flipped vertically (using layer.sublayerTransform) + contentLayer = [[CALayer alloc] init]; + [self.layer addSublayer:contentLayer]; + + resizeSynchronizer = [[FlutterResizeSynchronizer alloc] initWithDelegate:self]; + surfaceManager = [[FlutterSurfaceManager alloc] initWithLayer:contentLayer + openGLContext:self.openGLContext]; + _reshapeListener = reshapeListener; - self.wantsBestResolutionOpenGLSurface = YES; } return self; } +- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { + [self.openGLContext makeCurrentContext]; + glFlush(); + [NSOpenGLContext clearCurrentContext]; + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + self.layer.frame = self.bounds; + self.layer.sublayerTransform = CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, + -self.layer.bounds.size.height, 0); + contentLayer.frame = self.layer.bounds; + + [surfaceManager swapBuffers]; + + [CATransaction commit]; +} + +- (int)getFrameBufferIdForSize:(CGSize)size { + if ([resizeSynchronizer shouldEnsureSurfaceForSize:size]) { + [surfaceManager ensureSurfaceSize:size]; + } + return [surfaceManager glFrameBufferId]; +} + +- (void)present { + [resizeSynchronizer requestCommit]; +} + +- (void)start { + active = YES; + [self reshaped]; +} + +- (void)reshaped { + if (active) { + CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; + [resizeSynchronizer beginResize:scaledSize + notify:^{ + [_reshapeListener viewDidReshape:self]; + }]; + } +} + #pragma mark - NSView overrides +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self reshaped]; +} + /** * Declares that the view uses a flipped coordinate system, consistent with Flutter conventions. */ @@ -39,17 +111,13 @@ - (BOOL)isOpaque { return YES; } -- (void)reshape { - [super reshape]; - [_reshapeListener viewDidReshape:self]; -} - - (BOOL)acceptsFirstResponder { return YES; } - (void)viewDidChangeBackingProperties { [super viewDidChangeBackingProperties]; + // Force redraw [_reshapeListener viewDidReshape:self]; } From 78121e5c3d8de23710aec8eaff34ccdf438053d0 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 18:51:57 +0200 Subject: [PATCH 03/19] Consistency --- .../darwin/macos/framework/Source/FlutterSurfaceManager.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 657b84d837eb4..6f8cd09439e4e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -59,7 +59,7 @@ - (void)ensureSurfaceSize:(CGSize)size { NSOpenGLContext* prev = [NSOpenGLContext currentContext]; [openGLContext makeCurrentContext]; - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < kBufferCount; ++i) { if (_ioSurface[i]) { CFRelease(_ioSurface[i]); } From 1376e9651444fbc043ce11bcb8c4352543159334 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 19:21:55 +0200 Subject: [PATCH 04/19] Fix possible spurious wake-ups --- .../Source/FlutterResizeSynchronizer.mm | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm index b689cfe28ad7b..7348a7741273f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm @@ -5,8 +5,8 @@ @interface FlutterResizeSynchronizer () { uint32_t cookie; // counter to detect stale callbacks std::mutex mutex; - std::condition_variable condRun; - std::condition_variable condBlock; + std::condition_variable condNoPendingCommit; + std::condition_variable condPendingCommit; bool accepting; bool waiting; bool pendingCommit; @@ -38,7 +38,8 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { accepting = false; // let pending commits finish to unblock the raster thread - condRun.notify_all(); + pendingCommit = false; + condNoPendingCommit.notify_all(); // let the engine send resize notification notify(); @@ -47,13 +48,11 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { waiting = true; - condBlock.wait(lock); + condPendingCommit.wait(lock, [&] { return pendingCommit; }); - if (pendingCommit) { - [delegate resizeSynchronizerCommit:self]; - pendingCommit = false; - condRun.notify_all(); - } + [delegate resizeSynchronizerCommit:self]; + pendingCommit = false; + condNoPendingCommit.notify_all(); waiting = false; } @@ -76,21 +75,23 @@ - (void)requestCommit { if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call pendingCommit = true; - condBlock.notify_all(); - condRun.wait(lock); + condPendingCommit.notify_all(); + condNoPendingCommit.wait(lock, [&]() { return !pendingCommit; }); } else { // No resize, schedule commit on platform thread and wait until either done // or interrupted by incoming BeginResize + pendingCommit = true; dispatch_async(dispatch_get_main_queue(), [self, cookie_ = cookie] { std::unique_lock lock(mutex); if (cookie_ == cookie) { if (delegate) { [delegate resizeSynchronizerCommit:self]; } - condRun.notify_all(); + pendingCommit = false; + condNoPendingCommit.notify_all(); } }); - condRun.wait(lock); + condNoPendingCommit.wait(lock, [&]() { return !pendingCommit; }); } } From 321167083229c80c7d3d2c4105e62c1722b2fab0 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 19:59:10 +0200 Subject: [PATCH 05/19] Separate flush and commit Flush can be performed on raster thread --- .../macos/framework/Source/FlutterResizeSynchronizer.h | 6 ++++-- .../macos/framework/Source/FlutterResizeSynchronizer.mm | 2 ++ shell/platform/darwin/macos/framework/Source/FlutterView.mm | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h index 30a68a47b35eb..ebd53b4678fa4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h @@ -4,8 +4,10 @@ @protocol FlutterResizeSynchronizerDelegate -// Invoked on platform thread; Delegate should flush OpenGL context and -// flip the surfaces +// Invoked on raster thread; Delegate should flush the OpenGL context +- (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer; + +// Invoked on platform thread; Delegate should flip the surfaces - (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm index 7348a7741273f..6a084664a656c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm @@ -50,6 +50,7 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { condPendingCommit.wait(lock, [&] { return pendingCommit; }); + [delegate resizeSynchronizerFlush:self]; [delegate resizeSynchronizerCommit:self]; pendingCommit = false; condNoPendingCommit.notify_all(); @@ -80,6 +81,7 @@ - (void)requestCommit { } else { // No resize, schedule commit on platform thread and wait until either done // or interrupted by incoming BeginResize + [delegate resizeSynchronizerFlush:self]; pendingCommit = true; dispatch_async(dispatch_get_main_queue(), [self, cookie_ = cookie] { std::unique_lock lock(mutex); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 9db041e37eacd..2eeee2569db04 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -50,11 +50,13 @@ - (instancetype)initWithFrame:(NSRect)frame return self; } -- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { +- (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer { [self.openGLContext makeCurrentContext]; glFlush(); [NSOpenGLContext clearCurrentContext]; +} +- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { [CATransaction begin]; [CATransaction setDisableActions:YES]; self.layer.frame = self.bounds; From f49de06ee14361d3ff2d484f8df3b0f6e228e047 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 20:29:11 +0200 Subject: [PATCH 06/19] move assignment out of block --- .../darwin/macos/framework/Source/FlutterResizeSynchronizer.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm index 6a084664a656c..d6ea1499e96b1 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm @@ -74,15 +74,14 @@ - (void)requestCommit { return; } + pendingCommit = true; if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call - pendingCommit = true; condPendingCommit.notify_all(); condNoPendingCommit.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]; - pendingCommit = true; dispatch_async(dispatch_get_main_queue(), [self, cookie_ = cookie] { std::unique_lock lock(mutex); if (cookie_ == cookie) { From 6a5b14eeb4652913b67ef5f832a7ded6cc99beae Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 20:39:34 +0200 Subject: [PATCH 07/19] Change [FlutterView start] to synchronousResizing property --- .../darwin/macos/framework/Source/FlutterEngine.mm | 6 +++--- .../darwin/macos/framework/Source/FlutterView.h | 2 +- .../darwin/macos/framework/Source/FlutterView.mm | 10 +++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 0158e591c7820..1c31a30b5632f 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -321,7 +321,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { [self sendUserLocales]; [self updateDisplayConfig]; - [self.viewController.flutterView start]; + self.viewController.flutterView.synchronousResizing = YES; return YES; } @@ -363,7 +363,7 @@ - (void)setViewController:(FlutterViewController*)controller { _resourceContext = nil; } if (_engine) { - [self.viewController.flutterView start]; + self.viewController.flutterView.synchronousResizing = YES; } } @@ -415,7 +415,7 @@ - (void)updateDisplayConfig { CVDisplayLinkRelease(displayLinkRef); } -// Must be driven by FlutterView (i.e. [FlutterView start]) +// Called by [FlutterViewController viewDidReshape] - (void)updateWindowMetrics { if (!_engine) { return; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.h b/shell/platform/darwin/macos/framework/Source/FlutterView.h index 4123d1a5fde30..1e4358e11ce98 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -21,6 +21,7 @@ @interface FlutterView : NSView @property(readwrite, nonatomic, nonnull) NSOpenGLContext* openGLContext; +@property(readwrite, nonatomic) BOOL synchronousResizing; - (nullable instancetype)initWithFrame:(NSRect)frame shareContext:(nonnull NSOpenGLContext*)shareContext @@ -37,7 +38,6 @@ - (nullable instancetype)initWithCoder:(nonnull NSCoder*)coder NS_UNAVAILABLE; - (nonnull instancetype)init NS_UNAVAILABLE; -- (void)start; - (void)present; - (int)getFrameBufferIdForSize:(CGSize)size; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 2eeee2569db04..a5c0403b9bb33 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -13,7 +13,6 @@ @interface FlutterView () { __weak id _reshapeListener; FlutterResizeSynchronizer* resizeSynchronizer; FlutterSurfaceManager* surfaceManager; - BOOL active; CALayer* contentLayer; } @@ -80,18 +79,15 @@ - (void)present { [resizeSynchronizer requestCommit]; } -- (void)start { - active = YES; - [self reshaped]; -} - - (void)reshaped { - if (active) { + if (self.synchronousResizing) { CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; [resizeSynchronizer beginResize:scaledSize notify:^{ [_reshapeListener viewDidReshape:self]; }]; + } else { + [_reshapeListener viewDidReshape:self]; } } From e890a088714b418bae71e8f1e9461dc6158b7fa4 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Thu, 1 Oct 2020 23:18:40 +0200 Subject: [PATCH 08/19] Restore the previously removed updateWindowMetrics call --- shell/platform/darwin/macos/framework/Source/FlutterEngine.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 1c31a30b5632f..0ef5ad9654ef6 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -320,6 +320,7 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { } [self sendUserLocales]; + [self updateWindowMetrics]; [self updateDisplayConfig]; self.viewController.flutterView.synchronousResizing = YES; return YES; @@ -415,7 +416,6 @@ - (void)updateDisplayConfig { CVDisplayLinkRelease(displayLinkRef); } -// Called by [FlutterViewController viewDidReshape] - (void)updateWindowMetrics { if (!_engine) { return; From 9a13f68a4009987a0476355c25a953a252078c10 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 19:33:30 +0200 Subject: [PATCH 09/19] Improve comments and variable names --- .../Source/FlutterResizeSynchronizer.h | 25 ++++++++++- .../Source/FlutterResizeSynchronizer.mm | 43 +++++++++++-------- 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h index ebd53b4678fa4..f9e2717e3322e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h @@ -12,7 +12,29 @@ @end -// Encapsulates the logic for blocking platform thread during window resize +// 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)delegate; @@ -22,6 +44,7 @@ // - requestCommit is called // All requestCommit calls before `shouldEnsureSurfaceForSize` is called with // expected size are ignored; +// The notify block is invoked immediately after syncrhonizer mutex is acquired - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify; // Returns whether the view should ensure surfaces with given size; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm index d6ea1499e96b1..2e1eef3eff389 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm @@ -4,13 +4,18 @@ @interface FlutterResizeSynchronizer () { uint32_t cookie; // counter to detect stale callbacks + std::mutex mutex; - std::condition_variable condNoPendingCommit; - std::condition_variable condPendingCommit; - bool accepting; - bool waiting; - bool pendingCommit; - CGSize newSize; + 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 + // 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 delegate; } @end @@ -19,7 +24,7 @@ @implementation FlutterResizeSynchronizer - (instancetype)initWithDelegate:(id)delegate_ { if (self = [super init]) { - accepting = true; + acceptingCommit = true; delegate = delegate_; } return self; @@ -35,11 +40,11 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { // from now on, ignore all incoming commits until the block below gets // scheduled on raster thread - accepting = false; + acceptingCommit = false; // let pending commits finish to unblock the raster thread pendingCommit = false; - condNoPendingCommit.notify_all(); + condBlockBeginResize.notify_all(); // let the engine send resize notification notify(); @@ -48,36 +53,36 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify { waiting = true; - condPendingCommit.wait(lock, [&] { return pendingCommit; }); + condBlockRequestCommit.wait(lock, [&] { return pendingCommit; }); [delegate resizeSynchronizerFlush:self]; [delegate resizeSynchronizerCommit:self]; pendingCommit = false; - condNoPendingCommit.notify_all(); + condBlockBeginResize.notify_all(); waiting = false; } - (bool)shouldEnsureSurfaceForSize:(CGSize)size { std::unique_lock lock(mutex); - if (!accepting) { + if (!acceptingCommit) { if (CGSizeEqualToSize(newSize, size)) { - accepting = true; + acceptingCommit = true; } } - return accepting; + return acceptingCommit; } - (void)requestCommit { std::unique_lock lock(mutex); - if (!accepting) { + if (!acceptingCommit) { return; } pendingCommit = true; if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call - condPendingCommit.notify_all(); - condNoPendingCommit.wait(lock, [&]() { return !pendingCommit; }); + 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 @@ -89,10 +94,10 @@ - (void)requestCommit { [delegate resizeSynchronizerCommit:self]; } pendingCommit = false; - condNoPendingCommit.notify_all(); + condBlockBeginResize.notify_all(); } }); - condNoPendingCommit.wait(lock, [&]() { return !pendingCommit; }); + condBlockBeginResize.wait(lock, [&]() { return !pendingCommit; }); } } From 53c61017a3ae5ae4f9dfa8ebd482570341436f44 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 19:43:12 +0200 Subject: [PATCH 10/19] Refactor framebuffer creation --- .../framework/Source/FlutterSurfaceManager.mm | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 6f8cd09439e4e..fea285cfc289e 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -30,17 +30,8 @@ - (instancetype)initWithLayer:(CALayer*)layer_ openGLContext:(NSOpenGLContext*)o glGenFramebuffers(2, _frameBufferId); glGenTextures(2, _backingTexture); - glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[0]); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[0]); - glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[1]); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[1]); - glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + [self createFramebuffer:_frameBufferId[0] withBackingTexture:_backingTexture[0]]; + [self createFramebuffer:_frameBufferId[1] withBackingTexture:_backingTexture[1]]; if (prev) { [prev makeCurrentContext]; @@ -51,6 +42,16 @@ - (instancetype)initWithLayer:(CALayer*)layer_ openGLContext:(NSOpenGLContext*)o return self; } +- (void)createFramebuffer:(uint32_t)fbo withBackingTexture:(uint32_t)texture { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); +} + - (void)ensureSurfaceSize:(CGSize)size { if (CGSizeEqualToSize(size, surfaceSize)) { return; From fea653b1f17796f13511645d5b2af21375d8fc72 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 19:43:40 +0200 Subject: [PATCH 11/19] Check framebuffer status --- shell/platform/darwin/macos/BUILD.gn | 1 + .../darwin/macos/framework/Source/FlutterSurfaceManager.mm | 3 +++ 2 files changed, 4 insertions(+) diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 68e7ff5d14e4b..035492b1549ec 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -71,6 +71,7 @@ source_set("flutter_framework_source") { sources += _flutter_framework_headers deps = [ + "//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", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index fea285cfc289e..41ef3a12eae7b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -1,4 +1,5 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" +#import "flutter/fml/logging.h" #include @@ -90,6 +91,8 @@ - (void)ensureSurfaceSize:(CGSize)size { glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[i]); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, _backingTexture[i], 0); + + FML_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); } if (prev) { [prev makeCurrentContext]; From f929f97148b06e71a3dbe9ce91c2af1d92d7df36 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 20:22:09 +0200 Subject: [PATCH 12/19] Move contentLayer to FlutterSurfaceManager FlutterSurfaceManager is in charge of swapping buffers, and thus contentLayer and required flipping for OpenGL texture framebuffer should be implementation detail of FlutterSurfaceManager. --- .../framework/Source/FlutterSurfaceManager.mm | 17 +++++++++++++++-- .../macos/framework/Source/FlutterView.mm | 11 +---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 41ef3a12eae7b..062f0c5a64038 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -11,7 +11,9 @@ @interface FlutterSurfaceManager () { CGSize surfaceSize; - CALayer* layer; + CALayer* layer; // provided (parent layer) + CALayer* contentLayer; + NSOpenGLContext* openGLContext; uint32_t _frameBufferId[kBufferCount]; uint32_t _backingTexture[kBufferCount]; @@ -26,6 +28,11 @@ - (instancetype)initWithLayer:(CALayer*)layer_ openGLContext:(NSOpenGLContext*)o layer = layer_; openGLContext = opengLContext_; + // Layer for content. This is separate from provided layer, because it needs to be flipped + // vertically if we render to OpenGL texture + contentLayer = [[CALayer alloc] init]; + [layer_ addSublayer:contentLayer]; + NSOpenGLContext* prev = [NSOpenGLContext currentContext]; [openGLContext makeCurrentContext]; glGenFramebuffers(2, _frameBufferId); @@ -102,7 +109,13 @@ - (void)ensureSurfaceSize:(CGSize)size { } - (void)swapBuffers { - [layer setContents:(__bridge id)_ioSurface[kBack]]; + contentLayer.frame = layer.bounds; + // OpengL texture is rendered upside down + contentLayer.transform = + CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, -layer.frame.size.height, 0); + + [contentLayer setContents:(__bridge id)_ioSurface[kBack]]; + std::swap(_ioSurface[kBack], _ioSurface[kFront]); std::swap(_frameBufferId[kBack], _frameBufferId[kFront]); std::swap(_backingTexture[kBack], _backingTexture[kFront]); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index a5c0403b9bb33..7820d964fcf5c 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -13,7 +13,6 @@ @interface FlutterView () { __weak id _reshapeListener; FlutterResizeSynchronizer* resizeSynchronizer; FlutterSurfaceManager* surfaceManager; - CALayer* contentLayer; } @end @@ -35,13 +34,8 @@ - (instancetype)initWithFrame:(NSRect)frame [self setWantsLayer:YES]; - // Layer for content (which will be set by surfaceManager). This is separate from - // self.layer because it needs to be flipped vertically (using layer.sublayerTransform) - contentLayer = [[CALayer alloc] init]; - [self.layer addSublayer:contentLayer]; - resizeSynchronizer = [[FlutterResizeSynchronizer alloc] initWithDelegate:self]; - surfaceManager = [[FlutterSurfaceManager alloc] initWithLayer:contentLayer + surfaceManager = [[FlutterSurfaceManager alloc] initWithLayer:self.layer openGLContext:self.openGLContext]; _reshapeListener = reshapeListener; @@ -59,9 +53,6 @@ - (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { [CATransaction begin]; [CATransaction setDisableActions:YES]; self.layer.frame = self.bounds; - self.layer.sublayerTransform = CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, - -self.layer.bounds.size.height, 0); - contentLayer.frame = self.layer.bounds; [surfaceManager swapBuffers]; From c868a0553cdb52813f74737b73d16ff13e1cf3b5 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 20:35:01 +0200 Subject: [PATCH 13/19] Typo --- .../darwin/macos/framework/Source/FlutterResizeSynchronizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h index f9e2717e3322e..fe9a0bde6f1f5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h @@ -44,7 +44,7 @@ // - requestCommit is called // All requestCommit calls before `shouldEnsureSurfaceForSize` is called with // expected size are ignored; -// The notify block is invoked immediately after syncrhonizer mutex is acquired +// 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; From 745eaf62f352324acc26c9ce60d923d43fb4c662 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 2 Oct 2020 20:54:12 +0200 Subject: [PATCH 14/19] Add MacOSSwitchableOpenGLContext --- shell/platform/darwin/macos/BUILD.gn | 3 +++ .../framework/Source/FlutterSurfaceManager.mm | 22 ++++++------------- .../macos/framework/Source/FlutterView.mm | 5 +++-- .../Source/MacOSSwitchableGLContext.h | 21 ++++++++++++++++++ .../Source/MacOSSwitchableGLContext.mm | 19 ++++++++++++++++ 5 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h create mode 100644 shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 035492b1549ec..c844d92413f3c 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -66,11 +66,14 @@ 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", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 062f0c5a64038..7993cf554f95b 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -1,5 +1,6 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" #import "flutter/fml/logging.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h" #include @@ -33,19 +34,14 @@ - (instancetype)initWithLayer:(CALayer*)layer_ openGLContext:(NSOpenGLContext*)o contentLayer = [[CALayer alloc] init]; [layer_ addSublayer:contentLayer]; - NSOpenGLContext* prev = [NSOpenGLContext currentContext]; - [openGLContext makeCurrentContext]; + flutter::GLContextSwitch context_switch( + std::make_unique(opengLContext_)); + glGenFramebuffers(2, _frameBufferId); glGenTextures(2, _backingTexture); [self createFramebuffer:_frameBufferId[0] withBackingTexture:_backingTexture[0]]; [self createFramebuffer:_frameBufferId[1] withBackingTexture:_backingTexture[1]]; - - if (prev) { - [prev makeCurrentContext]; - } else { - [NSOpenGLContext clearCurrentContext]; - } } return self; } @@ -65,8 +61,9 @@ - (void)ensureSurfaceSize:(CGSize)size { return; } surfaceSize = size; - NSOpenGLContext* prev = [NSOpenGLContext currentContext]; - [openGLContext makeCurrentContext]; + + flutter::GLContextSwitch context_switch( + std::make_unique(openGLContext)); for (int i = 0; i < kBufferCount; ++i) { if (_ioSurface[i]) { @@ -101,11 +98,6 @@ - (void)ensureSurfaceSize:(CGSize)size { FML_DCHECK(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); } - if (prev) { - [prev makeCurrentContext]; - } else { - [NSOpenGLContext clearCurrentContext]; - } } - (void)swapBuffers { diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 7820d964fcf5c..9e8ba9d211e26 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -5,6 +5,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h" #import #import @@ -44,9 +45,9 @@ - (instancetype)initWithFrame:(NSRect)frame } - (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer { - [self.openGLContext makeCurrentContext]; + flutter::GLContextSwitch context_switch( + std::make_unique(self.openGLContext)); glFlush(); - [NSOpenGLContext clearCurrentContext]; } - (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { diff --git a/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h b/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h new file mode 100644 index 0000000000000..0ff951fca1e6a --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h @@ -0,0 +1,21 @@ +#include "flutter/flow/gl_context_switch.h" +#include "flutter/fml/memory/thread_checker.h" + +#import + +class MacOSSwitchableGLContext final : public flutter::SwitchableGLContext { + public: + explicit MacOSSwitchableGLContext(NSOpenGLContext* context); + + bool SetCurrent() override; + + bool RemoveCurrent() override; + + private: + NSOpenGLContext* context_; + NSOpenGLContext* previous_context_; + + FML_DECLARE_THREAD_CHECKER(checker); + + FML_DISALLOW_COPY_AND_ASSIGN(MacOSSwitchableGLContext); +}; diff --git a/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.mm b/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.mm new file mode 100644 index 0000000000000..d68d8dfcfc585 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.mm @@ -0,0 +1,19 @@ +#import "flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h" + +MacOSSwitchableGLContext::MacOSSwitchableGLContext(NSOpenGLContext* context) : context_(context) {} + +bool MacOSSwitchableGLContext::SetCurrent() { + FML_DCHECK_CREATION_THREAD_IS_CURRENT(checker); + previous_context_ = [NSOpenGLContext currentContext]; + [context_ makeCurrentContext]; + return true; +} + +bool MacOSSwitchableGLContext::RemoveCurrent() { + if (previous_context_) { + [previous_context_ makeCurrentContext]; + } else { + [NSOpenGLContext clearCurrentContext]; + } + return true; +} From 4748a6bbb63c0a81396794bbe35578d9b4f5b573 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 6 Oct 2020 02:07:21 +0200 Subject: [PATCH 15/19] Use sublayerTransform instead of child layer.transform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For whatever reason setting contentLayer.trasnform doesn’t happen within current CATransaction --- .../darwin/macos/framework/Source/FlutterSurfaceManager.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 7993cf554f95b..d297fc21c0b32 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -103,8 +103,8 @@ - (void)ensureSurfaceSize:(CGSize)size { - (void)swapBuffers { contentLayer.frame = layer.bounds; // OpengL texture is rendered upside down - contentLayer.transform = - CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, -layer.frame.size.height, 0); + layer.sublayerTransform = + CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, -layer.bounds.size.height, 0); [contentLayer setContents:(__bridge id)_ioSurface[kBack]]; From 30d37a05482d23cdd1f664ff33abb2b06cd598fa Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 6 Oct 2020 02:23:31 +0200 Subject: [PATCH 16/19] Simpler vertical flip --- .../darwin/macos/framework/Source/FlutterSurfaceManager.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index d297fc21c0b32..05f89860ac15d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -102,10 +102,8 @@ - (void)ensureSurfaceSize:(CGSize)size { - (void)swapBuffers { contentLayer.frame = layer.bounds; - // OpengL texture is rendered upside down - layer.sublayerTransform = - CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 0, -layer.bounds.size.height, 0); + contentLayer.transform = CATransform3DMakeScale(1, -1, 1); [contentLayer setContents:(__bridge id)_ioSurface[kBack]]; std::swap(_ioSurface[kBack], _ioSurface[kFront]); From 1f5fffce78e3819fd65a25e6c60579288150f234 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 26 Oct 2020 22:47:42 +0100 Subject: [PATCH 17/19] No need to set layer frame it is autoresized with the view --- shell/platform/darwin/macos/framework/Source/FlutterView.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/shell/platform/darwin/macos/framework/Source/FlutterView.mm index 9e8ba9d211e26..6839640b1ebca 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -53,7 +53,6 @@ - (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer { - (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { [CATransaction begin]; [CATransaction setDisableActions:YES]; - self.layer.frame = self.bounds; [surfaceManager swapBuffers]; From ff9f6ca7945d6d6911707ad0ea1e89c8514308f8 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Mon, 26 Oct 2020 22:56:41 +0100 Subject: [PATCH 18/19] Add comment about flipping the layer contents --- .../darwin/macos/framework/Source/FlutterSurfaceManager.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index 05f89860ac15d..2ddb190139670 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -103,6 +103,8 @@ - (void)ensureSurfaceSize:(CGSize)size { - (void)swapBuffers { contentLayer.frame = layer.bounds; + // The surface is an OpenGL texture, which means it has origin in bottom left corner + // and needs to be flipped vertically contentLayer.transform = CATransform3DMakeScale(1, -1, 1); [contentLayer setContents:(__bridge id)_ioSurface[kBack]]; From ed9f1d0087793f68e3b4c15d372c63029aeeda4d Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Tue, 27 Oct 2020 19:00:29 +0100 Subject: [PATCH 19/19] Amend licenses_flutter --- ci/licenses_golden/licenses_flutter | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 46097e6e73e77..c6e5047eaa2bb 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -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 @@ -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