From 131e179b7ceebaa4e8bab902de870bb1df875f6a Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 7 Jan 2021 10:04:48 -0600 Subject: [PATCH] [macos] Re-land FlutterOpenGLRenderer isolation (flutter/engine#22607) --- .../ci/licenses_golden/licenses_flutter | 3 + .../common/framework/Headers/FlutterTexture.h | 2 +- .../shell/platform/darwin/macos/BUILD.gn | 3 + .../macos/framework/Source/FlutterEngine.mm | 168 +++------------- .../framework/Source/FlutterEngineTest.mm | 6 +- .../framework/Source/FlutterEngine_Internal.h | 22 ++- .../Source/FlutterExternalTextureGL.mm | 2 +- .../framework/Source/FlutterGLCompositor.h | 3 +- .../framework/Source/FlutterGLCompositor.mm | 5 +- .../Source/FlutterGLCompositorUnittests.mm | 2 +- .../framework/Source/FlutterOpenGLRenderer.h | 77 ++++++++ .../framework/Source/FlutterOpenGLRenderer.mm | 184 ++++++++++++++++++ .../Source/FlutterOpenGLRendererTest.mm | 118 +++++++++++ .../macos/framework/Source/FlutterView.h | 13 +- .../macos/framework/Source/FlutterView.mm | 17 +- .../framework/Source/FlutterViewController.mm | 10 +- 16 files changed, 458 insertions(+), 177 deletions(-) create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 6b4331c3e57..cea63127911 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1083,6 +1083,9 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterInter FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIntermediateKeyResponder.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/FlutterOpenGLRenderer.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.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 diff --git a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h index 54ab0623be2..db0b0bdd0c1 100644 --- a/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h +++ b/engine/src/flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h @@ -39,7 +39,7 @@ FLUTTER_EXPORT /** * Registers a `FlutterTexture` for usage in Flutter and returns an id that can be used to reference * that texture when calling into Flutter with channels. Textures must be registered on the - * platform thread. + * platform thread. On success returns the pointer to the registered texture, else returns 0. */ - (int64_t)registerTexture:(NSObject*)texture; /** diff --git a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn index 88a65da11f0..76bf50ce15e 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn +++ b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn @@ -64,6 +64,8 @@ source_set("flutter_framework_source") { "framework/Source/FlutterIntermediateKeyResponder.mm", "framework/Source/FlutterMouseCursorPlugin.h", "framework/Source/FlutterMouseCursorPlugin.mm", + "framework/Source/FlutterOpenGLRenderer.h", + "framework/Source/FlutterOpenGLRenderer.mm", "framework/Source/FlutterResizeSynchronizer.h", "framework/Source/FlutterResizeSynchronizer.mm", "framework/Source/FlutterSurfaceManager.h", @@ -129,6 +131,7 @@ executable("flutter_desktop_darwin_unittests") { sources = [ "framework/Source/FlutterEngineTest.mm", "framework/Source/FlutterGLCompositorUnittests.mm", + "framework/Source/FlutterOpenGLRendererTest.mm", "framework/Source/FlutterViewControllerTest.mm", "framework/Source/FlutterViewControllerTestUtils.h", "framework/Source/FlutterViewControllerTestUtils.mm", diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 4bf9e3f2b18..7c5d26707a8 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -11,6 +11,8 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.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/embedder/embedder.h" /** @@ -37,42 +39,11 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { */ - (void)sendUserLocales; -/** - * Called by the engine to make the context the engine should draw into current. - */ -- (bool)engineCallbackOnMakeCurrent; - -/** - * Called by the engine to clear the context the engine should draw into. - */ -- (bool)engineCallbackOnClearCurrent; - -/** - * Called by the engine when the context's buffers should be swapped. - */ -- (bool)engineCallbackOnPresent; - -/** - * Called by the engine when framebuffer object ID is requested. - */ -- (uint32_t)engineCallbackOnFBO:(const FlutterFrameInfo*)info; - -/** - * Makes the resource context the current context. - */ -- (bool)engineCallbackOnMakeResourceCurrent; - /** * Handles a platform message from the engine. */ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message; -/** - * Forwards texture copy request to the corresponding texture via |textureID|. - */ -- (BOOL)populateTextureWithIdentifier:(int64_t)textureID - openGLTexture:(FlutterOpenGLTexture*)openGLTexture; - /** * Requests that the task be posted back the to the Flutter engine at the target time. The target * time is in the clock used by the Flutter engine. @@ -100,7 +71,6 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { @implementation FlutterEngineRegistrar { NSString* _pluginKey; FlutterEngine* _flutterEngine; - FlutterEngineProcTable _embedderAPI; } - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { @@ -119,7 +89,7 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { } - (id)textures { - return _flutterEngine; + return _flutterEngine.openGLRenderer; } - (NSView*)view { @@ -138,38 +108,10 @@ static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) { // Callbacks provided to the engine. See the called methods for documentation. #pragma mark - Static methods provided to engine configuration -static bool OnMakeCurrent(FlutterEngine* engine) { - return [engine engineCallbackOnMakeCurrent]; -} - -static bool OnClearCurrent(FlutterEngine* engine) { - return [engine engineCallbackOnClearCurrent]; -} - -static bool OnPresent(FlutterEngine* engine) { - return [engine engineCallbackOnPresent]; -} - -static uint32_t OnFBO(FlutterEngine* engine, const FlutterFrameInfo* info) { - return [engine engineCallbackOnFBO:info]; -} - -static bool OnMakeResourceCurrent(FlutterEngine* engine) { - return [engine engineCallbackOnMakeResourceCurrent]; -} - static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngine* engine) { [engine engineCallbackOnPlatformMessage:message]; } -static bool OnAcquireExternalTexture(FlutterEngine* engine, - int64_t texture_identifier, - size_t width, - size_t height, - FlutterOpenGLTexture* open_gl_texture) { - return [engine populateTextureWithIdentifier:texture_identifier openGLTexture:open_gl_texture]; -} - #pragma mark - @implementation FlutterEngine { @@ -179,22 +121,12 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, // The project being run by this engine. FlutterDartProject* _project; - // The context provided to the Flutter engine for resource loading. - NSOpenGLContext* _resourceContext; - - // The context that is owned by the currently displayed FlutterView. This is stashed in the engine - // so that the view doesn't need to be accessed from a background thread. - NSOpenGLContext* _mainOpenGLContext; - // A mapping of channel names to the registered handlers for those channels. NSMutableDictionary* _messageHandlers; // Whether the engine can continue running after the view controller is removed. BOOL _allowHeadlessExecution; - // A mapping of textureID to internal FlutterExternalTextureGL adapter. - NSMutableDictionary* _textures; - // Pointer to the Dart AOT snapshot and instruction data. _FlutterEngineAOTData* _aotData; @@ -218,10 +150,10 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, _project = project ?: [[FlutterDartProject alloc] init]; _messageHandlers = [[NSMutableDictionary alloc] init]; - _textures = [[NSMutableDictionary alloc] init]; _allowHeadlessExecution = allowHeadlessExecution; _embedderAPI.struct_size = sizeof(FlutterEngineProcTable); FlutterEngineGetProcAddresses(&_embedderAPI); + _openGLRenderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:self]; NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self @@ -249,17 +181,7 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, return NO; } - const FlutterRendererConfig rendererConfig = { - .type = kOpenGL, - .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), - .open_gl.make_current = (BoolCallback)OnMakeCurrent, - .open_gl.clear_current = (BoolCallback)OnClearCurrent, - .open_gl.present = (BoolCallback)OnPresent, - .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, - }; + const FlutterRendererConfig rendererConfig = [_openGLRenderer createRendererConfig]; // TODO(stuartmorgan): Move internal channel registration from FlutterViewController to here. @@ -362,10 +284,10 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, - (void)setViewController:(FlutterViewController*)controller { _viewController = controller; - _mainOpenGLContext = controller.flutterView.openGLContext; + [_openGLRenderer setFlutterView:controller.flutterView]; + if (!controller && !_allowHeadlessExecution) { [self shutDownEngine]; - _resourceContext = nil; } } @@ -377,9 +299,10 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, return nullptr; } - [_mainOpenGLContext makeCurrentContext]; + [_openGLRenderer.openGLContext makeCurrentContext]; - _macOSGLCompositor = std::make_unique(_viewController); + _macOSGLCompositor = std::make_unique( + _viewController, _openGLRenderer.openGLContext); _compositor = {}; _compositor.struct_size = sizeof(FlutterCompositor); @@ -410,7 +333,7 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, __weak FlutterEngine* weak_self = self; _macOSGLCompositor->SetPresentCallback( - [weak_self]() { return [weak_self engineCallbackOnPresent]; }); + [weak_self]() { return [weak_self.openGLRenderer glPresent]; }); _compositor.avoid_backing_store_cache = true; @@ -429,17 +352,6 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, return _engine != nullptr; } -- (NSOpenGLContext*)resourceContext { - if (!_resourceContext) { - NSOpenGLPixelFormatAttribute attributes[] = { - NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, 0, - }; - NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; - _resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; - } - return _resourceContext; -} - - (void)updateDisplayConfig { if (!_engine) { return; @@ -518,37 +430,6 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size()); } -- (bool)engineCallbackOnMakeCurrent { - if (!_mainOpenGLContext) { - return false; - } - [_mainOpenGLContext makeCurrentContext]; - return true; -} - -- (uint32_t)engineCallbackOnFBO:(const FlutterFrameInfo*)info { - CGSize size = CGSizeMake(info->size.width, info->size.height); - return [_viewController.flutterView frameBufferIDForSize:size]; -} - -- (bool)engineCallbackOnClearCurrent { - [NSOpenGLContext clearCurrentContext]; - return true; -} - -- (bool)engineCallbackOnPresent { - if (!_mainOpenGLContext) { - return false; - } - [self.viewController.flutterView present]; - return true; -} - -- (bool)engineCallbackOnMakeResourceCurrent { - [self.resourceContext makeCurrentContext]; - return true; -} - - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message length:message->message_size @@ -677,27 +558,28 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, #pragma mark - FlutterTextureRegistrar -- (BOOL)populateTextureWithIdentifier:(int64_t)textureID - openGLTexture:(FlutterOpenGLTexture*)openGLTexture { - return [_textures[@(textureID)] populateTexture:openGLTexture]; +- (int64_t)registerTexture:(id)texture { + return [_openGLRenderer registerTexture:texture]; } -- (int64_t)registerTexture:(id)texture { - FlutterExternalTextureGL* FlutterTexture = - [[FlutterExternalTextureGL alloc] initWithFlutterTexture:texture]; - int64_t textureID = [FlutterTexture textureID]; - _embedderAPI.RegisterExternalTexture(_engine, textureID); - _textures[@(textureID)] = FlutterTexture; - return textureID; +- (BOOL)registerTextureWithID:(int64_t)textureId { + return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess; } - (void)textureFrameAvailable:(int64_t)textureID { - _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID); + [_openGLRenderer textureFrameAvailable:textureID]; +} + +- (BOOL)markTextureFrameAvailable:(int64_t)textureID { + return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess; } - (void)unregisterTexture:(int64_t)textureID { - _embedderAPI.UnregisterExternalTexture(_engine, textureID); - [_textures removeObjectForKey:@(textureID)]; + [_openGLRenderer unregisterTexture:textureID]; +} + +- (BOOL)unregisterTextureWithID:(int64_t)textureID { + return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess; } #pragma mark - Task runner integration diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm index a3f602a1843..b011f55972e 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import + #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" @@ -12,7 +14,7 @@ namespace flutter::testing { namespace { -// Returns an engine configured for the text fixture resource configuration. +// Returns an engine configured for the test fixture resource configuration. FlutterEngine* CreateTestEngine() { NSString* fixtures = @(testing::GetFixturesPath()); FlutterDartProject* project = [[FlutterDartProject alloc] @@ -50,4 +52,4 @@ TEST(FlutterEngine, MessengerSend) { [engine shutDownEngine]; } -} // flutter::testing +} // namespace flutter::testing diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h index 48a27da52c5..78a73364685 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h @@ -6,6 +6,7 @@ #import +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h" #include "flutter/shell/platform/embedder/embedder.h" @interface FlutterEngine () @@ -16,10 +17,10 @@ @property(nonatomic, readonly) BOOL running; /** - * The resource context used by the engine for texture uploads. FlutterViews associated with this - * engine should be created to share with this context. + * Provides the renderer config needed to initialize the engine and also handles external texture + * management. */ -@property(nonatomic, readonly, nullable) NSOpenGLContext* resourceContext; +@property(nonatomic, readonly, nonnull) FlutterOpenGLRenderer* openGLRenderer; /** * Function pointers for interacting with the embedder.h API. @@ -36,4 +37,19 @@ */ - (void)sendPointerEvent:(const FlutterPointerEvent&)event; +/** + * Registers an external texture with the given id. Returns YES on success. + */ +- (BOOL)registerTextureWithID:(int64_t)textureId; + +/** + * Marks texture with the given id as available. Returns YES on success. + */ +- (BOOL)markTextureFrameAvailable:(int64_t)textureID; + +/** + * Unregisters an external texture with the given id. Returns YES on success. + */ +- (BOOL)unregisterTextureWithID:(int64_t)textureID; + @end diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm index 0314280e9f1..6a2a82031c1 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm @@ -32,7 +32,7 @@ static void OnCVOpenGLTextureRelease(CVOpenGLTextureRef cvOpenGLTexture) { } - (int64_t)textureID { - return reinterpret_cast(self); + return reinterpret_cast(_texture); } - (BOOL)populateTexture:(FlutterOpenGLTexture*)openGLTexture { diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index e33dd6bb1c7..8f0be73999a 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -18,7 +18,8 @@ namespace flutter { // FlutterGLCompositor is created and destroyed by FlutterEngine. class FlutterGLCompositor { public: - FlutterGLCompositor(FlutterViewController* view_controller); + FlutterGLCompositor(FlutterViewController* view_controller, + NSOpenGLContext* opengl_context); // Creates a BackingStore and saves updates the backing_store_out // data with the new BackingStore data. diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index 7a07212847b..51b1674ad9f 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -20,8 +20,9 @@ namespace flutter { -FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller) - : open_gl_context_(view_controller.flutterView.openGLContext) { +FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller, + NSOpenGLContext* opengl_context) + : open_gl_context_(opengl_context) { FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr"; view_controller_ = view_controller; } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm index 7db8d870e05..25fc3557517 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm @@ -14,7 +14,7 @@ TEST(FlutterGLCompositorTest, TestPresent) { id mockViewController = CreateMockViewController(nil); std::unique_ptr macos_compositor = - std::make_unique(mockViewController); + std::make_unique(mockViewController, nullptr); bool flag = false; macos_compositor->SetPresentCallback([f = &flag]() { diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h new file mode 100644 index 00000000000..33984752d9e --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h @@ -0,0 +1,77 @@ +// 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 + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#import "flutter/shell/platform/embedder/embedder.h" + +/** + * Provides the renderer config needed to initialize the embedder engine and also handles external + * texture management. This is initialized during FlutterEngine creation and then attached to the + * FlutterView once the FlutterViewController is initializer. + */ +@interface FlutterOpenGLRenderer : NSObject + +/** + * The resource context used by the engine for texture uploads. FlutterViews associated with this + * engine should be created to share with this context. + */ +@property(nonatomic, readonly, nullable) NSOpenGLContext* resourceContext; + +/** + * The main OpenGL which will be used for rendering contents to the FlutterView. + */ +@property(readwrite, nonatomic, nonnull) NSOpenGLContext* openGLContext; + +/** + * Intializes the renderer with the given FlutterEngine. + */ +- (nullable instancetype)initWithFlutterEngine:(nonnull FlutterEngine*)flutterEngine; + +/** + * Sets the FlutterView and the corresponding rendering OpenGL context. Unsets the resource context + * if the view is nil. + */ +- (void)setFlutterView:(nullable FlutterView*)view; + +/** + * Called by the engine to make the context the engine should draw into current. + */ +- (BOOL)makeCurrent; + +/** + * Called by the engine to clear the context the engine should draw into. + */ +- (BOOL)clearCurrent; + +/** + * Called by the engine when the context's buffers should be swapped. + */ +- (BOOL)glPresent; + +/** + * Called by the engine when framebuffer object ID is requested. + */ +- (uint32_t)fboForFrameInfo:(nonnull const FlutterFrameInfo*)info; + +/** + * Makes the resource context the current context. + */ +- (BOOL)makeResourceCurrent; + +/** + * Populates the texture registry with the provided openGLTexture. + */ +- (BOOL)populateTextureWithIdentifier:(int64_t)textureID + openGLTexture:(nonnull FlutterOpenGLTexture*)openGLTexture; + +/** + * Creates a FlutterRendererConfig that renders using OpenGL context(s) held + * by this class. + */ +- (FlutterRendererConfig)createRendererConfig; + +@end diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm new file mode 100644 index 00000000000..2cd20902a0a --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.mm @@ -0,0 +1,184 @@ +// 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/FlutterOpenGLRenderer.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#include "flutter/shell/platform/embedder/embedder.h" + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h" + +#pragma mark - Static methods for openGL callbacks that require the engine. + +static bool OnMakeCurrent(FlutterEngine* engine) { + return [engine.openGLRenderer makeCurrent]; +} + +static bool OnClearCurrent(FlutterEngine* engine) { + return [engine.openGLRenderer clearCurrent]; +} + +static bool OnPresent(FlutterEngine* engine) { + return [engine.openGLRenderer glPresent]; +} + +static uint32_t OnFBO(FlutterEngine* engine, const FlutterFrameInfo* info) { + return [engine.openGLRenderer fboForFrameInfo:info]; +} + +static bool OnMakeResourceCurrent(FlutterEngine* engine) { + return [engine.openGLRenderer makeResourceCurrent]; +} + +static bool OnAcquireExternalTexture(FlutterEngine* engine, + int64_t textureIdentifier, + size_t width, + size_t height, + FlutterOpenGLTexture* openGlTexture) { + return [engine.openGLRenderer populateTextureWithIdentifier:textureIdentifier + openGLTexture:openGlTexture]; +} + +#pragma mark - FlutterOpenGLRenderer implementation. + +@implementation FlutterOpenGLRenderer { + FlutterView* _flutterView; + + // The context provided to the Flutter engine for rendering to the FlutterView. This is lazily + // created during initialization of the FlutterView. This is used to render content into the + // FlutterView. + NSOpenGLContext* _openGLContext; + + // The context provided to the Flutter engine for resource loading. + NSOpenGLContext* _resourceContext; + + // A mapping of textureID to internal FlutterExternalTextureGL adapter. + NSMutableDictionary* _textures; + + FlutterEngine* _flutterEngine; +} + +- (instancetype)initWithFlutterEngine:(FlutterEngine*)flutterEngine { + self = [super init]; + if (self) { + _flutterEngine = flutterEngine; + _textures = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)setFlutterView:(FlutterView*)view { + _flutterView = view; + if (!view) { + _resourceContext = nil; + } +} + +- (BOOL)makeCurrent { + if (!_openGLContext) { + return false; + } + [_openGLContext makeCurrentContext]; + return true; +} + +- (BOOL)clearCurrent { + [NSOpenGLContext clearCurrentContext]; + return true; +} + +- (BOOL)glPresent { + if (!_openGLContext) { + return false; + } + [_flutterView present]; + return true; +} + +- (uint32_t)fboForFrameInfo:(const FlutterFrameInfo*)info { + CGSize size = CGSizeMake(info->size.width, info->size.height); + return [_flutterView frameBufferIDForSize:size]; +} + +- (NSOpenGLContext*)resourceContext { + if (!_resourceContext) { + NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, 0, + }; + NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + _resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + } + return _resourceContext; +} + +- (NSOpenGLContext*)openGLContext { + if (!_openGLContext) { + NSOpenGLContext* shareContext = [self resourceContext]; + _openGLContext = [[NSOpenGLContext alloc] initWithFormat:shareContext.pixelFormat + shareContext:shareContext]; + } + return _openGLContext; +} + +- (BOOL)makeResourceCurrent { + [self.resourceContext makeCurrentContext]; + return YES; +} + +#pragma mark - FlutterTextureRegistrar + +- (BOOL)populateTextureWithIdentifier:(int64_t)textureID + openGLTexture:(FlutterOpenGLTexture*)openGLTexture { + return [_textures[@(textureID)] populateTexture:openGLTexture]; +} + +- (int64_t)registerTexture:(id)texture { + FlutterExternalTextureGL* externalTexture = + [[FlutterExternalTextureGL alloc] initWithFlutterTexture:texture]; + int64_t textureID = [externalTexture textureID]; + BOOL success = [_flutterEngine registerTextureWithID:textureID]; + if (success) { + _textures[@(textureID)] = externalTexture; + return textureID; + } else { + NSLog(@"Unable to register the texture with id: %lld.", textureID); + return 0; + } +} + +- (void)textureFrameAvailable:(int64_t)textureID { + BOOL success = [_flutterEngine markTextureFrameAvailable:textureID]; + if (success) { + NSLog(@"Unable to mark texture with id %lld as available.", textureID); + } +} + +- (void)unregisterTexture:(int64_t)textureID { + bool success = [_flutterEngine unregisterTextureWithID:textureID]; + if (success) { + [_textures removeObjectForKey:@(textureID)]; + } else { + NSLog(@"Unable to unregister texture with id: %lld.", textureID); + } +} + +#pragma mark - Private methods + +- (FlutterRendererConfig)createRendererConfig { + const FlutterRendererConfig rendererConfig = { + .type = kOpenGL, + .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), + .open_gl.make_current = reinterpret_cast(OnMakeCurrent), + .open_gl.clear_current = reinterpret_cast(OnClearCurrent), + .open_gl.present = reinterpret_cast(OnPresent), + .open_gl.fbo_with_frame_info_callback = reinterpret_cast(OnFBO), + .open_gl.fbo_reset_after_present = true, + .open_gl.make_resource_current = reinterpret_cast(OnMakeResourceCurrent), + .open_gl.gl_external_texture_frame_callback = + reinterpret_cast(OnAcquireExternalTexture), + }; + return rendererConfig; +} + +@end diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm new file mode 100644 index 00000000000..15a2ba34194 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRendererTest.mm @@ -0,0 +1,118 @@ +// 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 + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterOpenGLRenderer.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/testing/testing.h" + +namespace flutter::testing { + +namespace { +// Returns an engine configured for the test fixture resource configuration. +FlutterEngine* CreateTestEngine() { + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + return [[FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true]; +} +} // namespace + +TEST(FlutterOpenGLRenderer, RegisterExternalTexture) { + FlutterEngine* engine = CreateTestEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + + id flutterTexture = OCMProtocolMock(@protocol(FlutterTexture)); + bool called = false; + + engine.embedderAPI.RegisterExternalTexture = + MOCK_ENGINE_PROC(RegisterExternalTexture, [&](auto engine, int64_t textureIdentifier) { + called = true; + EXPECT_EQ(textureIdentifier, reinterpret_cast(flutterTexture)); + return kSuccess; + }); + + [engine.openGLRenderer registerTexture:flutterTexture]; + EXPECT_TRUE(called); + + [engine shutDownEngine]; +} + +TEST(FlutterOpenGLRenderer, UnregisterExternalTexture) { + FlutterEngine* engine = CreateTestEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + + id flutterTexture = OCMProtocolMock(@protocol(FlutterTexture)); + bool called = false; + + int64_t registeredTextureId = [engine.openGLRenderer registerTexture:flutterTexture]; + engine.embedderAPI.UnregisterExternalTexture = + MOCK_ENGINE_PROC(UnregisterExternalTexture, [&](auto engine, int64_t textureIdentifier) { + called = true; + EXPECT_EQ(textureIdentifier, registeredTextureId); + return kSuccess; + }); + + [engine.openGLRenderer unregisterTexture:registeredTextureId]; + EXPECT_TRUE(called); + + [engine shutDownEngine]; +} + +TEST(FlutterOpenGLRenderer, MarkExternalTextureFrameAvailable) { + FlutterEngine* engine = CreateTestEngine(); + EXPECT_TRUE([engine runWithEntrypoint:@"main"]); + + id flutterTexture = OCMProtocolMock(@protocol(FlutterTexture)); + bool called = false; + + int64_t registeredTextureId = [engine.openGLRenderer registerTexture:flutterTexture]; + engine.embedderAPI.MarkExternalTextureFrameAvailable = MOCK_ENGINE_PROC( + MarkExternalTextureFrameAvailable, [&](auto engine, int64_t textureIdentifier) { + called = true; + EXPECT_EQ(textureIdentifier, registeredTextureId); + return kSuccess; + }); + + [engine.openGLRenderer textureFrameAvailable:registeredTextureId]; + EXPECT_TRUE(called); + + [engine shutDownEngine]; +} + +TEST(FlutterOpenGLRenderer, PresetDelegatesToFlutterView) { + FlutterEngine* engine = CreateTestEngine(); + FlutterOpenGLRenderer* renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:engine]; + id mockFlutterView = OCMClassMock([FlutterView class]); + [[mockFlutterView expect] present]; + [renderer setFlutterView:mockFlutterView]; + [renderer openGLContext]; + [renderer glPresent]; +} + +TEST(FlutterOpenGLRenderer, FBOReturnedByFlutterView) { + FlutterEngine* engine = CreateTestEngine(); + FlutterOpenGLRenderer* renderer = [[FlutterOpenGLRenderer alloc] initWithFlutterEngine:engine]; + id mockFlutterView = OCMClassMock([FlutterView class]); + FlutterFrameInfo frameInfo; + frameInfo.struct_size = sizeof(FlutterFrameInfo); + FlutterUIntSize dimensions; + dimensions.width = 100; + dimensions.height = 200; + frameInfo.size = dimensions; + CGSize size = CGSizeMake(dimensions.width, dimensions.height); + [[mockFlutterView expect] frameBufferIDForSize:size]; + [renderer setFlutterView:mockFlutterView]; + [renderer openGLContext]; + [renderer fboForFrameInfo:&frameInfo]; +} + +} // namespace flutter::testing diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h index cd96f58a229..611c57b11d8 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h @@ -20,19 +20,14 @@ */ @interface FlutterView : NSView -/** - * The OpenGL context of backing surface. - */ -@property(readwrite, nonatomic, nonnull) NSOpenGLContext* openGLContext; - - (nullable instancetype)initWithFrame:(NSRect)frame - shareContext:(nonnull NSOpenGLContext*)shareContext + mainContext:(nonnull NSOpenGLContext*)mainContext reshapeListener:(nonnull id)reshapeListener NS_DESIGNATED_INITIALIZER; -- (nullable instancetype)initWithShareContext:(nonnull NSOpenGLContext*)shareContext - reshapeListener: - (nonnull id)reshapeListener; +- (nullable instancetype)initWithMainContext:(nonnull NSOpenGLContext*)mainContext + reshapeListener: + (nonnull id)reshapeListener; - (nullable instancetype)initWithFrame:(NSRect)frameRect pixelFormat:(nullable NSOpenGLPixelFormat*)format NS_UNAVAILABLE; diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm index d6ce889f54e..978e09a2f19 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm @@ -15,30 +15,29 @@ __weak id _reshapeListener; FlutterResizeSynchronizer* _resizeSynchronizer; FlutterSurfaceManager* _surfaceManager; + NSOpenGLContext* _openGLContext; } @end @implementation FlutterView -- (instancetype)initWithShareContext:(NSOpenGLContext*)shareContext - reshapeListener:(id)reshapeListener { - return [self initWithFrame:NSZeroRect shareContext:shareContext reshapeListener:reshapeListener]; +- (instancetype)initWithMainContext:(NSOpenGLContext*)mainContext + reshapeListener:(id)reshapeListener { + return [self initWithFrame:NSZeroRect mainContext:mainContext reshapeListener:reshapeListener]; } - (instancetype)initWithFrame:(NSRect)frame - shareContext:(NSOpenGLContext*)shareContext + mainContext:(NSOpenGLContext*)mainContext reshapeListener:(id)reshapeListener { self = [super initWithFrame:frame]; if (self) { - self.openGLContext = [[NSOpenGLContext alloc] initWithFormat:shareContext.pixelFormat - shareContext:shareContext]; - + _openGLContext = mainContext; [self setWantsLayer:YES]; _resizeSynchronizer = [[FlutterResizeSynchronizer alloc] initWithDelegate:self]; _surfaceManager = [[FlutterSurfaceManager alloc] initWithLayer:self.layer - openGLContext:self.openGLContext]; + openGLContext:_openGLContext]; _reshapeListener = reshapeListener; } @@ -46,7 +45,7 @@ } - (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer { - MacOSGLContextSwitch context_switch(self.openGLContext); + MacOSGLContextSwitch context_switch(_openGLContext); glFlush(); } diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 6944041a1bf..ce80d345596 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -275,13 +275,13 @@ static void CommonInit(FlutterViewController* controller) { } - (void)loadView { - NSOpenGLContext* resourceContext = _engine.resourceContext; - if (!resourceContext) { - NSLog(@"Unable to create FlutterView; no resource context available."); + NSOpenGLContext* mainContext = _engine.openGLRenderer.openGLContext; + if (!mainContext) { + NSLog(@"Unable to create FlutterView; no GL context available."); return; } - FlutterView* flutterView = [[FlutterView alloc] initWithShareContext:resourceContext - reshapeListener:self]; + FlutterView* flutterView = [[FlutterView alloc] initWithMainContext:mainContext + reshapeListener:self]; self.view = flutterView; }