From cb35e23e34a1ce33adac3abb7c439c868eabe173 Mon Sep 17 00:00:00 2001 From: Richard Cai Date: Thu, 3 Dec 2020 11:57:15 -0500 Subject: [PATCH] (MacOS) Add FlutterGLCompositor with support for rendering multiple layers (flutter/engine#22782) * Create FlutterGLCompositor. * Add additional state to manage frame status and CALayers to FlutterGLCompositor FlutterGLCompositor supports rendering multiple layers. The first layer is rendered using the FlutterView. Additional CALayers are created if there is more than one layer. Platform view support will be added in following PR. --- .../ci/licenses_golden/licenses_flutter | 7 + .../shell/platform/darwin/macos/BUILD.gn | 10 ++ .../Source/FlutterBackingStoreData.h | 36 +++++ .../Source/FlutterBackingStoreData.mm | 24 +++ .../macos/framework/Source/FlutterEngine.mm | 59 +++++++- .../Source/FlutterFrameBufferProvider.h | 2 +- .../Source/FlutterFrameBufferProvider.mm | 2 +- .../framework/Source/FlutterGLCompositor.h | 80 ++++++++++ .../framework/Source/FlutterGLCompositor.mm | 142 ++++++++++++++++++ .../Source/FlutterGLCompositorUnittests.mm | 29 ++++ .../Source/FlutterViewControllerTest.mm | 26 +--- .../Source/FlutterViewControllerTestUtils.h | 18 +++ .../Source/FlutterViewControllerTestUtils.mm | 30 ++++ .../framework/Source/MacOSGLContextSwitch.h | 2 +- .../framework/Source/MacOSGLContextSwitch.mm | 2 +- 15 files changed, 441 insertions(+), 28 deletions(-) create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h create mode 100644 engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 038bf666ff4..181371cd40f 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1053,6 +1053,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 @@ -1062,6 +1064,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 @@ -1078,6 +1083,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/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn b/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn index 8a7f69477ae..79996675a99 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/BUILD.gn +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h new file mode 100644 index 00000000000..f1dc4f961da --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.h @@ -0,0 +1,36 @@ +// 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/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" + +#import +#import + +/** + * FlutterBackingStoreData holds data to be stored in the + * BackingStore's user_data. + */ +@interface FlutterBackingStoreData : NSObject + +- (nullable instancetype)initWithLayerId:(size_t)layerId + fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider + ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder; + +/** + * 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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm new file mode 100644 index 00000000000..4a082440952 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterBackingStoreData.mm @@ -0,0 +1,24 @@ +// 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" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h" + +#include + +@implementation FlutterBackingStoreData + +- (nullable instancetype)initWithLayerId:(size_t)layerId + fbProvider:(nonnull FlutterFrameBufferProvider*)fbProvider + ioSurfaceHolder:(nonnull FlutterIOSurfaceHolder*)ioSurfaceHolder { + if (self = [super init]) { + _layerId = layerId; + _frameBufferProvider = fbProvider; + _ioSurfaceHolder = ioSurfaceHolder; + } + return self; +} + +@end 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 d9ebd55f0d7..4bf9e3f2b18 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 @@ -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 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, // 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 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, 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,54 @@ static bool OnAcquireExternalTexture(FlutterEngine* engine, } } +- (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); + + _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]; }); + + _compositor.avoid_backing_store_cache = true; + + return &_compositor; +} + - (id)binaryMessenger { // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins // keeping the engine alive. diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h index f07e9ee2cd5..7dab6127156 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.h +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm index c7d4ff463c6..7bf08b3167c 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterFrameBufferProvider.mm @@ -15,7 +15,7 @@ @end @implementation FlutterFrameBufferProvider -- (instancetype)initWithOpenGLContext:(NSOpenGLContext*)openGLContext { +- (instancetype)initWithOpenGLContext:(const NSOpenGLContext*)openGLContext { if (self = [super init]) { MacOSGLContextSwitch context_switch(openGLContext); 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 new file mode 100644 index 00000000000..e33dd6bb1c7 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h @@ -0,0 +1,80 @@ +// 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 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 + // 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. + // Present sets frame_started_ to false. + 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: + const FlutterViewController* view_controller_; + const NSOpenGLContext* open_gl_context_; + PresentCallback present_callback_; + + // 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); +}; + +} // namespace flutter 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 new file mode 100644 index 00000000000..4f187232ec1 --- /dev/null +++ b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm @@ -0,0 +1,142 @@ +// 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 + +#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" + +namespace flutter { + +FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller) + : 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); + + 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 { + 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->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); + } + }; + + 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: { + 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_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_(); +} + +void FlutterGLCompositor::SetPresentCallback( + const FlutterGLCompositor::PresentCallback& present_callback) { + 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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm new file mode 100644 index 00000000000..7db8d870e05 --- /dev/null +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm index ab73278d9c5..671520f4949 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm +++ b/engine/src/flutter/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 @@ TEST(FlutterViewController, HasStringsWhenPasteboardEmpty) { 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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h new file mode 100644 index 00000000000..cc3c7f2bcff --- /dev/null +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.mm new file mode 100644 index 00000000000..972e3515283 --- /dev/null +++ b/engine/src/flutter/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; + } +} + +} diff --git a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h index a1957e4553c..079000dcea9 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.h +++ b/engine/src/flutter/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/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm b/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm index fd53920c789..73d0bfef51f 100644 --- a/engine/src/flutter/shell/platform/darwin/macos/framework/Source/MacOSGLContextSwitch.mm +++ b/engine/src/flutter/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]; }