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

Commit b98df6f

Browse files
committed
[macOS] Implement unobstructed platform views
1 parent 7ffa135 commit b98df6f

13 files changed

+373
-11
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@
5757
info.surface = surface;
5858
info.offset = CGPointMake(layer->offset.x, layer->offset.y);
5959
info.zIndex = i;
60+
FlutterBackingStorePresentInfo* present_info = layer->backing_store_present_info;
61+
if (present_info != nullptr && present_info->paint_region_rects != nullptr) {
62+
info.paintRegion = std::vector<FlutterRect>(
63+
present_info->paint_region_rects,
64+
present_info->paint_region_rects + present_info->paint_region_rects_count);
65+
}
6066
[surfaces addObject:info];
6167
}
6268
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#import <Cocoa/Cocoa.h>
66
#import <QuartzCore/QuartzCore.h>
77

8+
#include <vector>
9+
810
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterSurface.h"
911

1012
/**
@@ -15,6 +17,7 @@
1517
@property(readwrite, strong, nonatomic, nonnull) FlutterSurface* surface;
1618
@property(readwrite, nonatomic) CGPoint offset;
1719
@property(readwrite, nonatomic) size_t zIndex;
20+
@property(readwrite, nonatomic) std::vector<FlutterRect> paintRegion;
1821

1922
@end
2023

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

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ @interface FlutterSurfaceManager () {
2929

3030
// Currently visible layers.
3131
NSMutableArray<CALayer*>* _layers;
32+
33+
NSNumber* _enableSurfaceDebugInfo;
34+
CATextLayer* _infoLayer;
3235
}
3336

3437
/**
@@ -38,6 +41,56 @@ - (void)commit:(NSArray<FlutterSurfacePresentInfo*>*)surfaces;
3841

3942
@end
4043

44+
static NSColor* GetBorderColorForLayer(int layer) {
45+
NSArray* colors = @[
46+
[NSColor yellowColor],
47+
[NSColor cyanColor],
48+
[NSColor magentaColor],
49+
[NSColor greenColor],
50+
[NSColor purpleColor],
51+
[NSColor orangeColor],
52+
[NSColor blueColor],
53+
];
54+
return colors[layer % colors.count];
55+
}
56+
57+
static void UpdateContentSubLayers(CALayer* layer,
58+
IOSurfaceRef surface,
59+
CGFloat scale,
60+
CGSize surfaceSize,
61+
NSColor* borderColor,
62+
const std::vector<FlutterRect>& covered_area) {
63+
while (layer.sublayers.count > covered_area.size()) {
64+
[layer.sublayers.lastObject removeFromSuperlayer];
65+
}
66+
67+
while (layer.sublayers.count < covered_area.size()) {
68+
CALayer* newLayer = [CALayer layer];
69+
[layer addSublayer:newLayer];
70+
}
71+
72+
for (size_t i = 0; i < covered_area.size(); i++) {
73+
CALayer* subLayer = [layer.sublayers objectAtIndex:i];
74+
const auto& rect = covered_area[i];
75+
subLayer.frame = CGRectMake(rect.left / scale, rect.top / scale,
76+
(rect.right - rect.left) / scale, (rect.bottom - rect.top) / scale);
77+
78+
double w = surfaceSize.width;
79+
double h = surfaceSize.height;
80+
81+
subLayer.contentsRect = CGRectMake(rect.left / w, rect.top / h, (rect.right - rect.left) / w,
82+
(rect.bottom - rect.top) / h);
83+
84+
if (borderColor != nil) {
85+
// Visualize sublayer
86+
subLayer.borderColor = borderColor.CGColor;
87+
subLayer.borderWidth = 1.0;
88+
}
89+
90+
subLayer.contents = (__bridge id)surface;
91+
}
92+
}
93+
4194
@implementation FlutterSurfaceManager
4295

4396
- (instancetype)initWithDevice:(id<MTLDevice>)device
@@ -77,6 +130,17 @@ - (FlutterSurface*)surfaceForSize:(CGSize)size {
77130
return surface;
78131
}
79132

133+
- (BOOL)enableSurfaceDebugInfo {
134+
if (_enableSurfaceDebugInfo == nil) {
135+
_enableSurfaceDebugInfo =
136+
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableSurfaceDebugInfo"];
137+
if (_enableSurfaceDebugInfo == nil) {
138+
_enableSurfaceDebugInfo = @NO;
139+
}
140+
}
141+
return [_enableSurfaceDebugInfo boolValue];
142+
}
143+
80144
- (void)commit:(NSArray<FlutterSurfacePresentInfo*>*)surfaces {
81145
FML_DCHECK([NSThread isMainThread]);
82146

@@ -100,16 +164,38 @@ - (void)commit:(NSArray<FlutterSurfacePresentInfo*>*)surfaces {
100164
[_layers addObject:layer];
101165
}
102166

167+
bool enableSurfaceDebugInfo = self.enableSurfaceDebugInfo;
168+
103169
// Update contents of surfaces.
104170
for (size_t i = 0; i < surfaces.count; ++i) {
105171
FlutterSurfacePresentInfo* info = surfaces[i];
106172
CALayer* layer = _layers[i];
107173
CGFloat scale = _containingLayer.contentsScale;
108-
layer.frame = CGRectMake(info.offset.x / scale, info.offset.y / scale,
109-
info.surface.size.width / scale, info.surface.size.height / scale);
110-
layer.contents = (__bridge id)info.surface.ioSurface;
174+
if (i == 0) {
175+
layer.frame = CGRectMake(info.offset.x / scale, info.offset.y / scale,
176+
info.surface.size.width / scale, info.surface.size.height / scale);
177+
layer.contents = (__bridge id)info.surface.ioSurface;
178+
} else {
179+
layer.frame = CGRectZero;
180+
NSColor* borderColor = enableSurfaceDebugInfo ? GetBorderColorForLayer(i - 1) : nil;
181+
UpdateContentSubLayers(layer, info.surface.ioSurface, scale, info.surface.size, borderColor,
182+
info.paintRegion);
183+
}
111184
layer.zPosition = info.zIndex;
112185
}
186+
187+
if (enableSurfaceDebugInfo) {
188+
if (_infoLayer == nil) {
189+
_infoLayer = [[CATextLayer alloc] init];
190+
[_containingLayer addSublayer:_infoLayer];
191+
_infoLayer.fontSize = 15;
192+
_infoLayer.foregroundColor = [NSColor yellowColor].CGColor;
193+
_infoLayer.frame = CGRectMake(15, 15, 300, 100);
194+
_infoLayer.contentsScale = _containingLayer.contentsScale;
195+
_infoLayer.zPosition = 100000;
196+
}
197+
_infoLayer.string = [NSString stringWithFormat:@"Surface count: %li", _layers.count];
198+
}
113199
}
114200

115201
static CGSize GetRequiredFrameSize(NSArray<FlutterSurfacePresentInfo*>* surfaces) {

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

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ - (instancetype)init {
2525
self = [super initWithFrame:NSZeroRect];
2626
if (self) {
2727
[self setWantsLayer:YES];
28+
self.layer.contentsScale = 2.0;
2829
}
2930
return self;
3031
}
@@ -50,11 +51,13 @@ - (void)onPresent:(CGSize)frameSize withBlock:(nonnull dispatch_block_t)block {
5051

5152
static FlutterSurfacePresentInfo* CreatePresentInfo(FlutterSurface* surface,
5253
CGPoint offset = CGPointZero,
53-
size_t index = 0) {
54+
size_t index = 0,
55+
std::vector<FlutterRect> paintRegion = {}) {
5456
FlutterSurfacePresentInfo* res = [[FlutterSurfacePresentInfo alloc] init];
5557
res.surface = surface;
5658
res.offset = offset;
5759
res.zIndex = index;
60+
res.paintRegion = paintRegion;
5861
return res;
5962
}
6063

@@ -160,6 +163,10 @@ - (void)onPresent:(CGSize)frameSize withBlock:(nonnull dispatch_block_t)block {
160163
EXPECT_EQ(surface3, surface1);
161164
}
162165

166+
inline bool operator==(const CGRect& lhs, const CGRect& rhs) {
167+
return CGRectEqualToRect(lhs, rhs);
168+
}
169+
163170
TEST(FlutterSurfaceManager, LayerManagement) {
164171
TestView* testView = [[TestView alloc] init];
165172
FlutterSurfaceManager* surfaceManager = CreateSurfaceManager(testView);
@@ -176,15 +183,68 @@ - (void)onPresent:(CGSize)frameSize withBlock:(nonnull dispatch_block_t)block {
176183
auto surface2_2 = [surfaceManager surfaceForSize:CGSizeMake(20, 20)];
177184
[surfaceManager present:@[
178185
CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
179-
CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2)
186+
CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
187+
{
188+
FlutterRect{0, 0, 20, 20},
189+
FlutterRect{40, 0, 60, 20},
190+
})
180191
]
181192
notify:nil];
182193

183194
EXPECT_EQ(testView.layer.sublayers.count, 2ul);
184-
EXPECT_EQ([testView.layer.sublayers objectAtIndex:0].zPosition, 1.0);
185-
EXPECT_EQ([testView.layer.sublayers objectAtIndex:1].zPosition, 2.0);
195+
EXPECT_EQ(testView.layer.sublayers[0].zPosition, 1.0);
196+
EXPECT_EQ(testView.layer.sublayers[1].zPosition, 2.0);
197+
CALayer* firstOverlaySublayer;
198+
{
199+
NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
200+
EXPECT_EQ(sublayers.count, 2ul);
201+
EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
202+
EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
203+
EXPECT_TRUE(CGRectEqualToRect(sublayers[0].contentsRect, CGRectMake(0, 0, 1, 1)));
204+
EXPECT_TRUE(CGRectEqualToRect(sublayers[1].contentsRect, CGRectMake(2, 0, 1, 1)));
205+
EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
206+
firstOverlaySublayer = sublayers[0];
207+
}
186208
EXPECT_TRUE(CGSizeEqualToSize(testView.presentedFrameSize, CGSizeMake(70, 70)));
187209

210+
// Check second overlay sublayer is removed while first is reused and updated
211+
[surfaceManager present:@[
212+
CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
213+
CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
214+
{
215+
FlutterRect{0, 10, 20, 20},
216+
})
217+
]
218+
notify:nil];
219+
EXPECT_EQ(testView.layer.sublayers.count, 2ul);
220+
{
221+
NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
222+
EXPECT_EQ(sublayers.count, 1ul);
223+
EXPECT_EQ(sublayers[0], firstOverlaySublayer);
224+
EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 5, 10, 5)));
225+
}
226+
227+
// Check that second overlay sublayer is added back while first is reused and updated
228+
[surfaceManager present:@[
229+
CreatePresentInfo(surface2_1, CGPointMake(20, 10), 1),
230+
CreatePresentInfo(surface2_2, CGPointMake(40, 50), 2,
231+
{
232+
FlutterRect{0, 0, 20, 20},
233+
FlutterRect{40, 0, 60, 20},
234+
})
235+
]
236+
notify:nil];
237+
238+
EXPECT_EQ(testView.layer.sublayers.count, 2ul);
239+
{
240+
NSArray<CALayer*>* sublayers = testView.layer.sublayers[1].sublayers;
241+
EXPECT_EQ(sublayers.count, 2ul);
242+
EXPECT_EQ(sublayers[0], firstOverlaySublayer);
243+
EXPECT_TRUE(CGRectEqualToRect(sublayers[0].frame, CGRectMake(0, 0, 10, 10)));
244+
EXPECT_TRUE(CGRectEqualToRect(sublayers[1].frame, CGRectMake(20, 0, 10, 10)));
245+
EXPECT_EQ(sublayers[0].contents, sublayers[1].contents);
246+
}
247+
188248
auto surface3_1 = [surfaceManager surfaceForSize:CGSizeMake(50, 30)];
189249
[surfaceManager present:@[ CreatePresentInfo(surface3_1, CGPointMake(20, 10)) ] notify:nil];
190250

shell/platform/embedder/embedder.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,18 @@ typedef enum {
15681568
kFlutterLayerContentTypePlatformView,
15691569
} FlutterLayerContentType;
15701570

1571+
typedef struct {
1572+
size_t struct_size;
1573+
1574+
/// The area of the backing store that contains Flutter contents. Pixels
1575+
/// outside of this area are transparent and the embedder may choose not
1576+
/// to render them. Coordinates are in physical pixels.
1577+
FlutterRect* paint_region_rects;
1578+
1579+
/// Number of rectangles in the paint_region_rects array.
1580+
size_t paint_region_rects_count;
1581+
} FlutterBackingStorePresentInfo;
1582+
15711583
typedef struct {
15721584
/// This size of this struct. Must be sizeof(FlutterLayer).
15731585
size_t struct_size;
@@ -1587,6 +1599,10 @@ typedef struct {
15871599
FlutterPoint offset;
15881600
/// The size of the layer (in physical pixels).
15891601
FlutterSize size;
1602+
1603+
/// Extra information for the backing store that the embedder may
1604+
/// used during presentation.
1605+
FlutterBackingStorePresentInfo* backing_store_present_info;
15901606
} FlutterLayer;
15911607

15921608
typedef bool (*FlutterBackingStoreCreateCallback)(

shell/platform/embedder/embedder_external_view.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ bool EmbedderExternalView::HasPlatformView() const {
6262
return view_identifier_.platform_view_id.has_value();
6363
}
6464

65+
std::list<SkRect> EmbedderExternalView::GetDrawnRects(
66+
const SkRect& query) const {
67+
return slice_->searchNonOverlappingDrawnRects(query);
68+
}
69+
6570
bool EmbedderExternalView::HasEngineRenderedContents() {
6671
if (has_engine_rendered_contents_.has_value()) {
6772
return has_engine_rendered_contents_.value();

shell/platform/embedder/embedder_external_view.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class EmbedderExternalView {
109109

110110
bool Render(const EmbedderRenderTarget& render_target);
111111

112+
std::list<SkRect> GetDrawnRects(const SkRect& query) const;
113+
112114
private:
113115
// End the recording of the slice.
114116
// Noop if the slice's recording has already ended.

shell/platform/embedder/embedder_external_view_embedder.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,15 @@ void EmbedderExternalViewEmbedder::SubmitFrame(
238238
// platform view.
239239
if (external_view->HasEngineRenderedContents()) {
240240
const auto& exteral_render_target = matched_render_targets.at(view_id);
241+
const auto& external_view = pending_views_.at(view_id);
242+
auto rect_list = external_view->GetDrawnRects(SkRect::MakeIWH(
243+
pending_frame_size_.width(), pending_frame_size_.height()));
244+
std::vector<SkIRect> rects;
245+
for (const auto& rect : rect_list) {
246+
rects.push_back(rect.roundOut());
247+
}
241248
presented_layers.PushBackingStoreLayer(
242-
exteral_render_target->GetBackingStore());
249+
exteral_render_target->GetBackingStore(), rects);
243250
}
244251
}
245252

shell/platform/embedder/embedder_layers.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ EmbedderLayers::EmbedderLayers(SkISize frame_size,
1717

1818
EmbedderLayers::~EmbedderLayers() = default;
1919

20-
void EmbedderLayers::PushBackingStoreLayer(const FlutterBackingStore* store) {
20+
void EmbedderLayers::PushBackingStoreLayer(
21+
const FlutterBackingStore* store,
22+
const std::vector<SkIRect>& paint_region) {
2123
FlutterLayer layer = {};
2224

2325
layer.struct_size = sizeof(FlutterLayer);
@@ -35,6 +37,29 @@ void EmbedderLayers::PushBackingStoreLayer(const FlutterBackingStore* store) {
3537
layer.size.width = transformed_layer_bounds.width();
3638
layer.size.height = transformed_layer_bounds.height();
3739

40+
auto paint_region_rects = std::make_unique<std::vector<FlutterRect>>();
41+
paint_region_rects->reserve(paint_region.size());
42+
43+
for (const auto& rect : paint_region) {
44+
auto transformed_rect =
45+
root_surface_transformation_.mapRect(SkRect::Make(rect));
46+
paint_region_rects->push_back(FlutterRect{
47+
transformed_rect.x(),
48+
transformed_rect.y(),
49+
transformed_rect.right(),
50+
transformed_rect.bottom(),
51+
});
52+
}
53+
54+
auto present_info = std::make_unique<FlutterBackingStorePresentInfo>();
55+
present_info->struct_size = sizeof(FlutterBackingStorePresentInfo);
56+
present_info->paint_region_rects = paint_region_rects->data();
57+
present_info->paint_region_rects_count = paint_region_rects->size();
58+
paint_region_rects_referenced_.push_back(std::move(paint_region_rects));
59+
60+
layer.backing_store_present_info = present_info.get();
61+
present_info_refereced_.push_back(std::move(present_info));
62+
3863
presented_layers_.push_back(layer);
3964
}
4065

shell/platform/embedder/embedder_layers.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class EmbedderLayers {
2424

2525
~EmbedderLayers();
2626

27-
void PushBackingStoreLayer(const FlutterBackingStore* store);
27+
void PushBackingStoreLayer(const FlutterBackingStore* store,
28+
const std::vector<SkIRect>& drawn_region);
2829

2930
void PushPlatformViewLayer(FlutterPlatformViewIdentifier identifier,
3031
const EmbeddedViewParams& params);
@@ -42,6 +43,10 @@ class EmbedderLayers {
4243
mutations_referenced_;
4344
std::vector<std::unique_ptr<std::vector<const FlutterPlatformViewMutation*>>>
4445
mutations_arrays_referenced_;
46+
std::vector<std::unique_ptr<FlutterBackingStorePresentInfo>>
47+
present_info_refereced_;
48+
std::vector<std::unique_ptr<std::vector<FlutterRect>>>
49+
paint_region_rects_referenced_;
4550
std::vector<FlutterLayer> presented_layers_;
4651

4752
FML_DISALLOW_COPY_AND_ASSIGN(EmbedderLayers);

0 commit comments

Comments
 (0)