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

[macOS] Merge FlutterMetalCompositor, superclass #37648

Merged
merged 1 commit into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 1 addition & 3 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2596,6 +2596,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChann
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterChannelKeyResponderUnittests.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositorUnittests.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/FlutterEmbedderExternalTextureUnittests.mm
Expand All @@ -2621,9 +2622,6 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuP
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPluginTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin_Internal.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRendererTest.mm
Expand Down
4 changes: 1 addition & 3 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterMenuPlugin.h",
"framework/Source/FlutterMenuPlugin.mm",
"framework/Source/FlutterMenuPlugin_Internal.h",
"framework/Source/FlutterMetalCompositor.h",
"framework/Source/FlutterMetalCompositor.mm",
"framework/Source/FlutterMetalRenderer.h",
"framework/Source/FlutterMetalRenderer.mm",
"framework/Source/FlutterMouseCursorPlugin.h",
Expand Down Expand Up @@ -171,14 +169,14 @@ executable("flutter_desktop_darwin_unittests") {
sources = [
"framework/Source/AccessibilityBridgeMacTest.mm",
"framework/Source/FlutterChannelKeyResponderUnittests.mm",
"framework/Source/FlutterCompositorUnittests.mm",
"framework/Source/FlutterEmbedderExternalTextureUnittests.mm",
"framework/Source/FlutterEmbedderKeyResponderUnittests.mm",
"framework/Source/FlutterEngineTest.mm",
"framework/Source/FlutterEngineTestUtils.h",
"framework/Source/FlutterEngineTestUtils.mm",
"framework/Source/FlutterKeyboardManagerUnittests.mm",
"framework/Source/FlutterMenuPluginTest.mm",
"framework/Source/FlutterMetalCompositorUnittests.mm",
"framework/Source/FlutterMetalRendererTest.mm",
"framework/Source/FlutterMetalSurfaceManagerTest.mm",
"framework/Source/FlutterPlatformNodeDelegateMacTest.mm",
Expand Down
81 changes: 50 additions & 31 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <list>

#include "flutter/fml/macros.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h"
#include "flutter/shell/platform/embedder/embedder.h"
Expand All @@ -25,52 +26,57 @@ class FlutterCompositor {
// The view_provider is used to query FlutterViews from view IDs,
// which are used for presenting and creating backing stores.
// It must not be null, and is typically FlutterViewEngineProvider.
explicit FlutterCompositor(id<FlutterViewProvider> view_provider);
explicit FlutterCompositor(
id<FlutterViewProvider> view_provider,
FlutterPlatformViewController* platform_views_controller,
id<MTLDevice> mtl_device);

virtual ~FlutterCompositor() = default;
~FlutterCompositor() = default;

// 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, this compositor does not create a new backing
// store but rather returns the backing store associated with the
// FlutterView's FlutterSurfaceManager.
// Creates a backing store and saves updates the backing_store_out data with
// the new FlutterBackingStore data.
//
// 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 destruction_callback field which will
// be called when the embedder collects the backing store.
virtual bool CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) = 0;
// If the backing store is being requested for the first time for a given
// frame, this compositor does not create a new backing store but rather
// returns 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
// destruction_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.
virtual bool CollectBackingStore(
const FlutterBackingStore* backing_store) = 0;
// Releases the memory for any resources that were allocated for the
// specified backing store.
bool CollectBackingStore(const FlutterBackingStore* backing_store);

// Presents the FlutterLayers by updating the FlutterView specified by
// `view_id` using the layer content.
// Present sets frame_started_ to false.
virtual bool Present(uint64_t view_id,
const FlutterLayer** layers,
size_t layers_count) = 0;
// `view_id` using the layer content. Sets frame_started_ to false.
bool Present(uint64_t view_id,
const FlutterLayer** layers,
size_t layers_count);

// Callback triggered at the end of the Present function. has_flutter_content
// is true when Flutter content was rendered, otherwise false.
using PresentCallback = std::function<bool(bool has_flutter_content)>;

// PresentCallback is called at the end of the Present function.
// Registers a callback to be triggered at the end of the Present function.
// If a callback was previously registered, it will be replaced.
void SetPresentCallback(const PresentCallback& present_callback);

// Denotes the current status of the frame being composited.
// Started: A new frame has begun and we have cleared the old layer tree
// and are now creating backingstore(s) for the embedder to use.
// The status of the frame being composited.
// Started: A new frame has begun and we have cleared the old layer tree and
// are now creating backingstore(s) for the embedder to use.
// Presenting: the embedder has finished rendering into the provided
// backingstore(s) and we are creating the layer tree for the
// system compositor to present with.
// Ended: The frame has been presented and we are no longer processing
// it.
// backingstore(s) and we are creating the layer tree for the system
// compositor to present with.
// Ended: The frame has been presented and we are no longer processing it.
typedef enum { kStarted, kPresenting, kEnded } FrameStatus;

protected:
// Get the view associated with the view ID.
// Returns the view associated with the view ID.
//
// Returns nil if the ID is invalid.
FlutterView* GetView(uint64_t view_id);
Expand All @@ -94,12 +100,25 @@ class FlutterCompositor {
CATransform3D transform = CATransform3DIdentity);

private:
// Presents the platform view layer represented by `layer`. `layer_index` is
// used to position the layer in the z-axis. If the layer does not have a
// superview, it will become subview of `default_base_view`.
void PresentPlatformView(FlutterView* default_base_view,
const FlutterLayer* layer,
size_t layer_position);

// A list of the active CALayer objects for the frame that need to be removed.
std::list<CALayer*> active_ca_layers_;

// Where the compositor can query FlutterViews. Must not be null.
id<FlutterViewProvider> const view_provider_;

// The controller used to manage creation and deletion of platform views.
const FlutterPlatformViewController* platform_view_controller_;

// The Metal device used to draw graphics.
const id<MTLDevice> mtl_device_;

// Callback set by the embedder to be called when the layer tree has been
// correctly set up for this frame.
PresentCallback present_callback_;
Expand Down
132 changes: 129 additions & 3 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,139 @@
// found in the LICENSE file.

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"

#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterIOSurfaceHolder.h"

namespace flutter {

FlutterCompositor::FlutterCompositor(id<FlutterViewProvider> view_provider)
: view_provider_(view_provider) {
FML_CHECK(view_provider != nullptr) << "FlutterViewProvider* cannot be nullptr";
FlutterCompositor::FlutterCompositor(id<FlutterViewProvider> view_provider,
FlutterPlatformViewController* platform_view_controller,
id<MTLDevice> mtl_device)
: view_provider_(view_provider),
platform_view_controller_(platform_view_controller),
mtl_device_(mtl_device) {
FML_CHECK(view_provider != nullptr) << "view_provider cannot be nullptr";
}

bool FlutterCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
// TODO(dkwingsmt): This class only supports single-view for now. As more
// classes are gradually converted to multi-view, it should get the view ID
// from somewhere.
FlutterView* view = GetView(kFlutterDefaultViewId);
if (!view) {
return false;
}

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

backing_store_out->metal.struct_size = sizeof(FlutterMetalBackingStore);
backing_store_out->metal.texture.struct_size = sizeof(FlutterMetalTexture);

if (GetFrameStatus() != FrameStatus::kStarted) {
StartFrame();
// If the backing store is for the first layer, return the MTLTexture for the
// FlutterView.
FlutterMetalRenderBackingStore* backingStore =
reinterpret_cast<FlutterMetalRenderBackingStore*>([view backingStoreForSize:size]);
backing_store_out->metal.texture.texture =
(__bridge FlutterMetalTextureHandle)backingStore.texture;
} else {
FlutterIOSurfaceHolder* io_surface_holder = [[FlutterIOSurfaceHolder alloc] init];
[io_surface_holder recreateIOSurfaceWithSize:size];
auto texture_descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:size.width
height:size.height
mipmapped:NO];
texture_descriptor.usage =
MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;

backing_store_out->metal.texture.texture = (__bridge_retained FlutterMetalTextureHandle)
[mtl_device_ newTextureWithDescriptor:texture_descriptor
iosurface:[io_surface_holder ioSurface]
plane:0];

backing_store_out->metal.texture.user_data = (__bridge_retained void*)io_surface_holder;
}

backing_store_out->type = kFlutterBackingStoreTypeMetal;
backing_store_out->metal.texture.destruction_callback = [](void* user_data) {
if (user_data != nullptr) {
CFRelease(user_data);
}
};

return true;
}

bool FlutterCompositor::CollectBackingStore(const FlutterBackingStore* backing_store) {
// If we allocated this MTLTexture ourselves, user_data is not null, and we will need
// to release it manually.
if (backing_store->metal.texture.user_data != nullptr &&
backing_store->metal.texture.texture != nullptr) {
CFRelease(backing_store->metal.texture.texture);
}
return true;
}

bool FlutterCompositor::Present(uint64_t view_id,
const FlutterLayer** layers,
size_t layers_count) {
FlutterView* view = GetView(view_id);
if (!view) {
return false;
}

SetFrameStatus(FrameStatus::kPresenting);

bool has_flutter_content = false;
for (size_t i = 0; i < layers_count; ++i) {
const auto* layer = layers[i];
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);

switch (layer->type) {
case kFlutterLayerContentTypeBackingStore: {
if (backing_store->metal.texture.user_data) {
FlutterIOSurfaceHolder* io_surface_holder =
(__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data;
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
InsertCALayerForIOSurface(view, io_surface);
}
has_flutter_content = true;
break;
}
case kFlutterLayerContentTypePlatformView: {
PresentPlatformView(view, layer, i);
break;
}
};
}

return EndFrame(has_flutter_content);
}

void FlutterCompositor::PresentPlatformView(FlutterView* default_base_view,
const FlutterLayer* layer,
size_t layer_position) {
// TODO (https://github.com/flutter/flutter/issues/96668)
// once the issue is fixed, this check will pass.
FML_DCHECK([[NSThread currentThread] isMainThread])
<< "Must be on the main thread to present platform views";

int64_t platform_view_id = layer->platform_view->identifier;
NSView* platform_view = [platform_view_controller_ platformViewWithID:platform_view_id];

FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;

CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
layer->size.width / scale, layer->size.height / scale);
if (platform_view.superview == nil) {
[default_base_view addSubview:platform_view];
}
platform_view.layer.zPosition = layer_position;
}

void FlutterCompositor::SetPresentCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewProvider.h"
#import "flutter/testing/testing.h"
Expand Down Expand Up @@ -62,10 +62,10 @@ - (nullable FlutterView*)getView:(uint64_t)viewId {
}
} // namespace

