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

Commit 2b235bb

Browse files
committed
macOS: Fix platform view threading issues
1 parent 1f7fe40 commit 2b235bb

20 files changed

+174
-97
lines changed

shell/platform/darwin/macos/framework/Source/FlutterCompositor.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <list>
1010

1111
#include "flutter/fml/macros.h"
12+
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
1213
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
1314
#include "flutter/shell/platform/embedder/embedder.h"
1415

@@ -19,7 +20,9 @@ namespace flutter {
1920
// Platform views are not yet supported.
2021
class FlutterCompositor {
2122
public:
22-
explicit FlutterCompositor(FlutterViewController* view_controller);
23+
explicit FlutterCompositor(
24+
FlutterViewController* view_controller,
25+
FlutterPlatformViewController* platform_views_controller);
2326

2427
virtual ~FlutterCompositor() = default;
2528

@@ -46,7 +49,9 @@ class FlutterCompositor {
4649
// Present sets frame_started_ to false.
4750
virtual bool Present(const FlutterLayer** layers, size_t layers_count) = 0;
4851

49-
using PresentCallback = std::function<bool(bool has_flutter_content)>;
52+
using PresentCallback =
53+
std::function<bool(bool has_flutter_content,
54+
dispatch_block_t platform_thread_notify)>;
5055

5156
// PresentCallback is called at the end of the Present function.
5257
void SetPresentCallback(const PresentCallback& present_callback);
@@ -73,14 +78,21 @@ class FlutterCompositor {
7378

7479
// Calls the present callback and ensures the frame status is updated
7580
// to frame ended, returning whether the present was successful or not.
76-
bool EndFrame(bool has_flutter_content);
81+
bool EndFrame(bool has_flutter_content, dispatch_block_t on_notify);
82+
83+
void RemoveOldLayers();
7784

7885
// Creates a CALayer object which is backed by the supplied IOSurface, and
7986
// adds it to the root CALayer for this FlutterViewController's view.
8087
void InsertCALayerForIOSurface(
8188
const IOSurfaceRef& io_surface,
89+
size_t layer_position,
8290
CATransform3D transform = CATransform3DIdentity);
8391

92+
// Presents the platform view layer represented by `layer`. `layer_index` is
93+
// used to position the layer in the z-axis.
94+
void PresentPlatformView(const FlutterLayer* layer, size_t layer_index);
95+
8496
private:
8597
// A list of the active CALayer objects for the frame that need to be removed.
8698
std::list<CALayer*> active_ca_layers_;
@@ -92,6 +104,8 @@ class FlutterCompositor {
92104
// Current frame status.
93105
FrameStatus frame_status_ = kEnded;
94106

107+
const FlutterPlatformViewController* platform_views_controller_;
108+
95109
FML_DISALLOW_COPY_AND_ASSIGN(FlutterCompositor);
96110
};
97111

shell/platform/darwin/macos/framework/Source/FlutterCompositor.mm

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
namespace flutter {
99

10-
FlutterCompositor::FlutterCompositor(FlutterViewController* view_controller) {
10+
FlutterCompositor::FlutterCompositor(FlutterViewController* view_controller,
11+
FlutterPlatformViewController* platform_views_controller)
12+
: view_controller_(view_controller), platform_views_controller_(platform_views_controller) {
1113
FML_CHECK(view_controller != nullptr) << "FlutterViewController* cannot be nullptr";
12-
13-
view_controller_ = view_controller;
1414
}
1515

1616
void FlutterCompositor::SetPresentCallback(
@@ -19,18 +19,21 @@
1919
}
2020

2121
void FlutterCompositor::StartFrame() {
22+
SetFrameStatus(FrameStatus::kStarted);
23+
}
24+
25+
void FlutterCompositor::RemoveOldLayers() {
2226
// First remove all CALayers from the superlayer.
2327
for (auto layer : active_ca_layers_) {
2428
[layer removeFromSuperlayer];
2529
}
2630

2731
// Reset active layers.
2832
active_ca_layers_.clear();
29-
SetFrameStatus(FrameStatus::kStarted);
3033
}
3134

32-
bool FlutterCompositor::EndFrame(bool has_flutter_content) {
33-
bool status = present_callback_(has_flutter_content);
35+
bool FlutterCompositor::EndFrame(bool has_flutter_content, dispatch_block_t on_notify) {
36+
bool status = present_callback_(has_flutter_content, on_notify);
3437
SetFrameStatus(FrameStatus::kEnded);
3538
return status;
3639
}
@@ -44,15 +47,34 @@
4447
}
4548

4649
void FlutterCompositor::InsertCALayerForIOSurface(const IOSurfaceRef& io_surface,
50+
size_t layer_position,
4751
CATransform3D transform) {
4852
// FlutterCompositor manages the lifecycle of CALayers.
4953
CALayer* content_layer = [[CALayer alloc] init];
5054
content_layer.transform = transform;
5155
content_layer.frame = view_controller_.flutterView.layer.bounds;
5256
[content_layer setContents:(__bridge id)io_surface];
5357
[view_controller_.flutterView.layer addSublayer:content_layer];
54-
58+
content_layer.zPosition = layer_position;
5559
active_ca_layers_.push_back(content_layer);
5660
}
5761

62+
void FlutterCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) {
63+
FML_DCHECK([[NSThread currentThread] isMainThread])
64+
<< "Must be on the main thread to present platform views";
65+
66+
int64_t platform_view_id = layer->platform_view->identifier;
67+
NSView* platform_view = [platform_views_controller_ platformViewWithID:platform_view_id];
68+
69+
FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;
70+
71+
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
72+
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
73+
layer->size.width / scale, layer->size.height / scale);
74+
if (platform_view.superview == nil) {
75+
[view_controller_.flutterView addSubview:platform_view];
76+
}
77+
platform_view.layer.zPosition = layer_position;
78+
}
79+
5880
} // namespace flutter

shell/platform/darwin/macos/framework/Source/FlutterEngine.mm

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -438,30 +438,32 @@ - (FlutterCompositor*)createFlutterCompositor {
438438
FlutterMetalRenderer* metalRenderer = reinterpret_cast<FlutterMetalRenderer*>(_renderer);
439439
_macOSCompositor = std::make_unique<flutter::FlutterMetalCompositor>(
440440
_viewController, _platformViewController, metalRenderer.device);
441-
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
442-
if (has_flutter_content) {
443-
FlutterMetalRenderer* metalRenderer =
444-
reinterpret_cast<FlutterMetalRenderer*>(weakSelf.renderer);
445-
return [metalRenderer present:0 /*=textureID*/] == YES;
446-
} else {
447-
return true;
448-
}
449-
});
441+
_macOSCompositor->SetPresentCallback(
442+
[weakSelf](bool has_flutter_content, dispatch_block_t platform_thread_notify) {
443+
if (has_flutter_content) {
444+
FlutterMetalRenderer* metalRenderer =
445+
reinterpret_cast<FlutterMetalRenderer*>(weakSelf.renderer);
446+
return [metalRenderer present:0 /*=textureID*/ withBlock:platform_thread_notify] == YES;
447+
} else {
448+
return true;
449+
}
450+
});
450451
} else {
451452
FlutterOpenGLRenderer* openGLRenderer = reinterpret_cast<FlutterOpenGLRenderer*>(_renderer);
452453
[openGLRenderer.openGLContext makeCurrentContext];
453-
_macOSCompositor = std::make_unique<flutter::FlutterGLCompositor>(_viewController,
454-
openGLRenderer.openGLContext);
455-
456-
_macOSCompositor->SetPresentCallback([weakSelf](bool has_flutter_content) {
457-
if (has_flutter_content) {
458-
FlutterOpenGLRenderer* openGLRenderer =
459-
reinterpret_cast<FlutterOpenGLRenderer*>(weakSelf.renderer);
460-
return [openGLRenderer glPresent] == YES;
461-
} else {
462-
return true;
463-
}
464-
});
454+
_macOSCompositor = std::make_unique<flutter::FlutterGLCompositor>(
455+
_viewController, _platformViewController, openGLRenderer.openGLContext);
456+
457+
_macOSCompositor->SetPresentCallback(
458+
[weakSelf](bool has_flutter_content, dispatch_block_t block) {
459+
if (has_flutter_content) {
460+
FlutterOpenGLRenderer* openGLRenderer =
461+
reinterpret_cast<FlutterOpenGLRenderer*>(weakSelf.renderer);
462+
return [openGLRenderer glPresentWithBlock:block] == YES;
463+
} else {
464+
return true;
465+
}
466+
});
465467
}
466468

467469
_compositor = {};

shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ @interface FlutterEngine (Test)
407407
// Latch to ensure the entire layer tree has been generated and presented.
408408
fml::AutoResetWaitableEvent latch;
409409
auto compositor = engine.macOSCompositor;
410-
compositor->SetPresentCallback([&](bool has_flutter_content) {
410+
compositor->SetPresentCallback([&](bool has_flutter_content, dispatch_block_t) {
411411
latch.Signal();
412412
return true;
413413
});

shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace flutter {
2020
class FlutterGLCompositor : public FlutterCompositor {
2121
public:
2222
FlutterGLCompositor(FlutterViewController* view_controller,
23+
FlutterPlatformViewController* platform_views_controller,
2324
NSOpenGLContext* opengl_context);
2425

2526
virtual ~FlutterGLCompositor() = default;

shell/platform/darwin/macos/framework/Source/FlutterGLCompositor.mm

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
namespace flutter {
2121

2222
FlutterGLCompositor::FlutterGLCompositor(FlutterViewController* view_controller,
23+
FlutterPlatformViewController* platform_views_controller,
2324
NSOpenGLContext* opengl_context)
24-
: FlutterCompositor(view_controller), open_gl_context_(opengl_context) {}
25+
: FlutterCompositor(view_controller, platform_views_controller),
26+
open_gl_context_(opengl_context) {}
2527

2628
bool FlutterGLCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
2729
FlutterBackingStore* backing_store_out) {
@@ -79,6 +81,11 @@
7981

8082
bool has_flutter_content = false;
8183

84+
std::vector<dispatch_block_t> actions;
85+
actions.push_back(^{
86+
RemoveOldLayers();
87+
});
88+
8289
for (size_t i = 0; i < layers_count; ++i) {
8390
const auto* layer = layers[i];
8491
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
@@ -91,21 +98,28 @@
9198
FlutterIOSurfaceHolder* io_surface_holder = [backing_store_data ioSurfaceHolder];
9299
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
93100

94-
// The surface is an OpenGL texture, which means it has origin in bottom left corner
95-
// and needs to be flipped vertically
96-
InsertCALayerForIOSurface(io_surface, CATransform3DMakeScale(1, -1, 1));
101+
actions.push_back(^{
102+
// The surface is an OpenGL texture, which means it has origin in bottom left corner
103+
// and needs to be flipped vertically
104+
InsertCALayerForIOSurface(io_surface, i, CATransform3DMakeScale(1, -1, 1));
105+
});
97106
}
98107
has_flutter_content = true;
99108
break;
100109
}
101110
case kFlutterLayerContentTypePlatformView:
102-
// Add functionality in follow up PR.
103-
FML_LOG(WARNING) << "Presenting PlatformViews not yet supported";
111+
actions.push_back(^{
112+
PresentPlatformView(layer, i);
113+
});
104114
break;
105115
};
106116
}
107117

108-
return EndFrame(has_flutter_content);
118+
return EndFrame(has_flutter_content, ^{
119+
for (auto& action : actions) {
120+
action();
121+
}
122+
});
109123
}
110124

111125
} // namespace flutter

shell/platform/darwin/macos/framework/Source/FlutterGLCompositorUnittests.mm

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
id mockViewController = CreateMockViewController();
1515

1616
std::unique_ptr<flutter::FlutterGLCompositor> macos_compositor =
17-
std::make_unique<FlutterGLCompositor>(mockViewController, nullptr);
17+
std::make_unique<FlutterGLCompositor>(mockViewController, nullptr, nullptr);
1818

1919
bool flag = false;
20-
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
21-
*f = true;
22-
return true;
23-
});
20+
macos_compositor->SetPresentCallback(
21+
[f = &flag](bool has_flutter_content, dispatch_block_t on_notify) {
22+
*f = true;
23+
return true;
24+
});
2425

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

shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
#include "flutter/fml/macros.h"
99
#include "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
10-
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
1110

1211
namespace flutter {
1312

@@ -45,12 +44,7 @@ class FlutterMetalCompositor : public FlutterCompositor {
4544
bool Present(const FlutterLayer** layers, size_t layers_count) override;
4645

4746
private:
48-
// Presents the platform view layer represented by `layer`. `layer_index` is
49-
// used to position the layer in the z-axis.
50-
void PresentPlatformView(const FlutterLayer* layer, size_t layer_index);
51-
5247
const id<MTLDevice> mtl_device_;
53-
const FlutterPlatformViewController* platform_views_controller_;
5448

5549
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMetalCompositor);
5650
};

shell/platform/darwin/macos/framework/Source/FlutterMetalCompositor.mm

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@
1414
FlutterViewController* view_controller,
1515
FlutterPlatformViewController* platform_views_controller,
1616
id<MTLDevice> mtl_device)
17-
: FlutterCompositor(view_controller),
18-
mtl_device_(mtl_device),
19-
platform_views_controller_(platform_views_controller) {}
17+
: FlutterCompositor(view_controller, platform_views_controller), mtl_device_(mtl_device) {}
2018

2119
bool FlutterMetalCompositor::CreateBackingStore(const FlutterBackingStoreConfig* config,
2220
FlutterBackingStore* backing_store_out) {
@@ -80,7 +78,13 @@
8078
bool FlutterMetalCompositor::Present(const FlutterLayer** layers, size_t layers_count) {
8179
SetFrameStatus(FrameStatus::kPresenting);
8280

81+
std::vector<dispatch_block_t> actions;
82+
actions.push_back(^{
83+
RemoveOldLayers();
84+
});
85+
8386
bool has_flutter_content = false;
87+
8488
for (size_t i = 0; i < layers_count; ++i) {
8589
const auto* layer = layers[i];
8690
FlutterBackingStore* backing_store = const_cast<FlutterBackingStore*>(layer->backing_store);
@@ -91,38 +95,26 @@
9195
FlutterIOSurfaceHolder* io_surface_holder =
9296
(__bridge FlutterIOSurfaceHolder*)backing_store->metal.texture.user_data;
9397
IOSurfaceRef io_surface = [io_surface_holder ioSurface];
94-
InsertCALayerForIOSurface(io_surface);
98+
actions.push_back(^{
99+
InsertCALayerForIOSurface(io_surface, i);
100+
});
95101
}
96102
has_flutter_content = true;
97103
break;
98104
}
99105
case kFlutterLayerContentTypePlatformView:
100-
PresentPlatformView(layer, i);
106+
actions.push_back(^{
107+
PresentPlatformView(layer, i);
108+
});
101109
break;
102110
};
103111
}
104112

