Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Refactor MacOS to use Compositor to create backing stores using FlutterSurfaceManager #22500

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterEngine_Internal.h",
"framework/Source/FlutterExternalTextureGL.h",
"framework/Source/FlutterExternalTextureGL.mm",
"framework/Source/FlutterMacOSGLCompositor.h",
"framework/Source/FlutterMacOSGLCompositor.mm",
"framework/Source/FlutterMouseCursorPlugin.h",
"framework/Source/FlutterMouseCursorPlugin.mm",
"framework/Source/FlutterResizeSynchronizer.h",
Expand All @@ -73,9 +75,13 @@ source_set("flutter_framework_source") {
sources += _flutter_framework_headers

deps = [
"//flutter/flow:flow",
"//flutter/fml",
"//flutter/fml:fml",
"//flutter/shell/platform/common/cpp:common_cpp_switches",
"//flutter/shell/platform/darwin/common:framework_shared",
"//flutter/shell/platform/embedder:embedder_as_internal_library",
"//third_party/skia",
]

public_configs = [ "//flutter:config" ]
Expand Down Expand Up @@ -115,7 +121,10 @@ executable("flutter_desktop_darwin_unittests") {

sources = [
"framework/Source/FlutterEngineTest.mm",
"framework/Source/FlutterMacOSGLCompositorUnittests.mm",
"framework/Source/FlutterViewControllerTest.mm",
"framework/Source/FlutterViewControllerTestsUtils.h",
"framework/Source/FlutterViewControllerTestsUtils.mm",
]

cflags_objcc = [ "-fobjc-arc" ]
Expand Down
51 changes: 50 additions & 1 deletion shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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/FlutterMacOSGLCompositor.h"
#import "flutter/shell/platform/embedder/embedder.h"

/**
Expand Down Expand Up @@ -197,6 +197,11 @@ @implementation FlutterEngine {

// Pointer to the Dart AOT snapshot and instruction data.
_FlutterEngineAOTData* _aotData;

// FlutterMacOSGLCompositor is created by the engine.
// This is only created when the engine has a FlutterViewController
// and used to support platform views.
std::unique_ptr<flutter::FlutterMacOSGLCompositor> _macOSCompositor;
}

- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
Expand Down Expand Up @@ -306,6 +311,15 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
flutterArguments.aot_data = _aotData;
}

// Only create a Compositor if we have a ViewController.
if (_viewController) {
// Engine does not need to manage the life cycle of compositor
// since compositor is captured and copied in embedder.cc.
FlutterCompositor compositor = {};
[self setupCompositor:&compositor];
flutterArguments.compositor = &compositor;
}

FlutterEngineResult result = _embedderAPI.Initialize(
FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
if (result != kSuccess) {
Expand Down Expand Up @@ -360,6 +374,41 @@ - (void)setViewController:(FlutterViewController*)controller {
}
}

- (void)setupCompositor:(FlutterCompositor*)compositor {
[_mainOpenGLContext makeCurrentContext];

_macOSCompositor =
std::make_unique<flutter::FlutterMacOSGLCompositor>(_viewController, _resourceContext);

compositor->struct_size = sizeof(FlutterCompositor);
compositor->user_data = _macOSCompositor.get();

compositor->create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
FlutterBackingStore* backing_store_out, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterMacOSGLCompositor*>(user_data)->CreateBackingStore(
config, backing_store_out);
};

compositor->collect_backing_store_callback = [](const FlutterBackingStore* backing_store, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterMacOSGLCompositor*>(user_data)->CollectBackingStore(
backing_store);
};

compositor->present_layers_callback = [](const FlutterLayer** layers, //
size_t layers_count, //
void* user_data //
) {
return reinterpret_cast<flutter::FlutterMacOSGLCompositor*>(user_data)->Present(layers,
layers_count);
};

_macOSCompositor->SetPresentCallback([self]() { return [self engineCallbackOnPresent]; });
}

- (id<FlutterBinaryMessenger>)binaryMessenger {
// TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
// keeping the engine alive.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// 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 <vector>

#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 {

/**
* FlutterMacOSGLCompositor creates and manages backing stores used for
* rendering Flutter content and presents Flutter content and Platform views.
*/
class FlutterMacOSGLCompositor {
public:
FlutterMacOSGLCompositor(FlutterViewController* view_controller,
NSOpenGLContext* open_gl_context);

virtual ~FlutterMacOSGLCompositor();

// Creates a backing store according to FlutterBackingStoreConfig
// by modifying backing_store_out.
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<bool()>;

// PresentCallback is called at the end of the Present function.
void SetPresentCallback(const PresentCallback& present_callback);

protected:
FlutterViewController* view_controller_;
PresentCallback present_callback_;
NSOpenGLContext* open_gl_context_;

// Creates a FlutterSurfaceManager and uses the FlutterSurfaceManager's
// underlying FBO and texture in the backing store.
bool CreateBackingStoreUsingSurfaceManager(
const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out);

FML_DISALLOW_COPY_AND_ASSIGN(FlutterMacOSGLCompositor);
};

} // namespace flutter
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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/FlutterMacOSGLCompositor.h"

#import <OpenGL/gl.h>
#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 <unistd.h>

namespace flutter {

FlutterMacOSGLCompositor::FlutterMacOSGLCompositor(FlutterViewController* view_controller,
NSOpenGLContext* open_gl_context)
: view_controller_(view_controller), open_gl_context_(open_gl_context) {}

FlutterMacOSGLCompositor::~FlutterMacOSGLCompositor() = default;

bool FlutterMacOSGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
return CreateBackingStoreUsingSurfaceManager(config, backing_store_out);
}

bool FlutterMacOSGLCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) {
// The memory for FlutterSurfaceManager is handled in the destruction callback.
// No other memory has to be collected.
return true;
}

