Skip to content

Commit

Permalink
Reland: [Impeller] Turned on new blur. (flutter#48472) (flutter#49642)
Browse files Browse the repository at this point in the history
This is a reland of the new gaussian blur.

Changes since revert:
1) Textures are now recycled with ping ponging to reduce memory usage
1) Mipmaps are generated to diminish the shimmering that happens with animated blurs (in metal)

---

This new blur should perform faster since it scales down the image before blurring it. Jonah did early testing of it and found it to be faster. Scrolling around with the blur perf bug it seems faster. It also has a wider test bed and is hopefully easier to maintain since it contains all of its logic for both directions.

testing: There are existing blur tests and we've backfilled more as we've added features to this blur.

fixes flutter/flutter#131580 fixes flutter/flutter#138259

https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]:
https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
  • Loading branch information
gaaclarke authored Jan 18, 2024
1 parent 6614de3 commit aede5b0
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 44 deletions.
34 changes: 26 additions & 8 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3758,10 +3758,13 @@ TEST_P(AiksTest, GaussianBlurSetsMipCountOnPass) {
canvas.Restore();

Picture picture = canvas.EndRecordingAsPicture();
EXPECT_EQ(1, picture.pass->GetRequiredMipCount());
EXPECT_EQ(4, picture.pass->GetRequiredMipCount());
}

TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kMetal ? 4 : 1;

Canvas canvas;
canvas.DrawCircle({100, 100}, 50, {.color = Color::CornflowerBlue()});
canvas.SaveLayer({}, std::nullopt,
Expand All @@ -3782,11 +3785,14 @@ TEST_P(AiksTest, GaussianBlurAllocatesCorrectMipCountRenderTarget) {
max_mip_count =
std::max(it->texture->GetTextureDescriptor().mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, 1lu);
EXPECT_EQ(max_mip_count, blur_required_mip_count);
}

TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
fml::testing::LogCapture log_capture;
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kMetal ? 4 : 1;

Canvas canvas;
canvas.DrawPaint({.color = Color::Wheat()});
canvas.SaveLayer({.blend_mode = BlendMode::kMultiply});
Expand All @@ -3809,12 +3815,19 @@ TEST_P(AiksTest, GaussianBlurMipMapNestedLayer) {
max_mip_count =
std::max(it->texture->GetTextureDescriptor().mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, 1lu);
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
EXPECT_EQ(max_mip_count, blur_required_mip_count);
if (GetParam() == PlaygroundBackend::kMetal) {
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
} else {
EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
}
}

TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
size_t blur_required_mip_count =
GetParam() == PlaygroundBackend::kMetal ? 4 : 1;
fml::testing::LogCapture log_capture;
Canvas canvas;
canvas.SaveLayer(
Expand All @@ -3835,9 +3848,14 @@ TEST_P(AiksTest, GaussianBlurMipMapImageFilter) {
max_mip_count =
std::max(it->texture->GetTextureDescriptor().mip_count, max_mip_count);
}
EXPECT_EQ(max_mip_count, 1lu);
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
EXPECT_EQ(max_mip_count, blur_required_mip_count);
if (GetParam() == PlaygroundBackend::kMetal) {
EXPECT_EQ(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
} else {
EXPECT_NE(log_capture.str().find(GaussianBlurFilterContents::kNoMipsError),
std::string::npos);
}
}

} // namespace testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ namespace impeller {
/// - `FilterContents::MakeGaussianBlur`
/// - //flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.glsl
///
///\deprecated Previously 2 of these were chained to do 2D blurs, use
/// \ref GaussianBlurFilterContents instead since it has better
/// performance.
class DirectionalGaussianBlurFilterContents final : public FilterContents {
public:
DirectionalGaussianBlurFilterContents();
Expand Down
40 changes: 4 additions & 36 deletions impeller/entity/contents/filters/filter_contents.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,50 +50,18 @@ std::shared_ptr<FilterContents> FilterContents::MakeDirectionalGaussianBlur(
return blur;
}

#ifdef IMPELLER_ENABLE_NEW_GAUSSIAN_FILTER
const int32_t FilterContents::kBlurFilterRequiredMipCount = 4;
#else
const int32_t FilterContents::kBlurFilterRequiredMipCount = 1;
#endif

std::shared_ptr<FilterContents> FilterContents::MakeGaussianBlur(
const FilterInput::Ref& input,
Sigma sigma_x,
Sigma sigma_y,
BlurStyle blur_style,
Entity::TileMode tile_mode) {
constexpr bool use_new_filter =
#ifdef IMPELLER_ENABLE_NEW_GAUSSIAN_FILTER
true;
#else
false;
#endif

// TODO(https://github.com/flutter/flutter/issues/131580): Remove once the new
// blur handles all cases.
if (use_new_filter) {
auto blur = std::make_shared<GaussianBlurFilterContents>(
sigma_x.sigma, sigma_y.sigma, tile_mode);
blur->SetInputs({input});
return blur;
}
std::shared_ptr<FilterContents> x_blur = MakeDirectionalGaussianBlur(
/*input=*/input,
/*sigma=*/sigma_x,
/*direction=*/Point(1, 0),
/*blur_style=*/BlurStyle::kNormal,
/*tile_mode=*/tile_mode,
/*is_second_pass=*/false,
/*secondary_sigma=*/{});
std::shared_ptr<FilterContents> y_blur = MakeDirectionalGaussianBlur(
/*input=*/FilterInput::Make(x_blur),
/*sigma=*/sigma_y,
/*direction=*/Point(0, 1),
/*blur_style=*/blur_style,
/*tile_mode=*/tile_mode,
/*is_second_pass=*/true,
/*secondary_sigma=*/sigma_x);
return y_blur;
auto blur = std::make_shared<GaussianBlurFilterContents>(
sigma_x.sigma, sigma_y.sigma, tile_mode);
blur->SetInputs({input});
return blur;
}

std::shared_ptr<FilterContents> FilterContents::MakeBorderMaskBlur(
Expand Down
8 changes: 8 additions & 0 deletions impeller/entity/entity_pass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,14 @@ static EntityPassTarget CreateRenderTarget(ContentContext& renderer,
/// What's important is the `StorageMode` of the textures, which cannot be
/// changed for the lifetime of the textures.

if (context->GetBackendType() != Context::BackendType::kMetal) {
// TODO(https://github.com/flutter/flutter/issues/141495): Implement mip map
// generation on vulkan.
// TODO(https://github.com/flutter/flutter/issues/141732): Implement mip map
// generation on opengles.
mip_count = 1;
}

RenderTarget target;
if (context->GetCapabilities()->SupportsOffscreenMSAA()) {
target = RenderTarget::CreateOffscreenMSAA(
Expand Down

0 comments on commit aede5b0

Please sign in to comment.