From 7c09d8aec6fa160c095cc0440adef74e3c1ffdbf Mon Sep 17 00:00:00 2001 From: RichardJCai Date: Tue, 27 Oct 2020 13:59:23 -0400 Subject: [PATCH 1/2] Create FlutterGLCompositor. Refactor rendering one layer to use the FlutterGLCompositor. In this commit, FlutterGLCompositor only supports rendering one view and does not support rendering platform views. --- ci/licenses_golden/licenses_flutter | 7 ++ shell/platform/darwin/macos/BUILD.gn | 10 +++ .../Source/FlutterBackingStoreData.h | 17 ++++ .../Source/FlutterBackingStoreData.mm | 27 ++++++ .../macos/framework/Source/FlutterEngine.mm | 50 ++++++++++- .../framework/Source/FlutterGLCompositor.h | 56 ++++++++++++ .../framework/Source/FlutterGLCompositor.mm | 88 +++++++++++++++++++ .../Source/FlutterGLCompositorUnittests.mm | 29 ++++++ .../Source/FlutterViewControllerTest.mm | 26 +----- .../Source/FlutterViewControllerTestUtils.h | 18 ++++ .../Source/FlutterViewControllerTestUtils.mm | 30 +++++++ 11 files changed, 334 insertions(+), 24 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index a474d22e5a416..e9d2fe4e5915e 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1052,6 +1052,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPlug FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Info.plist FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -1061,6 +1063,9 @@ 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/FlutterFrameBufferProvider.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h @@ -1077,6 +1082,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView. FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm 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/FlutterViewControllerTestUtils.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index 8a7f69477aed5..79996675a9965 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -46,6 +46,8 @@ source_set("flutter_framework_source") { sources = [ "framework/Source/FlutterAppDelegate.mm", + "framework/Source/FlutterBackingStoreData.h", + "framework/Source/FlutterBackingStoreData.mm", "framework/Source/FlutterDartProject.mm", "framework/Source/FlutterDartProject_Internal.h", "framework/Source/FlutterEngine.mm", @@ -54,6 +56,8 @@ source_set("flutter_framework_source") { "framework/Source/FlutterExternalTextureGL.mm", "framework/Source/FlutterFrameBufferProvider.h", "framework/Source/FlutterFrameBufferProvider.mm", + "framework/Source/FlutterGLCompositor.h", + "framework/Source/FlutterGLCompositor.mm", "framework/Source/FlutterIOSurfaceHolder.h", "framework/Source/FlutterIOSurfaceHolder.mm", "framework/Source/FlutterMouseCursorPlugin.h", @@ -77,9 +81,12 @@ source_set("flutter_framework_source") { sources += _flutter_framework_headers deps = [ + "//flutter/flow:flow", + "//flutter/fml", "//flutter/shell/platform/common/cpp:common_cpp_switches", "//flutter/shell/platform/darwin/common:framework_shared", "//flutter/shell/platform/embedder:embedder_as_internal_library", + "//third_party/skia", ] public_configs = [ "//flutter:config" ] @@ -119,7 +126,10 @@ executable("flutter_desktop_darwin_unittests") { sources = [ "framework/Source/FlutterEngineTest.mm", + "framework/Source/FlutterGLCompositorUnittests.mm", "framework/Source/FlutterViewControllerTest.mm", + "framework/Source/FlutterViewControllerTestUtils.h", + "framework/Source/FlutterViewControllerTestUtils.mm", ] cflags_objcc = [ "-fobjc-arc" ] diff --git a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h new file mode 100644 index 0000000000000..753895e1be395 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h @@ -0,0 +1,17 @@ +// 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 + +/** + * FlutterBackingStoreData holds data to be stored in the + * BackingStore's user_data. + */ +@interface FlutterBackingStoreData : NSObject + +- (nullable instancetype)initWithIsRootView:(bool)isRootView; + +- (bool)isRootView; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm new file mode 100644 index 0000000000000..342b9fc079e66 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm @@ -0,0 +1,27 @@ +// 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/FlutterBackingStoreData.h" + +#include + +@interface FlutterBackingStoreData () { + bool _isRootView; +} +@end + +@implementation FlutterBackingStoreData + +- (nullable instancetype)initWithIsRootView:(bool)isRootView { + if (self = [super init]) { + _isRootView = isRootView; + } + return self; +} + +- (bool)isRootView { + return _isRootView; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index d9ebd55f0d7ec..6e50f5d6b4504 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -10,7 +10,7 @@ #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/FlutterViewController_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h" #import "flutter/shell/platform/embedder/embedder.h" /** @@ -197,6 +197,13 @@ @implementation FlutterEngine { // Pointer to the Dart AOT snapshot and instruction data. _FlutterEngineAOTData* _aotData; + + // _macOSGLCompositor is created when the engine is created and + // it's destruction is handled by ARC when the engine is destroyed. + std::unique_ptr _macOSGLCompositor; + + // FlutterCompositor is copied and used in embedder.cc. + FlutterCompositor _compositor; } - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { @@ -306,6 +313,8 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { flutterArguments.aot_data = _aotData; } + flutterArguments.compositor = [self createFlutterCompositor]; + FlutterEngineResult result = _embedderAPI.Initialize( FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine); if (result != kSuccess) { @@ -360,6 +369,45 @@ - (void)setViewController:(FlutterViewController*)controller { } } +- (FlutterCompositor*)createFlutterCompositor { + [_mainOpenGLContext makeCurrentContext]; + + _macOSGLCompositor = std::make_unique(_viewController); + + _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(user_data)->CreateBackingStore( + config, backing_store_out); + }; + + _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, // + void* user_data // + ) { + return reinterpret_cast(user_data)->CollectBackingStore( + backing_store); + }; + + _compositor.present_layers_callback = [](const FlutterLayer** layers, // + size_t layers_count, // + void* user_data // + ) { + return reinterpret_cast(user_data)->Present(layers, + layers_count); + }; + + __weak FlutterEngine* weak_self = self; + _macOSGLCompositor->SetPresentCallback( + [weak_self]() { return [weak_self engineCallbackOnPresent]; }); + + return &_compositor; +} + - (id)binaryMessenger { // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins // keeping the engine alive. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h new file mode 100644 index 0000000000000..19293facd40fd --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -0,0 +1,56 @@ +// 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. + +#include + +#include "flutter/fml/macros.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" +#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#include "flutter/shell/platform/embedder/embedder.h" +#include "third_party/skia/include/gpu/GrDirectContext.h" + +namespace flutter { + +// FlutterGLCompositor creates and manages the backing stores used for +// rendering Flutter content and presents Flutter content and Platform views. +// Platform views are not yet supported. +// FlutterGLCompositor is created and destroyed by FlutterEngine. +class FlutterGLCompositor { + public: + FlutterGLCompositor(FlutterViewController* view_controller); + + // Creates a BackingStore and saves updates the backing_store_out + // data with the new BackingStore data. + // If the view requesting the backing store is the root view, + // we do not create a new backing store but rather return the + // backing store associated with the root view's FlutterSurfaceManager. + // + // Any additional state allocated for the backing store and + // saved as user_data in the backing store must be collected + // in the backing_store's desctruction_callback field which will + // be called when the embedder collects the backing store. + bool CreateBackingStore(const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out); + + // Releases the memory for any state used by the backing store. + bool CollectBackingStore(const FlutterBackingStore* backing_store); + + // Presents the FlutterLayers by updating FlutterView(s) using the + // layer content. + bool Present(const FlutterLayer** layers, size_t layers_count); + + using PresentCallback = std::function; + + // PresentCallback is called at the end of the Present function. + void SetPresentCallback(const PresentCallback& present_callback); + + private: + FlutterViewController* view_controller_; + PresentCallback present_callback_; + NSOpenGLContext* open_gl_context_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterGLCompositor); +}; + +} // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm new file mode 100644 index 0000000000000..a2fb9faa65607 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -0,0 +1,88 @@ +// 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/FlutterGLCompositor.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" + +#import +#import "flutter/fml/logging.h" +#import "flutter/fml/platform/darwin/cf_utils.h" +#import "third_party/skia/include/core/SkCanvas.h" +#import "third_party/skia/include/core/SkSurface.h" +#import "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h" +#import "third_party/skia/include/utils/mac/SkCGUtils.h" + +#include + +namespace flutter { + +FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller) + : view_controller_(view_controller), + open_gl_context_(view_controller.flutterView.openGLContext) {} + +bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, + FlutterBackingStore* backing_store_out) { + CGSize size = CGSizeMake(config->size.width, config->size.height); + FlutterBackingStoreData* data = + [[FlutterBackingStoreData alloc] initWithIsRootView:config->is_root_view]; + + backing_store_out->type = kFlutterBackingStoreTypeOpenGL; + backing_store_out->is_cacheable = true; + backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + backing_store_out->open_gl.framebuffer.target = GL_RGBA8; + + if (config->is_root_view) { + // The root view uses FlutterSurfaceManager and is not cacheable since + // the fbo id changes on every present. + backing_store_out->is_cacheable = false; + auto fbo = [view_controller_.flutterView frameBufferIDForSize:size]; + backing_store_out->open_gl.framebuffer.name = fbo; + } else { + FML_CHECK(false) << "Compositor only supports creating a backing store for the root view"; + } + + backing_store_out->open_gl.framebuffer.user_data = (__bridge_retained void*)data; + backing_store_out->open_gl.framebuffer.destruction_callback = [](void* user_data) { + if (user_data != nullptr) { + CFRelease(user_data); + } + }; + + return true; +} + +bool FlutterGLCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) { + return true; +} + +bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) { + for (size_t i = 0; i < layers_count; ++i) { + const auto* layer = layers[i]; + FlutterBackingStore* backing_store = const_cast(layer->backing_store); + switch (layer->type) { + case kFlutterLayerContentTypeBackingStore: { + FlutterBackingStoreData* data = + (__bridge FlutterBackingStoreData*)(backing_store->open_gl.framebuffer.user_data); + if (![data isRootView]) { + FML_CHECK(false) << "Compositor only supports presenting the root view."; + } + break; + } + case kFlutterLayerContentTypePlatformView: + // Add functionality in follow up PR. + FML_CHECK(false) << "Presenting PlatformViews not yet supported"; + break; + }; + } + return present_callback_(); +} + +void FlutterGLCompositor::SetPresentCallback( + const FlutterGLCompositor::PresentCallback& present_callback) { + present_callback_ = present_callback; +} + +} // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm new file mode 100644 index 0000000000000..7db8d870e05a9 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm @@ -0,0 +1,29 @@ +// 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/Source/FlutterGLCompositor.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h" +#import "flutter/testing/testing.h" + +namespace flutter::testing { + +TEST(FlutterGLCompositorTest, TestPresent) { + id mockViewController = CreateMockViewController(nil); + + std::unique_ptr macos_compositor = + std::make_unique(mockViewController); + + bool flag = false; + macos_compositor->SetPresentCallback([f = &flag]() { + *f = true; + return true; + }); + + ASSERT_TRUE(macos_compositor->Present(nil, 0)); + ASSERT_TRUE(flag); +} + +} // flutter::testing diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index ab73278d9c5bf..671520f494923 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm @@ -9,34 +9,14 @@ #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/FlutterViewControllerTestUtils.h" #include "flutter/testing/testing.h" namespace flutter::testing { -// Returns a mock FlutterViewController that is able to work in environments -// without a real pasteboard. -id mockViewController(NSString* pasteboardString) { - NSString* fixtures = @(testing::GetFixturesPath()); - FlutterDartProject* project = [[FlutterDartProject alloc] - initWithAssetsPath:fixtures - ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; - - // Mock pasteboard so that this test will work in environments without a - // real pasteboard. - id pasteboardMock = OCMClassMock([NSPasteboard class]); - OCMExpect([pasteboardMock stringForType:[OCMArg any]]).andDo(^(NSInvocation* invocation) { - NSString* returnValue = pasteboardString.length > 0 ? pasteboardString : nil; - [invocation setReturnValue:&returnValue]; - }); - id viewControllerMock = OCMPartialMock(viewController); - OCMStub([viewControllerMock pasteboard]).andReturn(pasteboardMock); - return viewControllerMock; -} - TEST(FlutterViewController, HasStringsWhenPasteboardEmpty) { // Mock FlutterViewController so that it behaves like the pasteboard is empty. - id viewControllerMock = mockViewController(nil); + id viewControllerMock = CreateMockViewController(nil); // Call hasStrings and expect it to be false. __block bool calledAfterClear = false; @@ -56,7 +36,7 @@ id mockViewController(NSString* pasteboardString) { TEST(FlutterViewController, HasStringsWhenPasteboardFull) { // Mock FlutterViewController so that it behaves like the pasteboard has a // valid string. - id viewControllerMock = mockViewController(@"some string"); + id viewControllerMock = CreateMockViewController(@"some string"); // Call hasStrings and expect it to be true. __block bool called = false; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h new file mode 100644 index 0000000000000..cc3c7f2bcff17 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h @@ -0,0 +1,18 @@ +// 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 + +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" +#import "flutter/testing/testing.h" + +namespace flutter::testing { + +// Returns a mock FlutterViewController that is able to work in environments +// without a real pasteboard. +id CreateMockViewController(NSString* pasteboardString); + +} diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm new file mode 100644 index 0000000000000..972e3515283ed --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm @@ -0,0 +1,30 @@ +// 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/FlutterViewControllerTestUtils.h" + +namespace flutter::testing { + +id CreateMockViewController(NSString* pasteboardString) { + { + NSString* fixtures = @(testing::GetFixturesPath()); + FlutterDartProject* project = [[FlutterDartProject alloc] + initWithAssetsPath:fixtures + ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project]; + + // Mock pasteboard so that this test will work in environments without a + // real pasteboard. + id pasteboardMock = OCMClassMock([NSPasteboard class]); + OCMExpect([pasteboardMock stringForType:[OCMArg any]]).andDo(^(NSInvocation* invocation) { + NSString* returnValue = pasteboardString.length > 0 ? pasteboardString : nil; + [invocation setReturnValue:&returnValue]; + }); + id viewControllerMock = OCMPartialMock(viewController); + OCMStub([viewControllerMock pasteboard]).andReturn(pasteboardMock); + return viewControllerMock; + } +} + +} From 1770cef53981dc0674d8e464fbd82feabc0c0c2a Mon Sep 17 00:00:00 2001 From: richardjcai Date: Mon, 30 Nov 2020 15:01:20 -0500 Subject: [PATCH 2/2] Add additional state to manage frame status and CALayers to FlutterGLCompositor --- .../Source/FlutterBackingStoreData.h | 23 +++- .../Source/FlutterBackingStoreData.mm | 19 ++-- .../macos/framework/Source/FlutterEngine.mm | 9 ++ .../Source/FlutterFrameBufferProvider.h | 2 +- .../Source/FlutterFrameBufferProvider.mm | 2 +- .../framework/Source/FlutterGLCompositor.h | 36 +++++- .../framework/Source/FlutterGLCompositor.mm | 104 +++++++++++++----- .../framework/Source/MacOSGLContextSwitch.h | 2 +- .../framework/Source/MacOSGLContextSwitch.mm | 2 +- 9 files changed, 151 insertions(+), 48 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h index 753895e1be395..f1dc4f961da62 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h @@ -2,7 +2,11 @@ // 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/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" + #import +#import /** * FlutterBackingStoreData holds data to be stored in the @@ -10,8 +14,23 @@ */ @interface FlutterBackingStoreData : NSObject -- (nullable instancetype)initWithIsRootView:(bool)isRootView; +- (nullable instancetype)initWithLayerId:(size_t)layerId + fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider + ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder; -- (bool)isRootView; +/** + * The layer's key value in FlutterGLCompositor's ca_layer_map_. + */ +@property(nonatomic, readonly) size_t layerId; + +/** + * Provides the fbo for rendering the layer. + */ +@property(nonnull, nonatomic, readonly) FlutterFrameBufferProvider* frameBufferProvider; + +/** + * Contains the IOSurfaceRef with the layer contents. + */ +@property(nonnull, nonatomic, readonly) FlutterIOSurfaceHolder* ioSurfaceHolder; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm index 342b9fc079e66..4a08244095229 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm @@ -3,25 +3,22 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" #include -@interface FlutterBackingStoreData () { - bool _isRootView; -} -@end - @implementation FlutterBackingStoreData -- (nullable instancetype)initWithIsRootView:(bool)isRootView { +- (nullable instancetype)initWithLayerId:(size_t)layerId + fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider + ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder { if (self = [super init]) { - _isRootView = isRootView; + _layerId = layerId; + _frameBufferProvider = fbProvider; + _ioSurfaceHolder = ioSurfaceHolder; } return self; } -- (bool)isRootView { - return _isRootView; -} - @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 6e50f5d6b4504..4bf9e3f2b1845 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -370,6 +370,13 @@ - (void)setViewController:(FlutterViewController*)controller { } - (FlutterCompositor*)createFlutterCompositor { + // 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(_viewController); @@ -405,6 +412,8 @@ - (FlutterCompositor*)createFlutterCompositor { _macOSGLCompositor->SetPresentCallback( [weak_self]() { return [weak_self engineCallbackOnPresent]; }); + _compositor.avoid_backing_store_cache = true; + return &_compositor; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h b/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h index f07e9ee2cd5ef..7dab6127156d2 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h @@ -9,7 +9,7 @@ */ @interface FlutterFrameBufferProvider : NSObject -- (nullable instancetype)initWithOpenGLContext:(nonnull NSOpenGLContext*)opengLContext; +- (nullable instancetype)initWithOpenGLContext:(nonnull const NSOpenGLContext*)opengLContext; /** * Returns the id of the framebuffer. diff --git a/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm b/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm index c7d4ff463c611..7bf08b3167ca5 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm @@ -15,7 +15,7 @@ @interface FlutterFrameBufferProvider () { @end @implementation FlutterFrameBufferProvider -- (instancetype)initWithOpenGLContext:(NSOpenGLContext*)openGLContext { +- (instancetype)initWithOpenGLContext:(const NSOpenGLContext*)openGLContext { if (self = [super init]) { MacOSGLContextSwitch context_switch(openGLContext); diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h index 19293facd40fd..e33dd6bb1c7db 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include +#include #include "flutter/fml/macros.h" #include "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.h" @@ -22,9 +22,10 @@ class FlutterGLCompositor { // Creates a BackingStore and saves updates the backing_store_out // data with the new BackingStore data. - // If the view requesting the backing store is the root view, - // we do not create a new backing store but rather return the - // backing store associated with the root view's FlutterSurfaceManager. + // If the backing store is being requested for the first time + // for a given frame, we do not create a new backing store but + // rather return the backing store associated with the + // FlutterView's FlutterSurfaceManager. // // Any additional state allocated for the backing store and // saved as user_data in the backing store must be collected @@ -38,6 +39,7 @@ class FlutterGLCompositor { // Presents the FlutterLayers by updating FlutterView(s) using the // layer content. + // Present sets frame_started_ to false. bool Present(const FlutterLayer** layers, size_t layers_count); using PresentCallback = std::function; @@ -46,9 +48,31 @@ class FlutterGLCompositor { void SetPresentCallback(const PresentCallback& present_callback); private: - FlutterViewController* view_controller_; + const FlutterViewController* view_controller_; + const NSOpenGLContext* open_gl_context_; PresentCallback present_callback_; - NSOpenGLContext* open_gl_context_; + + // Count for how many CALayers have been created for a frame. + // Resets when a frame is finished. + // ca_layer_count_ is also used as a layerId. + size_t ca_layer_count_ = 0; + + // Maps a layer_id (size_t) to a CALayer. + // The layer_id starts at 0 for a given frame + // and increments by 1 for each new CALayer. + std::map ca_layer_map_; + + // frame_started_ keeps track of if a layer has been + // created for the frame. + bool frame_started_ = false; + + // Set frame_started_ to true and reset all layer state. + void StartFrame(); + + // Creates a CALayer and adds it to ca_layer_map_ and increments + // ca_layer_count_; Returns the key value (size_t) for the layer in + // ca_layer_map_. + size_t CreateCALayer(); FML_DISALLOW_COPY_AND_ASSIGN(FlutterGLCompositor); }; diff --git a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm index a2fb9faa65607..4f187232ec143 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -3,48 +3,60 @@ // found in the LICENSE file. #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" #import + #import "flutter/fml/logging.h" #import "flutter/fml/platform/darwin/cf_utils.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" #import "third_party/skia/include/core/SkCanvas.h" #import "third_party/skia/include/core/SkSurface.h" #import "third_party/skia/include/gpu/gl/GrGLAssembleInterface.h" #import "third_party/skia/include/utils/mac/SkCGUtils.h" -#include - namespace flutter { FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller) - : view_controller_(view_controller), - open_gl_context_(view_controller.flutterView.openGLContext) {} + : open_gl_context_(view_controller.flutterView.openGLContext) { + FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr"; + view_controller_ = view_controller; +} bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config, FlutterBackingStore* backing_store_out) { CGSize size = CGSizeMake(config->size.width, config->size.height); - FlutterBackingStoreData* data = - [[FlutterBackingStoreData alloc] initWithIsRootView:config->is_root_view]; - backing_store_out->type = kFlutterBackingStoreTypeOpenGL; - backing_store_out->is_cacheable = true; - backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; - backing_store_out->open_gl.framebuffer.target = GL_RGBA8; - - if (config->is_root_view) { - // The root view uses FlutterSurfaceManager and is not cacheable since - // the fbo id changes on every present. - backing_store_out->is_cacheable = false; + if (!frame_started_) { + StartFrame(); + // If the backing store is for the first layer, return the fbo for the + // FlutterView. auto fbo = [view_controller_.flutterView frameBufferIDForSize:size]; backing_store_out->open_gl.framebuffer.name = fbo; } else { - FML_CHECK(false) << "Compositor only supports creating a backing store for the root view"; + FlutterFrameBufferProvider* fb_provider = + [[FlutterFrameBufferProvider alloc] initWithOpenGLContext:open_gl_context_]; + FlutterIOSurfaceHolder* io_surface_holder = [FlutterIOSurfaceHolder alloc]; + + GLuint fbo = [fb_provider glFrameBufferId]; + GLuint texture = [fb_provider glTextureId]; + + size_t layer_id = CreateCALayer(); + + [io_surface_holder bindSurfaceToTexture:texture fbo:fbo size:size]; + FlutterBackingStoreData* data = + [[FlutterBackingStoreData alloc] initWithLayerId:layer_id + fbProvider:fb_provider + ioSurfaceHolder:io_surface_holder]; + + backing_store_out->open_gl.framebuffer.name = fbo; + backing_store_out->open_gl.framebuffer.user_data = (__bridge_retained void*)data; } - backing_store_out->open_gl.framebuffer.user_data = (__bridge_retained void*)data; + backing_store_out->type = kFlutterBackingStoreTypeOpenGL; + backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer; + backing_store_out->open_gl.framebuffer.target = GL_RGBA8; backing_store_out->open_gl.framebuffer.destruction_callback = [](void* user_data) { if (user_data != nullptr) { CFRelease(user_data); @@ -64,19 +76,36 @@ FlutterBackingStore* backing_store = const_cast(layer->backing_store); switch (layer->type) { case kFlutterLayerContentTypeBackingStore: { - FlutterBackingStoreData* data = - (__bridge FlutterBackingStoreData*)(backing_store->open_gl.framebuffer.user_data); - if (![data isRootView]) { - FML_CHECK(false) << "Compositor only supports presenting the root view."; + if (backing_store->open_gl.framebuffer.user_data) { + FlutterBackingStoreData* 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]; } break; } case kFlutterLayerContentTypePlatformView: // Add functionality in follow up PR. - FML_CHECK(false) << "Presenting PlatformViews not yet supported"; + FML_LOG(WARNING) << "Presenting PlatformViews not yet supported"; break; }; } + // The frame has been presented, prepare FlutterGLCompositor to + // render a new frame. + frame_started_ = false; return present_callback_(); } @@ -85,4 +114,29 @@ present_callback_ = present_callback; } +void FlutterGLCompositor::StartFrame() { + // First reset all the state. + ca_layer_count_ = 0; + + // First remove all CALayers from the superlayer. + for (auto const& ca_layer_kvp : ca_layer_map_) { + [ca_layer_kvp.second removeFromSuperlayer]; + } + + // Reset layer map. + ca_layer_map_.clear(); + + frame_started_ = true; +} + +size_t FlutterGLCompositor::CreateCALayer() { + // FlutterGLCompositor manages the lifecycle of content layers. + // The id for a CALayer starts at 0 and increments by 1 for + // any given frame. + CALayer* content_layer = [[CALayer alloc] init]; + [view_controller_.flutterView.layer addSublayer:content_layer]; + ca_layer_map_[ca_layer_count_] = content_layer; + return ca_layer_count_++; +} + } // namespace flutter diff --git a/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h b/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h index a1957e4553c5e..079000dcea91a 100644 --- a/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h +++ b/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h @@ -10,7 +10,7 @@ */ class MacOSGLContextSwitch { public: - explicit MacOSGLContextSwitch(NSOpenGLContext* context); + explicit MacOSGLContextSwitch(const NSOpenGLContext* context); ~MacOSGLContextSwitch(); MacOSGLContextSwitch(const MacOSGLContextSwitch&) = delete; diff --git a/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm b/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm index fd53920c7898a..73d0bfef51f41 100644 --- a/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm +++ b/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm @@ -4,7 +4,7 @@ #import "flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h" -MacOSGLContextSwitch::MacOSGLContextSwitch(NSOpenGLContext* context) { +MacOSGLContextSwitch::MacOSGLContextSwitch(const NSOpenGLContext* context) { previous_ = [NSOpenGLContext currentContext]; [context makeCurrentContext]; }