Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
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
12 changes: 9 additions & 3 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class FlutterCompositor {

// 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)>;
using PresentCallback =
std::function<bool(bool has_flutter_content,
dispatch_block_t platfrom_thread_notify)>;

// Registers a callback to be triggered at the end of the Present function.
// If a callback was previously registered, it will be replaced.
Expand All @@ -85,18 +87,22 @@ class FlutterCompositor {
void SetFrameStatus(FrameStatus frame_status);
FrameStatus GetFrameStatus();

// Clears the previous CALayers and updates the frame status to frame started.
// Sets frame status to frame started.
void StartFrame();

// Clears the previous CALayers.
void RemoveOldLayers();

// Calls the present callback and ensures the frame status is updated
// to frame ended, returning whether the present was successful or not.
bool EndFrame(bool has_flutter_content);
bool EndFrame(bool has_flutter_content, dispatch_block_t on_notify);

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

private:
Expand Down
32 changes: 25 additions & 7 deletions shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@
SetFrameStatus(FrameStatus::kPresenting);

bool has_flutter_content = false;

std::vector<dispatch_block_t> actions;
actions.push_back(^{
RemoveOldLayers();
});

for (size_t i = 0; i < layers_count; ++i) {
const auto* layer = layers[i];
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
Expand All @@ -100,19 +106,27 @@
FlutterIOSurfaceHolder* io_surface_holder =
(__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data;
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
InsertCALayerForIOSurface(view, io_surface);
actions.push_back(^{
InsertCALayerForIOSurface(view, io_surface, i);
});
}
has_flutter_content = true;
break;
}
case kFlutterLayerContentTypePlatformView: {
PresentPlatformView(view, layer, i);
actions.push_back(^{
PresentPlatformView(view, layer, i);
});
break;
}
};
}

return EndFrame(has_flutter_content);
return EndFrame(has_flutter_content, ^{
for (auto& action : actions) {
action();
}
});
}

void FlutterCompositor::PresentPlatformView(FlutterView* default_base_view,
Expand Down Expand Up @@ -143,18 +157,21 @@
}

void FlutterCompositor::StartFrame() {
SetFrameStatus(FrameStatus::kStarted);
}

void FlutterCompositor::RemoveOldLayers() {
// First remove all CALayers from the superlayer.
for (auto layer : active_ca_layers_) {
[layer removeFromSuperlayer];
}

// Reset active layers.
active_ca_layers_.clear();
SetFrameStatus(FrameStatus::kStarted);
}

bool FlutterCompositor::EndFrame(bool has_flutter_content) {
bool status = present_callback_(has_flutter_content);
bool FlutterCompositor::EndFrame(bool has_flutter_content, dispatch_block_t on_notify) {
bool status = present_callback_(has_flutter_content, on_notify);
SetFrameStatus(FrameStatus::kEnded);
return status;
}
Expand All @@ -173,14 +190,15 @@

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

content_layer.zPosition = layer_position;
active_ca_layers_.push_back(content_layer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ - (nullable FlutterView*)getView:(uint64_t)viewId {
/*mtl_device*/ nullptr);

bool flag = false;
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content, dispatch_block_t) {
*f = true;
return true;
});
Expand Down
25 changes: 13 additions & 12 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -423,18 +423,19 @@ - (FlutterCompositor*)createFlutterCompositor {

_macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
_viewProvider, _platformViewController, _renderer.device);
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
// TODO(dkwingsmt): The compositor only supports single-view for now. As
// more classes are gradually converted to multi-view, it should get the
// view ID from somewhere.
uint64_t viewId = kFlutterDefaultViewId;
if (has_flutter_content) {
return [weakSelf.renderer present:viewId] == YES;
} else {
[weakSelf.renderer presentWithoutContent:viewId];
return true;
}
});
_macOSCompositor->SetPresentCallback(
[weakSelf](bool has_flutter_content, dispatch_block_t on_notify) {
// TODO(dkwingsmt): The compositor only supports single-view for now. As
// more classes are gradually converted to multi-view, it should get the
// view ID from somewhere.
uint64_t viewId = kFlutterDefaultViewId;
if (has_flutter_content) {
return [weakSelf.renderer present:viewId withBlock:on_notify] == YES;
} else {
[weakSelf.renderer presentWithoutContent:viewId];
return true;
}
});