TEST(FlutterMetalCompositorTest, TestPresent) {
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(
MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
TEST(FlutterCompositorTest, TestPresent) {
std::unique_ptr<flutter::FlutterCompositor> macos_compositor =
std::make_unique<FlutterCompositor>(MockViewProvider(), /*platform_view_controller*/ nullptr,
/*mtl_device*/ nullptr);

bool flag = false;
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
Expand All @@ -77,10 +77,10 @@ - (nullable FlutterView*)getView:(uint64_t)viewId {
ASSERT_TRUE(flag);
}

TEST(FlutterMetalCompositorTest, TestCreate) {
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(
MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
TEST(FlutterCompositorTest, TestCreate) {
std::unique_ptr<flutter::FlutterCompositor> macos_compositor =
std::make_unique<FlutterCompositor>(MockViewProvider(), /*platform_view_controller*/ nullptr,
/*mtl_device*/ nullptr);

FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
Expand All @@ -96,10 +96,10 @@ - (nullable FlutterView*)getView:(uint64_t)viewId {
ASSERT_EQ(texture.height, 600ul);
}

TEST(FlutterMetalCompositorTest, TestCompositing) {
std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(
MockViewProvider(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
TEST(FlutterCompositorTest, TestCompositing) {
std::unique_ptr<flutter::FlutterCompositor> macos_compositor =
std::make_unique<FlutterCompositor>(MockViewProvider(), /*platform_view_controller*/ nullptr,
/*mtl_device*/ nullptr);

FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
Expand Down
9 changes: 4 additions & 5 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#include <iostream>
#include <vector>

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMetalRenderer.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
Expand Down Expand Up @@ -202,9 +202,8 @@ @implementation FlutterEngine {
// Pointer to the Dart AOT snapshot and instruction data.
_FlutterEngineAOTData* _aotData;

// _macOSCompositor is created when the engine is created and
// its destruction is handled by ARC when the engine is destroyed.
// This is either a FlutterGLCompositor or a FlutterMetalCompositor instance.
// _macOSCompositor is created when the engine is created and its destruction is handled by ARC
// when the engine is destroyed.
std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;

FlutterViewEngineProvider* _viewProvider;
Expand Down Expand Up @@ -424,7 +423,7 @@ - (FlutterCompositor*)createFlutterCompositor {
__weak FlutterEngine* weakSelf = self;

FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
_macOSCompositor = std::make_unique<flutter::FlutterMetalCompositor>(
_macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
_viewProvider, _platformViewController, metalRenderer.device);
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
if (has_flutter_content) {
Expand Down
Loading