bool FlutterMacOSGLCompositor::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<FlutterBackingStore*>(layer->backing_store);
switch (layer->type) {
case kFlutterLayerContentTypeBackingStore: {
FlutterSurfaceManager* surfaceManager =
(__bridge FlutterSurfaceManager*)backing_store->user_data;

CGSize size = CGSizeMake(layer->size.width, layer->size.height);
[view_controller_.flutterView frameBufferIDForSize:size];
[surfaceManager setLayerContentWithIOSurface:[surfaceManager getIOSurface]];
break;
}
case kFlutterLayerContentTypePlatformView:
// Add functionality in follow up PR.
FML_CHECK(false) << "Presenting PlatformViews not yet supported";
break;
};
}
return present_callback_();
}

bool FlutterMacOSGLCompositor::CreateBackingStoreUsingSurfaceManager(
const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
FlutterSurfaceManager* surfaceManager =
[[FlutterSurfaceManager alloc] initWithLayer:view_controller_.flutterView.layer
openGLContext:open_gl_context_];

GLuint fbo = [surfaceManager getFramebuffer];
GLuint texture = [surfaceManager getTexture];
IOSurfaceRef* io_surface_ref = [surfaceManager getIOSurface];

CGSize size = CGSizeMake(config->size.width, config->size.height);

[surfaceManager backTextureWithIOSurface:io_surface_ref size:size backingTexture:texture fbo:fbo];

backing_store_out->type = kFlutterBackingStoreTypeOpenGL;
backing_store_out->user_data = (__bridge_retained void*)surfaceManager;
backing_store_out->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
backing_store_out->open_gl.framebuffer.target = GL_RGBA8;
backing_store_out->open_gl.framebuffer.name = fbo;
backing_store_out->open_gl.framebuffer.user_data = backing_store_out->user_data;
backing_store_out->open_gl.framebuffer.destruction_callback = [](void* user_data) {
if (user_data != nullptr) {
CFRelease(user_data);
}
};

return true;
}

void FlutterMacOSGLCompositor::SetPresentCallback(
const FlutterMacOSGLCompositor::PresentCallback& present_callback) {
present_callback_ = present_callback;
}

} // namespace flutter
Original file line number Diff line number Diff line change
@@ -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 <Foundation/Foundation.h>

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMacOSGLCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestsUtils.h"
#import "flutter/testing/testing.h"

namespace flutter::testing {

TEST(FlutterMacOSGLCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController(nil);

std::unique_ptr<flutter::FlutterMacOSGLCompositor> macos_compositor =
std::make_unique<FlutterMacOSGLCompositor>(mockViewController, nil);

bool flag = false;
macos_compositor->SetPresentCallback([f = &flag]() {
*f = true;
return true;
});

ASSERT_TRUE(macos_compositor->Present(nil, 0));
ASSERT_TRUE(flag);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a test to ensure after collect backing store gets called, we invoke the destructor.

} // flutter::testing
Original file line number Diff line number Diff line change
@@ -1,18 +1,60 @@
// 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 <Cocoa/Cocoa.h>

// Manages the IOSurfaces for FlutterView
@interface FlutterSurfaceManager : NSObject

- (nullable instancetype)initWithLayer:(nonnull CALayer*)containingLayer
openGLContext:(nonnull NSOpenGLContext*)opengLContext;
- (instancetype)initWithLayer:(CALayer*)layer openGLContext:(NSOpenGLContext*)opengLContext;

- (void)ensureSurfaceSize:(CGSize)size;
- (void)swapBuffers;

- (uint32_t)glFrameBufferId;

/**
* Sets the CALayer content to the content of _ioSurface[kBack].
*/
- (void)setLayerContent;

/**
* Sets the CALayer content to the content of the provided ioSurface.
*/
- (void)setLayerContentWithIOSurface:(IOSurfaceRef*)ioSurface;

/**
* Binds the IOSurface to the provided texture/framebuffer.
*/
- (void)backTextureWithIOSurface:(IOSurfaceRef*)ioSurface
size:(CGSize)size
backingTexture:(GLuint)texture
fbo:(GLuint)fbo;

// Methods used by FlutterMacOSCompositor to render Flutter content
// using a single Framebuffer/Texture/IOSurface.

/**
* Returns the kFront framebuffer.
* The framebuffer is used by FlutterMacOSCompositor to create a backing store.
* The framebuffer is collected when the backing store that uses the
* framebuffer is collected.
*/
- (uint32_t)getFramebuffer;

/**
* Returns the kFront texture.
* The texture is used by FlutterMacOSCompositor to create a backing store.
* The texture is collected when the backing store that uses the
* texture is collected.
*/
- (uint32_t)getTexture;

/**
* Returns the kFront IOSurfaceRef.
* The IOSurface is backed by the FBO provided by getFramebuffer
* and texture provided by getTexture. The IOSurface is used
* in FlutterMacOSCompositor's Present call.
* The IOSurface is collected when the backing store that uses the
* IOSurface is collected.
*/
- (IOSurfaceRef*)getIOSurface;

@end
Loading