_compositor = {};
_compositor.struct_size = sizeof(FlutterCompositor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ @interface FlutterEngine (Test)
// Latch to ensure the entire layer tree has been generated and presented.
fml::AutoResetWaitableEvent latch;
auto compositor = engine.macOSCompositor;
compositor->SetPresentCallback([&](bool has_flutter_content) {
compositor->SetPresentCallback([&](bool has_flutter_content, dispatch_block_t completion) {
latch.Signal();
return true;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
/**
* Called by the engine when the given view's buffers should be swapped.
*/
- (BOOL)present:(uint64_t)viewId;
- (BOOL)present:(uint64_t)viewId withBlock:(nullable dispatch_block_t)block;

/**
* Tells the renderer that there is no Flutter content available for this frame.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static bool OnPresentDrawableOfDefaultView(FlutterEngine* engine,
// operates on the default view. To support multi-view, we need a new callback
// that also receives a view ID.
uint64_t viewId = kFlutterDefaultViewId;
return [engine.renderer present:viewId];
return [engine.renderer present:viewId withBlock:nil];
}

static bool OnAcquireExternalTexture(FlutterEngine* engine,
Expand Down Expand Up @@ -104,12 +104,12 @@ - (FlutterMetalTexture)createTextureForView:(uint64_t)viewId size:(CGSize)size {
return embedderTexture;
}

- (BOOL)present:(uint64_t)viewId {
- (BOOL)present:(uint64_t)viewId withBlock:(dispatch_block_t)block {
FlutterView* view = [_viewProvider getView:viewId];
if (view == nil) {
return NO;
}
[view present];
[view presentWithBlock:block];
return YES;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ void SetEngineDefaultView(FlutterEngine* engine, id flutterView) {
FlutterRenderer* renderer = [[FlutterRenderer alloc] initWithFlutterEngine:engine];
id mockFlutterView = OCMClassMock([FlutterView class]);
SetEngineDefaultView(engine, mockFlutterView);
[(FlutterView*)[mockFlutterView expect] present];
[renderer present:kFlutterDefaultViewId];
[(FlutterView*)[mockFlutterView expect] presentWithBlock:nil];
[renderer present:kFlutterDefaultViewId withBlock:nil];
}

TEST(FlutterRenderer, TextureReturnedByFlutterView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@
*/
- (void)requestCommit;

/**
* Called from rasterizer thread, will block until delegate resizeSynchronizerCommit:
* method is called (on platform thread).
*
* The onCommit block will be invoked on platform thread during the core
* animation transaction.
*/
- (void)requestCommitWithBlock:(nullable dispatch_block_t)onCommit;

/**
* Called from view to notify the synchronizer that there are no Flutter frames
* coming. Synchronizer must unblock main thread and not block until another
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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

#import <QuartzCore/QuartzCore.h>
#include <mutex>

@interface FlutterResizeSynchronizer () {
Expand Down Expand Up @@ -38,6 +39,8 @@ @interface FlutterResizeSynchronizer () {
BOOL _shuttingDown;

__weak id<FlutterResizeSynchronizerDelegate> _delegate;

dispatch_block_t _onCommit;
}
@end

Expand Down Expand Up @@ -83,7 +86,16 @@ - (void)beginResize:(CGSize)size notify:(dispatch_block_t)notify {
_condBlockRequestCommit.wait(lock, [&] { return _pendingCommit || _shuttingDown; });

[_delegate resizeSynchronizerFlush:self];

[CATransaction begin];
[CATransaction setDisableActions:YES];
[_delegate resizeSynchronizerCommit:self];
if (_onCommit) {
_onCommit();
}
_onCommit = nil;
[CATransaction commit];

_pendingCommit = NO;
_condBlockBeginResize.notify_all();

Expand All @@ -106,6 +118,10 @@ - (BOOL)shouldEnsureSurfaceForSize:(CGSize)size {
}

- (void)requestCommit {
[self requestCommitWithBlock:nil];
}

- (void)requestCommitWithBlock:(dispatch_block_t)onCommit {
std::unique_lock<std::mutex> lock(_mutex);

if (!_acceptingCommit || _shuttingDown) {
Expand All @@ -116,18 +132,25 @@ - (void)requestCommit {

_pendingCommit = YES;
if (_waiting) { // BeginResize is in progress, interrupt it and schedule commit call
_onCommit = onCommit;
_condBlockRequestCommit.notify_all();
_condBlockBeginResize.wait(lock, [&]() { return !_pendingCommit || _shuttingDown; });
} else {
// No resize, schedule commit on platform thread and wait until either done
// or interrupted by incoming BeginResize
[_delegate resizeSynchronizerFlush:self];
dispatch_async(dispatch_get_main_queue(), [self, cookie = _cookie] {
dispatch_async(dispatch_get_main_queue(), [self, cookie = _cookie, onCommit] {
std::unique_lock<std::mutex> lock(_mutex);
if (cookie == _cookie) {
[CATransaction begin];
[CATransaction setDisableActions:YES];
if (_delegate) {
[_delegate resizeSynchronizerCommit:self];
}
if (onCommit) {
onCommit();
}
[CATransaction commit];
_pendingCommit = NO;
_condBlockBeginResize.notify_all();
}
Expand Down
8 changes: 6 additions & 2 deletions shell/platform/darwin/macos/framework/Source/FlutterView.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ constexpr uint64_t kFlutterDefaultViewId = 0;
- (nonnull instancetype)init NS_UNAVAILABLE;

/**
* Flushes the graphics context and flips the surfaces. Expected to be called on raster thread.
* Flushes the OpenGL context and flips the surfaces. Expected to be called
* on raster thread. Blocks until the operation is complete.
*
* The onCommit block will be invoked on platform thread during the core
* animation transaction;
*/
- (void)present;
- (void)presentWithBlock:(nullable dispatch_block_t)onCommit;

/**
* Called when there is no Flutter content available to render. This must be passed to resize
Expand Down
4 changes: 2 additions & 2 deletions shell/platform/darwin/macos/framework/Source/FlutterView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ - (FlutterRenderBackingStore*)backingStoreForSize:(CGSize)size {
return [_resizableBackingStoreProvider backingStore];
}

- (void)present {
[_resizeSynchronizer requestCommit];
- (void)presentWithBlock:(dispatch_block_t)onCommit {
[_resizeSynchronizer requestCommitWithBlock:onCommit];
}

- (void)presentWithoutContent {
Expand Down