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

Commit 81f0924

Browse files
authored
PlatformView Blur for Backdrop Filter (#34596)
1 parent 548f978 commit 81f0924

14 files changed

+1204
-7
lines changed

flow/embedded_views.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ class EmbeddedViewParams {
248248
// Clippings are ignored.
249249
const SkRect& finalBoundingRect() const { return final_bounding_rect_; }
250250

251+
// Pushes the stored DlImageFilter object to the mutators stack.
252+
void PushImageFilter(std::shared_ptr<const DlImageFilter> filter) {
253+
mutators_stack_.PushBackdropFilter(filter);
254+
}
255+
251256
// Whether the embedder should construct DisplayList objects to hold the
252257
// rendering commands for each between-view slice of the layer tree.
253258
bool display_list_enabled() const { return display_list_enabled_; }
@@ -439,6 +444,18 @@ class ExternalViewEmbedder {
439444
// 'EndFrame', otherwise returns false.
440445
bool GetUsedThisFrame() const { return used_this_frame_; }
441446

447+
// Pushes the platform view id of a visited platform view to a list of
448+
// visited platform views.
449+
virtual void PushVisitedPlatformView(int64_t view_id) {}
450+
451+
// Pushes a DlImageFilter object to each platform view within a list of
452+
// visited platform views.
453+
//
454+
// See also: |PushVisitedPlatformView| for pushing platform view ids to the
455+
// visited platform views list.
456+
virtual void PushFilterToVisitedPlatformViews(
457+
std::shared_ptr<const DlImageFilter> filter) {}
458+
442459
private:
443460
bool used_this_frame_ = false;
444461

flow/layers/backdrop_filter_layer.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ void BackdropFilterLayer::Preroll(PrerollContext* context,
4343
const SkMatrix& matrix) {
4444
Layer::AutoPrerollSaveLayerState save =
4545
Layer::AutoPrerollSaveLayerState::Create(context, true, bool(filter_));
46+
if (context->view_embedder != nullptr) {
47+
context->view_embedder->PushFilterToVisitedPlatformViews(filter_);
48+
}
4649
SkRect child_paint_bounds = SkRect::MakeEmpty();
4750
PrerollChildren(context, matrix, &child_paint_bounds);
4851
child_paint_bounds.join(context->cull_rect);

flow/layers/platform_view_layer.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ void PlatformViewLayer::Preroll(PrerollContext* context,
2929
context->display_list_enabled);
3030
context->view_embedder->PrerollCompositeEmbeddedView(view_id_,
3131
std::move(params));
32+
context->view_embedder->PushVisitedPlatformView(view_id_);
3233
}
3334

3435
void PlatformViewLayer::Paint(PaintContext& context) const {

flow/mutators_stack_unittests.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ TEST(MutatorsStack, Equality) {
163163
stack.PushClipPath(path);
164164
int alpha = 240;
165165
stack.PushOpacity(alpha);
166+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
167+
stack.PushBackdropFilter(filter);
166168

167169
MutatorsStack stackOther;
168170
SkMatrix matrixOther = SkMatrix::Scale(1, 1);
@@ -175,6 +177,9 @@ TEST(MutatorsStack, Equality) {
175177
stackOther.PushClipPath(otherPath);
176178
int otherAlpha = 240;
177179
stackOther.PushOpacity(otherAlpha);
180+
auto otherFilter =
181+
std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
182+
stackOther.PushBackdropFilter(otherFilter);
178183

179184
ASSERT_TRUE(stack == stackOther);
180185
}
@@ -204,6 +209,11 @@ TEST(Mutator, Initialization) {
204209
int alpha = 240;
205210
Mutator mutator5 = Mutator(alpha);
206211
ASSERT_TRUE(mutator5.GetType() == MutatorType::kOpacity);
212+
213+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
214+
Mutator mutator6 = Mutator(filter);
215+
ASSERT_TRUE(mutator6.GetType() == MutatorType::kBackdropFilter);
216+
ASSERT_TRUE(mutator6.GetFilter() == *filter);
207217
}
208218

209219
TEST(Mutator, CopyConstructor) {
@@ -232,6 +242,11 @@ TEST(Mutator, CopyConstructor) {
232242
Mutator mutator5 = Mutator(alpha);
233243
Mutator copy5 = Mutator(mutator5);
234244
ASSERT_TRUE(mutator5 == copy5);
245+
246+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
247+
Mutator mutator6 = Mutator(filter);
248+
Mutator copy6 = Mutator(mutator6);
249+
ASSERT_TRUE(mutator6 == copy6);
235250
}
236251

237252
TEST(Mutator, Equality) {
@@ -260,6 +275,11 @@ TEST(Mutator, Equality) {
260275
Mutator mutator5 = Mutator(alpha);
261276
Mutator otherMutator5 = Mutator(alpha);
262277
ASSERT_TRUE(mutator5 == otherMutator5);
278+
279+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
280+
Mutator mutator6 = Mutator(filter);
281+
Mutator otherMutator6 = Mutator(filter);
282+
ASSERT_TRUE(mutator6 == otherMutator6);
263283
}
264284

265285
TEST(Mutator, UnEquality) {
@@ -275,6 +295,13 @@ TEST(Mutator, UnEquality) {
275295
Mutator mutator2 = Mutator(alpha);
276296
Mutator otherMutator2 = Mutator(alpha2);
277297
ASSERT_TRUE(mutator2 != otherMutator2);
298+
299+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
300+
auto filter2 =
301+
std::make_shared<DlBlurImageFilter>(10, 10, DlTileMode::kClamp);
302+
Mutator mutator3 = Mutator(filter);
303+
Mutator otherMutator3 = Mutator(filter2);
304+
ASSERT_TRUE(mutator3 != otherMutator3);
278305
}
279306

280307
} // namespace testing

shell/common/shell_test_external_view_embedder.cc

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ SkISize ShellTestExternalViewEmbedder::GetLastSubmittedFrameSize() {
2828
return last_submitted_frame_size_;
2929
}
3030

31+
std::vector<int64_t> ShellTestExternalViewEmbedder::GetVisitedPlatformViews() {
32+
return visited_platform_views_;
33+
}
34+
35+
MutatorsStack ShellTestExternalViewEmbedder::GetStack(int64_t view_id) {
36+
return mutators_stacks_[view_id];
37+
}
38+
3139
// |ExternalViewEmbedder|
3240
void ShellTestExternalViewEmbedder::CancelFrame() {}
3341

@@ -41,7 +49,16 @@ void ShellTestExternalViewEmbedder::BeginFrame(
4149
// |ExternalViewEmbedder|
4250
void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView(
4351
int view_id,
44-
std::unique_ptr<EmbeddedViewParams> params) {}
52+
std::unique_ptr<EmbeddedViewParams> params) {
53+
SkRect view_bounds = SkRect::Make(frame_size_);
54+
std::unique_ptr<EmbedderViewSlice> view;
55+
if (params->display_list_enabled()) {
56+
view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
57+
} else {
58+
view = std::make_unique<SkPictureEmbedderViewSlice>(view_bounds);
59+
}
60+
slices_.insert_or_assign(view_id, std::move(view));
61+
}
4562

4663
// |ExternalViewEmbedder|
4764
PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction(
@@ -62,9 +79,24 @@ ShellTestExternalViewEmbedder::GetCurrentBuilders() {
6279
}
6380

6481
// |ExternalViewEmbedder|
82+
void ShellTestExternalViewEmbedder::PushVisitedPlatformView(int64_t view_id) {
83+
visited_platform_views_.push_back(view_id);
84+
}
85+
86+
// |ExternalViewEmbedder|
87+
void ShellTestExternalViewEmbedder::PushFilterToVisitedPlatformViews(
88+
std::shared_ptr<const DlImageFilter> filter) {
89+
for (int64_t id : visited_platform_views_) {
90+
EmbeddedViewParams params = current_composition_params_[id];
91+
params.PushImageFilter(filter);
92+
current_composition_params_[id] = params;
93+
mutators_stacks_[id] = params.mutatorsStack();
94+
}
95+
}
96+
6597
EmbedderPaintContext ShellTestExternalViewEmbedder::CompositeEmbeddedView(
6698
int view_id) {
67-
return {nullptr, nullptr};
99+
return {slices_[view_id]->canvas(), slices_[view_id]->builder()};
68100
}
69101

70102
// |ExternalViewEmbedder|

shell/common/shell_test_external_view_embedder.h

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

88
#include "flutter/flow/embedded_views.h"
99
#include "flutter/fml/raster_thread_merger.h"
10+
#include "third_party/skia/include/core/SkPictureRecorder.h"
1011

1112
namespace flutter {
1213

@@ -32,9 +33,15 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder {
3233
// the external view embedder.
3334
int GetSubmittedFrameCount();
3435

35-
// Returns the size of last submitted frame surface
36+
// Returns the size of last submitted frame surface.
3637
SkISize GetLastSubmittedFrameSize();
3738

39+
// Returns the mutators stack for the given platform view.
40+
MutatorsStack GetStack(int64_t);
41+
42+
// Returns the list of visited platform views.
43+
std::vector<int64_t> GetVisitedPlatformViews();
44+
3845
private:
3946
// |ExternalViewEmbedder|
4047
void CancelFrame() override;
@@ -64,6 +71,13 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder {
6471
// |ExternalViewEmbedder|
6572
EmbedderPaintContext CompositeEmbeddedView(int view_id) override;
6673

74+
// |ExternalViewEmbedder|
75+
void PushVisitedPlatformView(int64_t view_id) override;
76+
77+
// |ExternalViewEmbedder|
78+
void PushFilterToVisitedPlatformViews(
79+
std::shared_ptr<const DlImageFilter> filter) override;
80+
6781
// |ExternalViewEmbedder|
6882
void SubmitFrame(GrDirectContext* context,
6983
std::unique_ptr<SurfaceFrame> frame) override;
@@ -84,7 +98,11 @@ class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder {
8498
PostPrerollResult post_preroll_result_;
8599

86100
bool support_thread_merging_;
87-
101+
SkISize frame_size_;
102+
std::map<int64_t, std::unique_ptr<EmbedderViewSlice>> slices_;
103+
std::map<int64_t, MutatorsStack> mutators_stacks_;
104+
std::map<int64_t, EmbeddedViewParams> current_composition_params_;
105+
std::vector<int64_t> visited_platform_views_;
88106
std::atomic<int> submitted_frame_count_;
89107
std::atomic<SkISize> last_submitted_frame_size_;
90108

shell/common/shell_unittests.cc

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212

1313
#include "assets/directory_asset_bundle.h"
1414
#include "common/graphics/persistent_cache.h"
15+
#include "flutter/flow/layers/backdrop_filter_layer.h"
1516
#include "flutter/flow/layers/display_list_layer.h"
1617
#include "flutter/flow/layers/layer_raster_cache_item.h"
18+
#include "flutter/flow/layers/platform_view_layer.h"
1719
#include "flutter/flow/layers/transform_layer.h"
1820
#include "flutter/fml/command_line.h"
1921
#include "flutter/fml/dart/dart_converter.h"
@@ -764,12 +766,66 @@ TEST_F(ShellTest, ExternalEmbedderNoThreadMerger) {
764766

765767
PumpOneFrame(shell.get(), 100, 100, builder);
766768
end_frame_latch.Wait();
767-
768769
ASSERT_TRUE(end_frame_called);
769770

770771
DestroyShell(std::move(shell));
771772
}
772773

774+
TEST_F(ShellTest, PushBackdropFilterToVisitedPlatformViews) {
775+
auto settings = CreateSettingsForFixture();
776+
fml::AutoResetWaitableEvent end_frame_latch;
777+
bool end_frame_called = false;
778+
auto end_frame_callback =
779+
[&](bool should_resubmit_frame,
780+
fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger) {
781+
ASSERT_TRUE(raster_thread_merger.get() == nullptr);
782+
ASSERT_FALSE(should_resubmit_frame);
783+
end_frame_called = true;
784+
end_frame_latch.Signal();
785+
};
786+
auto external_view_embedder = std::make_shared<ShellTestExternalViewEmbedder>(
787+
end_frame_callback, PostPrerollResult::kResubmitFrame, false);
788+
auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(),
789+
false, external_view_embedder);
790+
791+
// Create the surface needed by rasterizer
792+
PlatformViewNotifyCreated(shell.get());
793+
794+
auto configuration = RunConfiguration::InferFromSettings(settings);
795+
configuration.SetEntrypoint("emptyMain");
796+
797+
RunEngine(shell.get(), std::move(configuration));
798+
799+
LayerTreeBuilder builder = [&](std::shared_ptr<ContainerLayer> root) {
800+
auto platform_view_layer = std::make_shared<PlatformViewLayer>(
801+
SkPoint::Make(10, 10), SkSize::Make(10, 10), 50);
802+
root->Add(platform_view_layer);
803+
auto filter = std::make_shared<DlBlurImageFilter>(5, 5, DlTileMode::kClamp);
804+
auto backdrop_filter_layer =
805+
std::make_shared<BackdropFilterLayer>(filter, DlBlendMode::kSrcOver);
806+
root->Add(backdrop_filter_layer);
807+
auto platform_view_layer2 = std::make_shared<PlatformViewLayer>(
808+
SkPoint::Make(10, 10), SkSize::Make(10, 10), 75);
809+
backdrop_filter_layer->Add(platform_view_layer2);
810+
};
811+
812+
PumpOneFrame(shell.get(), 100, 100, builder);
813+
end_frame_latch.Wait();
814+
ASSERT_EQ(external_view_embedder->GetVisitedPlatformViews().size(),
815+
(const unsigned long)2);
816+
ASSERT_EQ(external_view_embedder->GetVisitedPlatformViews()[0], 50);
817+
ASSERT_EQ(external_view_embedder->GetVisitedPlatformViews()[1], 75);
818+
ASSERT_TRUE(external_view_embedder->GetStack(75).is_empty());
819+
ASSERT_FALSE(external_view_embedder->GetStack(50).is_empty());
820+
821+
auto filter = DlBlurImageFilter(5, 5, DlTileMode::kClamp);
822+
auto mutator = *external_view_embedder->GetStack(50).Begin();
823+
ASSERT_EQ(mutator->GetType(), MutatorType::kBackdropFilter);
824+
ASSERT_EQ(mutator->GetFilter(), filter);
825+
826+
DestroyShell(std::move(shell));
827+
}
828+
773829
// TODO(https://github.com/flutter/flutter/issues/59816): Enable on fuchsia.
774830
TEST_F(ShellTest,
775831
#if defined(OS_FUCHSIA)

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
3333
@end
3434

3535
namespace flutter {
36+
// Becomes NO if Apple's API changes and blurred backdrop filters cannot be applied.
37+
BOOL canApplyBlurBackdrop = YES;
3638

3739
std::shared_ptr<FlutterPlatformViewLayer> FlutterPlatformViewLayerPool::GetLayer(
3840
GrDirectContext* gr_context,
@@ -318,6 +320,15 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
318320
}
319321
}
320322

323+
void FlutterPlatformViewsController::PushFilterToVisitedPlatformViews(
324+
std::shared_ptr<const DlImageFilter> filter) {
325+
for (int64_t id : visited_platform_views_) {
326+
EmbeddedViewParams params = current_composition_params_[id];
327+
params.PushImageFilter(filter);
328+
current_composition_params_[id] = params;
329+
}
330+
}
331+
321332
void FlutterPlatformViewsController::PrerollCompositeEmbeddedView(
322333
int view_id,
323334
std::unique_ptr<EmbeddedViewParams> params) {
@@ -414,6 +425,8 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
414425
CGRectGetWidth(flutter_view.bounds),
415426
CGRectGetHeight(flutter_view.bounds))] autorelease];
416427

428+
NSMutableArray* blurRadii = [[[NSMutableArray alloc] init] autorelease];
429+
417430
auto iter = mutators_stack.Begin();
418431
while (iter != mutators_stack.End()) {
419432
switch ((*iter)->GetType()) {
@@ -434,11 +447,25 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
434447
case kOpacity:
435448
embedded_view.alpha = (*iter)->GetAlphaFloat() * embedded_view.alpha;
436449
break;
437-
case kBackdropFilter:
450+
case kBackdropFilter: {
451+
// We only support DlBlurImageFilter for BackdropFilter.
452+
if ((*iter)->GetFilter().asBlur() && canApplyBlurBackdrop) {
453+
// sigma_x is arbitrarily chosen as the radius value because Quartz sets
454+
// sigma_x and sigma_y equal to each other. DlBlurImageFilter's Tile Mode
455+
// is not supported in Quartz's gaussianBlur CAFilter, so it is not used
456+
// to blur the PlatformView.
457+
[blurRadii addObject:@((*iter)->GetFilter().asBlur()->sigma_x())];
458+
}
438459
break;
460+
}
439461
}
440462
++iter;
441463
}
464+
465+
if (canApplyBlurBackdrop) {
466+
canApplyBlurBackdrop = [clipView applyBlurBackdropFilters:blurRadii];
467+
}
468+
442469
// Reverse the offset of the clipView.
443470
// The clipView's frame includes the final translate of the final transform matrix.
444471
// So we need to revese this translate so the platform view can layout at the correct offset.
@@ -517,6 +544,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
517544
clip_count_.clear();
518545
views_to_recomposite_.clear();
519546
layer_pool_->RecycleLayers();
547+
visited_platform_views_.clear();
520548
}
521549

522550
SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) {
@@ -777,6 +805,7 @@ - (BOOL)flt_hasFirstResponderInViewHierarchySubtree {
777805
void FlutterPlatformViewsController::ResetFrameState() {
778806
slices_.clear();
779807
composition_order_.clear();
808+
visited_platform_views_.clear();
780809
}
781810

782811
} // namespace flutter

0 commit comments

Comments
 (0)