-
Notifications
You must be signed in to change notification settings - Fork 6k
[MacOS] Add support for creating and presenting platform views. #22905
Changes from all commits
c1fbeac
f049b8b
4b85acf
4dbbff9
b8f5837
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h" | ||
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h" | ||
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" | ||
| #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController_Internal.h" | ||
| #import "flutter/shell/platform/embedder/embedder.h" | ||
|
|
||
| /** | ||
|
|
@@ -57,6 +58,16 @@ - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)t | |
| */ | ||
| - (void)loadAOTData:(NSString*)assetsDir; | ||
|
|
||
| /** | ||
| * Creates and returns a FlutterCompositor* to be used by the embedder. | ||
| */ | ||
| - (FlutterCompositor*)createFlutterCompositor; | ||
|
|
||
| /** | ||
| * Create a platform view channel and setup a method call handler. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Creates [...] and sets up [...] |
||
| */ | ||
| - (void)setupPlatformViewChannel; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: setUpPlatformViewChannel. "Setup" is a noun; the verb is "set up". |
||
|
|
||
| @end | ||
|
|
||
| #pragma mark - | ||
|
|
@@ -141,6 +152,13 @@ @implementation FlutterEngine { | |
|
|
||
| // FlutterCompositor is copied and used in embedder.cc. | ||
| FlutterCompositor _compositor; | ||
|
|
||
| // A method channel for platform view functionality. | ||
| FlutterMethodChannel* _platformViewsChannel; | ||
|
|
||
| // Used to support creation and deletion of platform views and | ||
| // registering platform view factories. | ||
| FlutterPlatformViewController* _platformViewController; | ||
| } | ||
|
|
||
| - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { | ||
|
|
@@ -248,6 +266,9 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { | |
| flutterArguments.aot_data = _aotData; | ||
| } | ||
|
|
||
| [self setupPlatformViewChannel]; | ||
| _platformViewController = [[FlutterPlatformViewController alloc] init]; | ||
|
|
||
| flutterArguments.compositor = [self createFlutterCompositor]; | ||
|
|
||
| FlutterRendererConfig rendererConfig = [_renderer createRendererConfig]; | ||
|
|
@@ -428,6 +449,10 @@ - (void)sendPointerEvent:(const FlutterPointerEvent&)event { | |
| _embedderAPI.SendPointerEvent(_engine, &event, 1); | ||
| } | ||
|
|
||
| - (FlutterPlatformViewController*)platformViewController { | ||
| return _platformViewController; | ||
| } | ||
|
|
||
| #pragma mark - Private methods | ||
|
|
||
| - (void)sendUserLocales { | ||
|
|
@@ -504,6 +529,67 @@ - (void)shutDownEngine { | |
| _engine = nullptr; | ||
| } | ||
|
|
||
| - (FlutterCompositor*)createFlutterCompositor { | ||
RichardJCai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // TODO(richardjcai): Add support for creating a FlutterGLCompositor | ||
| // with a nil _viewController for headless engines. | ||
| // https://github.com/flutter/flutter/issues/71606 | ||
| if (_viewController == nullptr) { | ||
| return nullptr; | ||
| } | ||
|
|
||
| [_mainOpenGLContext makeCurrentContext]; | ||
|
|
||
| _macOSGLCompositor = | ||
| std::make_unique<flutter::FlutterGLCompositor>(_viewController, _platformViewController); | ||
|
|
||
| _compositor = {}; | ||
| _compositor.struct_size = sizeof(FlutterCompositor); | ||
| _compositor.user_data = _macOSGLCompositor.get(); | ||
|
|
||
| _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, // | ||
| FlutterBackingStore* backing_store_out, // | ||
| void* user_data // | ||
| ) { | ||
| return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->CreateBackingStore( | ||
| config, backing_store_out); | ||
| }; | ||
|
|
||
| _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, // | ||
| void* user_data // | ||
| ) { | ||
| return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->CollectBackingStore( | ||
| backing_store); | ||
| }; | ||
|
|
||
| _compositor.present_layers_callback = [](const FlutterLayer** layers, // | ||
| size_t layers_count, // | ||
| void* user_data // | ||
| ) { | ||
| return reinterpret_cast<flutter::FlutterGLCompositor*>(user_data)->Present(layers, | ||
| layers_count); | ||
| }; | ||
|
|
||
| __weak FlutterEngine* weakSelf = self; | ||
| _macOSGLCompositor->SetPresentCallback( | ||
| [weakSelf]() { return [weakSelf engineCallbackOnPresent]; }); | ||
|
|
||
| _compositor.avoid_backing_store_cache = true; | ||
|
|
||
| return &_compositor; | ||
| } | ||
|
|
||
| - (void)setupPlatformViewChannel { | ||
| _platformViewsChannel = | ||
| [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views" | ||
| binaryMessenger:self.binaryMessenger | ||
| codec:[FlutterStandardMethodCodec sharedInstance]]; | ||
|
|
||
| __weak FlutterEngine* weakSelf = self; | ||
| [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { | ||
| [[weakSelf platformViewController] handleMethodCall:call result:result]; | ||
| }]; | ||
| } | ||
|
|
||
| #pragma mark - FlutterBinaryMessenger | ||
|
|
||
| - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { | ||
|
|
@@ -610,11 +696,11 @@ - (BOOL)unregisterTextureWithID:(int64_t)textureID { | |
| - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime { | ||
| const auto engine_time = _embedderAPI.GetCurrentTime(); | ||
|
|
||
| __weak FlutterEngine* weak_self = self; | ||
| __weak FlutterEngine* weakSelf = self; | ||
| auto worker = ^{ | ||
| FlutterEngine* strong_self = weak_self; | ||
| if (strong_self && strong_self->_engine) { | ||
| auto result = _embedderAPI.RunTask(strong_self->_engine, &task); | ||
| FlutterEngine* strongSelf = weakSelf; | ||
| if (strongSelf && strongSelf->_engine) { | ||
| auto result = _embedderAPI.RunTask(strongSelf->_engine, &task); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reference to |
||
| if (result != kSuccess) { | ||
| NSLog(@"Could not post a task to the Flutter engine."); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,11 @@ | |
| NSOpenGLContext* opengl_context) | ||
| : open_gl_context_(opengl_context) { | ||
| FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr"; | ||
| FML_CHECK(platform_view_controller != nullptr) | ||
| << "FlutterPlatformViewController* cannot be nullptr"; | ||
|
|
||
| view_controller_ = view_controller; | ||
| platform_view_controller_ = platform_view_controller; | ||
| } | ||
|
|
||
| bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, | ||
|
|
@@ -75,35 +79,21 @@ | |
| } | ||
|
|
||
| bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) { | ||
| [platform_view_controller_ disposePlatformViews]; | ||
| for (size_t i = 0; i < layers_count; ++i) { | ||
| const auto* layer = layers[i]; | ||
| FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store); | ||
| switch (layer->type) { | ||
| case kFlutterLayerContentTypeBackingStore: { | ||
| if (backing_store->open_gl.framebuffer.user_data) { | ||
| FlutterBackingStoreData* backing_store_data = | ||
| FlutterBackingStoreData* flutter_backing_store_data = | ||
| (__bridge FlutterBackingStoreData*)backing_store->open_gl.framebuffer.user_data; | ||
|
|
||
| FlutterIOSurfaceHolder* io_surface_holder = [backing_store_data ioSurfaceHolder]; | ||
| size_t layer_id = [backing_store_data layerId]; | ||
|
|
||
| CALayer* content_layer = ca_layer_map_[layer_id]; | ||
|
|
||
| FML_CHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; | ||
|
|
||
| content_layer.frame = content_layer.superlayer.bounds; | ||
|
|
||
| // The surface is an OpenGL texture, which means it has origin in bottom left corner | ||
| // and needs to be flipped vertically | ||
| content_layer.transform = CATransform3DMakeScale(1, -1, 1); | ||
| IOSurfaceRef io_surface_contents = [io_surface_holder ioSurface]; | ||
| [content_layer setContents:(__bridge id)io_surface_contents]; | ||
| PresentBackingStoreContent(flutter_backing_store_data, i); | ||
| } | ||
| break; | ||
| } | ||
| case kFlutterLayerContentTypePlatformView: | ||
RichardJCai marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Add functionality in follow up PR. | ||
| FML_LOG(WARNING) << "Presenting PlatformViews not yet supported"; | ||
| PresentPlatformView(layer, i); | ||
| break; | ||
| }; | ||
| } | ||
|
|
@@ -113,6 +103,48 @@ | |
| return present_callback_(); | ||
| } | ||
|
|
||
| void FlutterGLCompositor::PresentBackingStoreContent( | ||
| FlutterBackingStoreData* flutter_backing_store_data, | ||
| size_t layer_position) { | ||
| FML_DCHECK([[NSThread currentThread] isMainThread]) | ||
| << "Must be on the main thread to update CALayer contents"; | ||
|
|
||
| FlutterIOSurfaceHolder* io_surface_holder = [flutter_backing_store_data ioSurfaceHolder]; | ||
| size_t layer_id = [flutter_backing_store_data layerId]; | ||
|
|
||
| CALayer* content_layer = ca_layer_map_[layer_id]; | ||
|
|
||
| FML_DCHECK(content_layer) << "Unable to find a content layer with layer id " << layer_id; | ||
|
|
||
| content_layer.frame = content_layer.superlayer.bounds; | ||
| content_layer.zPosition = layer_position; | ||
|
|
||
| // The surface is an OpenGL texture, which means it has origin in bottom left corner | ||
| // and needs to be flipped vertically | ||
| content_layer.transform = CATransform3DMakeScale(1, -1, 1); | ||
| IOSurfaceRef io_surface_contents = [io_surface_holder ioSurface]; | ||
| [content_layer setContents:(__bridge id)io_surface_contents]; | ||
| } | ||
|
|
||
| void FlutterGLCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) { | ||
| FML_DCHECK([[NSThread currentThread] isMainThread]) | ||
| << "Must be on the main thread to handle presenting platform views"; | ||
|
|
||
| FML_DCHECK(platform_view_controller_.platformViews.count(layer->platform_view->identifier)) | ||
| << "Platform view not found for id: " << layer->platform_view->identifier; | ||
|
|
||
| NSView* platform_view = platform_view_controller_.platformViews[layer->platform_view->identifier]; | ||
|
|
||
| CGFloat scale = [[NSScreen mainScreen] backingScaleFactor]; | ||
| platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale, | ||
| layer->size.width / scale, layer->size.height / scale); | ||
| if (platform_view.superview == nil) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: we generally use |
||
| [view_controller_.flutterView addSubview:platform_view]; | ||
| } else { | ||
| platform_view.layer.zPosition = layer_position; | ||
| } | ||
| } | ||
|
|
||
| void FlutterGLCompositor::SetPresentCallback( | ||
| const FlutterGLCompositor::PresentCallback& present_callback) { | ||
| present_callback_ = present_callback; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: s/used by the embedder/provided to the embedder API/. The embedder is the runner (in our case), not the flutter engine.