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

[macOS, multiwindow] Compositor gets FlutterView lazily #36392

Merged
merged 22 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from 9 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
13 changes: 9 additions & 4 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <list>

#include "flutter/fml/macros.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
#include "flutter/shell/platform/embedder/embedder.h"

namespace flutter {
Expand All @@ -19,7 +19,9 @@ namespace flutter {
// Platform views are not yet supported.
class FlutterCompositor {
public:
explicit FlutterCompositor(FlutterViewController* view_controller);
using ViewProvider = std::function<FlutterView*(uint64_t view_id)>;

explicit FlutterCompositor(ViewProvider get_view_callback);

virtual ~FlutterCompositor() = default;

Expand Down Expand Up @@ -62,7 +64,7 @@ class FlutterCompositor {
typedef enum { kStarted, kPresenting, kEnded } FrameStatus;

protected:
__weak const FlutterViewController* view_controller_;
FlutterView* GetView(uint64_t view_id);

// Gets and sets the FrameStatus for the current frame.
void SetFrameStatus(FrameStatus frame_status);
Expand All @@ -76,15 +78,18 @@ class FlutterCompositor {
bool EndFrame(bool has_flutter_content);

// Creates a CALayer object which is backed by the supplied IOSurface, and
// adds it to the root CALayer for this FlutterViewController's view.
// adds it to the root CALayer for the given view.
void InsertCALayerForIOSurface(
FlutterView* view,
const IOSurfaceRef& io_surface,
CATransform3D transform = CATransform3DIdentity);

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

ViewProvider get_view_callback_;

// 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
17 changes: 10 additions & 7 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@

namespace flutter {

FlutterCompositor::FlutterCompositor(FlutterViewController* view_controller) {
FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr";

view_controller_ = view_controller;
FlutterCompositor::FlutterCompositor(ViewProvider get_view_callback) {
get_view_callback_ = std::move(get_view_callback);
}

void FlutterCompositor::SetPresentCallback(
Expand All @@ -35,6 +33,10 @@
return status;
}

FlutterView* FlutterCompositor::GetView(uint64_t view_id) {
return get_view_callback_(view_id);
}

void FlutterCompositor::SetFrameStatus(FlutterCompositor::FrameStatus frame_status) {
frame_status_ = frame_status;
}
Expand All @@ -43,14 +45,15 @@
return frame_status_;
}

void FlutterCompositor::InsertCALayerForIOSurface(const IOSurfaceRef& io_surface,
void FlutterCompositor::InsertCALayerForIOSurface(FlutterView* view,
const IOSurfaceRef& io_surface,
CATransform3D transform) {
// FlutterCompositor manages the lifecycle of CALayers.
CALayer* content_layer = [[CALayer alloc] init];
content_layer.transform = transform;
content_layer.frame = view_controller_.flutterView.layer.bounds;
content_layer.frame = view.layer.bounds;
[content_layer setContents:(__bridge id)io_surface];
[view_controller_.flutterView.layer addSublayer:content_layer];
[view.layer addSublayer:content_layer];

active_ca_layers_.push_back(content_layer);
}
Expand Down
8 changes: 6 additions & 2 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -434,14 +434,18 @@ - (FlutterCompositor*)createFlutterCompositor {

__weak FlutterEngine* weakSelf = self;

flutter::FlutterCompositor::ViewProvider getViewCallback = [weakSelf](uint64_t view_id) {
return weakSelf.viewController == nullptr ? nullptr : weakSelf.viewController.flutterView;
};

if ([FlutterRenderingBackend renderUsingMetal]) {
FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
_macOSCompositor = std::make_unique<flutter::FlutterMetalCompositor>(
_viewController, _platformViewController, metalRenderer.device);
std::move(getViewCallback), _platformViewController, metalRenderer.device);
} else {
FlutterOpenGLRenderer* openGLRenderer = reinterpret_cast<FlutterOpenGLRenderer*>(_renderer);
[openGLRenderer.openGLContext makeCurrentContext];
_macOSCompositor = std::make_unique<flutter::FlutterGLCompositor>(_viewController,
_macOSCompositor = std::make_unique<flutter::FlutterGLCompositor>(std::move(getViewCallback),
openGLRenderer.openGLContext);
}
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace flutter {
// FlutterGLCompositor is created and destroyed by FlutterEngine.
class FlutterGLCompositor : public FlutterCompositor {
public:
FlutterGLCompositor(FlutterViewController* view_controller,
FlutterGLCompositor(ViewProvider get_view_callback,
NSOpenGLContext* opengl_context);

virtual ~FlutterGLCompositor() = default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@

namespace flutter {

FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller,
FlutterGLCompositor::FlutterGLCompositor(ViewProvider get_view_callback,
NSOpenGLContext* opengl_context)
: FlutterCompositor(view_controller), open_gl_context_(opengl_context) {}
: FlutterCompositor(get_view_callback), open_gl_context_(opengl_context) {}

bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
if (!view_controller_) {
// Always gets the first view, #0. After Flutter supports multi-view, it
// should get the view ID from somewhere.
FlutterView* view = GetView(0);
if (!view) {
return false;
}

Expand All @@ -36,8 +39,7 @@
// If the backing store is for the first layer, return the fbo for the
// FlutterView.
FlutterOpenGLRenderBackingStore* backingStore =
reinterpret_cast<FlutterOpenGLRenderBackingStore*>(
[view_controller_.flutterView backingStoreForSize:size]);
reinterpret_cast<FlutterOpenGLRenderBackingStore*>([view backingStoreForSize:size]);
backing_store_out->open_gl.framebuffer.name = backingStore.frameBufferID;
} else {
FlutterFrameBufferProvider* fb_provider =
Expand Down Expand Up @@ -75,6 +77,13 @@
}

bool FlutterGLCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
// Always gets the first view, #0. After Flutter supports multi-view, it
// should get the view ID from somewhere.
FlutterView* view = GetView(0);
if (!view) {
return false;
}

SetFrameStatus(FrameStatus::kPresenting);

bool has_flutter_content = false;
Expand All @@ -93,7 +102,7 @@

// The surface is an OpenGL texture, which means it has origin in bottom left corner
// and needs to be flipped vertically
InsertCALayerForIOSurface(io_surface, CATransform3DMakeScale(1, -1, 1));
InsertCALayerForIOSurface(view, io_surface, CATransform3DMakeScale(1, -1, 1));
}
has_flutter_content = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#import <Foundation/Foundation.h>
#import <OCMock/OCMock.h>

#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
Expand All @@ -11,10 +12,13 @@
namespace flutter::testing {

TEST(FlutterGLCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController();
id mock_view = OCMClassMock([FlutterView class]);
flutter::FlutterCompositor::ViewProvider get_view_callback = [&mock_view](uint64_t view_id) {
return mock_view;
};

std::unique_ptr<flutter::FlutterGLCompositor> macos_compositor =
std::make_unique<FlutterGLCompositor>(mockViewController, nullptr);
std::make_unique<FlutterGLCompositor>(get_view_callback, nullptr);

bool flag = false;
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace flutter {
class FlutterMetalCompositor : public FlutterCompositor {
public:
explicit FlutterMetalCompositor(
FlutterViewController* view_controller,
ViewProvider get_view_callback,
FlutterPlatformViewController* platform_views_controller,
id<MTLDevice> mtl_device);

Expand Down Expand Up @@ -46,8 +46,11 @@ class FlutterMetalCompositor : public FlutterCompositor {

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

const id<MTLDevice> mtl_device_;
const FlutterPlatformViewController* platform_views_controller_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@
namespace flutter {

FlutterMetalCompositor::FlutterMetalCompositor(
FlutterViewController* view_controller,
ViewProvider get_view_callback,
FlutterPlatformViewController* platform_views_controller,
id<MTLDevice> mtl_device)
: FlutterCompositor(view_controller),
: FlutterCompositor(get_view_callback),
mtl_device_(mtl_device),
platform_views_controller_(platform_views_controller) {}

bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
FlutterBackingStore* backing_store_out) {
if (!view_controller_) {
// Always gets the first view, #0. After Flutter supports multi-view, it
// should get the view ID from somewhere.
FlutterView* view = GetView(0);
if (!view) {
Copy link
Contributor

Choose a reason for hiding this comment

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

When can this happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When the caller sends a view ID that the engine does not recognize. When the engine is run as a whole, this should not happen.

return false;
}

Expand All @@ -34,8 +37,7 @@
// If the backing store is for the first layer, return the MTLTexture for the
// FlutterView.
FlutterMetalRenderBackingStore* backingStore =
reinterpret_cast<FlutterMetalRenderBackingStore*>(
[view_controller_.flutterView backingStoreForSize:size]);
reinterpret_cast<FlutterMetalRenderBackingStore*>([view backingStoreForSize:size]);
backing_store_out->metal.texture.texture =
(__bridge FlutterMetalTextureHandle)backingStore.texture;
} else {
Expand Down Expand Up @@ -78,6 +80,13 @@
}

bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
// Always gets the first view, #0. After Flutter supports multi-view, it
// should get the view ID from somewhere.
FlutterView* view = GetView(0);
if (!view) {
return false;
}

SetFrameStatus(FrameStatus::kPresenting);

bool has_flutter_content = false;
Expand All @@ -91,21 +100,24 @@
FlutterIOSurfaceHolder* io_surface_holder =
(__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data;
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
InsertCALayerForIOSurface(io_surface);
InsertCALayerForIOSurface(view, io_surface);
}
has_flutter_content = true;
break;
}
case kFlutterLayerContentTypePlatformView:
PresentPlatformView(layer, i);
case kFlutterLayerContentTypePlatformView: {
PresentPlatformView(view, layer, i);
break;
}
};
}

return EndFrame(has_flutter_content);
}

void FlutterMetalCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) {
void FlutterMetalCompositor::PresentPlatformView(FlutterView* default_super_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])
Expand All @@ -120,7 +132,7 @@
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) {
[view_controller_.flutterView addSubview:platform_view];
[default_super_view addSubview:platform_view];
}
platform_view.layer.zPosition = layer_position;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,37 @@
// found in the LICENSE file.

#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/FlutterViewControllerTestUtils.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
#import "flutter/testing/testing.h"

namespace flutter::testing {

TEST(FlutterMetalCompositorTest, TestPresent) {
id mockViewController = CreateMockViewController();
flutter::FlutterCompositor::ViewProvider MockGetViewCallback() {
FlutterView* viewMock = OCMClassMock([FlutterView class]);
FlutterMetalRenderBackingStore* backingStoreMock =
OCMClassMock([FlutterMetalRenderBackingStore class]);
__block id<MTLTexture> textureMock = OCMProtocolMock(@protocol(MTLTexture));
OCMStub([backingStoreMock texture]).andReturn(textureMock);

OCMStub([viewMock backingStoreForSize:CGSize{}])
.ignoringNonObjectArgs()
.andDo(^(NSInvocation* invocation) {
CGSize size;
[invocation getArgument:&size atIndex:2];
OCMStub([textureMock width]).andReturn(size.width);
OCMStub([textureMock height]).andReturn(size.height);
})
.andReturn(backingStoreMock);
return [viewMock](uint64_t view_id) { return viewMock; };
}

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

bool flag = false;
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
Expand All @@ -28,12 +46,9 @@
}

TEST(FlutterMetalCompositorTest, TestCreate) {
id mockViewController = CreateMockViewController();
[mockViewController loadView];

std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
MockGetViewCallback(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);

FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
Expand All @@ -50,12 +65,9 @@
}

TEST(FlutterMetalCompositorTest, TestCompositing) {
id mockViewController = CreateMockViewController();
[mockViewController loadView];

std::unique_ptr<flutter::FlutterMetalCompositor> macos_compositor =
std::make_unique<FlutterMetalCompositor>(
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
MockGetViewCallback(), /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);

FlutterBackingStore backing_store;
FlutterBackingStoreConfig config;
Expand Down