105-
return EndFrame(has_flutter_content);
106-
}
107-
108-
void FlutterMetalCompositor::PresentPlatformView(const FlutterLayer* layer, size_t layer_position) {
109-
// TODO (https://github.com/flutter/flutter/issues/96668)
110-
// once the issue is fixed, this check will pass.
111-
FML_DCHECK([[NSThread currentThread] isMainThread])
112-
<< "Must be on the main thread to present platform views";
113-
114-
int64_t platform_view_id = layer->platform_view->identifier;
115-
NSView* platform_view = [platform_views_controller_ platformViewWithID:platform_view_id];
116-
117-
FML_DCHECK(platform_view) << "Platform view not found for id: " << platform_view_id;
118-
119-
CGFloat scale = [[NSScreen mainScreen] backingScaleFactor];
120-
platform_view.frame = CGRectMake(layer->offset.x / scale, layer->offset.y / scale,
121-
layer->size.width / scale, layer->size.height / scale);
122-
if (platform_view.superview == nil) {
123-
[view_controller_.flutterView addSubview:platform_view];
124-
}
125-
platform_view.layer.zPosition = layer_position;
113+
return EndFrame(has_flutter_content, ^{
114+
for (auto& action : actions) {
115+
action();
116+
}
117+
});
126118
}
127119

128120
} // namespace flutter

shell/platform/darwin/macos/framework/Source/FlutterMetalCompositorUnittests.mm

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
mockViewController, /*platform_view_controller*/ nullptr, /*mtl_device*/ nullptr);
1919

2020
bool flag = false;
21-
macos_compositor->SetPresentCallback([f = &flag](bool has_flutter_content) {
22-
*f = true;
23-
return true;
24-
});
21+
macos_compositor->SetPresentCallback(
22+
[f = &flag](bool has_flutter_content, dispatch_block_t on_notify) {
23+
*f = true;
24+
return true;
25+
});
2526

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

0 commit comments

Comments
 (0)