Skip to content

Commit

Permalink
[Impeller] new blur: clamp downsample scalar to 1/16 (#50262)
Browse files Browse the repository at this point in the history
fixes flutter/flutter#142753

The theory here is that once you start throwing away more than 15/16th of the signal it's pretty noticable.  By looking at the blur under different circumstances that seems like a reasonable limit.

There is no automated test for this.  Doing so would be quite involved and would involve evaluating multiple rendered frames.  There is a manual test.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
gaaclarke authored Feb 2, 2024
1 parent fee0214 commit 7c7631b
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 5 deletions.
4 changes: 4 additions & 0 deletions impeller/aiks/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ template("aiks_unittests_component") {
testonly = true
if (defined(invoker.defines)) {
defines = invoker.defines
} else {
defines = []
}
defines += [ "_USE_MATH_DEFINES" ]

sources = predefined_sources + additional_sources
deps = [
":aiks",
Expand Down
39 changes: 39 additions & 0 deletions impeller/aiks/aiks_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4024,5 +4024,44 @@ TEST_P(AiksTest, GaussianBlurBackdropTinyMipMap) {
}
}

TEST_P(AiksTest, GaussianBlurAnimatedBackdrop) {
// This test is for checking out how stable rendering is when content is
// translated underneath a blur. Animating under a blur can cause
// *shimmering* to happen as a result of pixel alignment.
// See also: https://github.com/flutter/flutter/issues/140193
auto boston = std::make_shared<Image>(
CreateTextureForFixture("boston.jpg", /*enable_mipmapping=*/true));
ASSERT_TRUE(boston);
int64_t count = 0;
Scalar sigma = 20.0;
Scalar freq = 0.1;
Scalar amp = 50.0;
auto callback = [&](AiksContext& renderer) -> std::optional<Picture> {
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
{
ImGui::SliderFloat("Sigma", &sigma, 0, 200);
ImGui::SliderFloat("Frequency", &freq, 0.01, 2.0);
ImGui::SliderFloat("Amplitude", &amp, 1, 100);
}
ImGui::End();

Canvas canvas;
canvas.Scale(GetContentScale());
Scalar y = amp * sin(freq * 2.0 * M_PI * count / 60);
canvas.DrawImage(boston,
Point(1024 / 2 - boston->GetSize().width / 2,
(768 / 2 - boston->GetSize().height / 2) + y),
{});
canvas.ClipRect(Rect::MakeLTRB(100, 100, 900, 700));
canvas.SaveLayer({.blend_mode = BlendMode::kSource}, std::nullopt,
ImageFilter::MakeBlur(Sigma(sigma), Sigma(sigma),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kClamp));
count += 1;
return canvas.EndRecordingAsPicture();
};
ASSERT_TRUE(OpenPlaygroundHere(callback));
}

} // namespace testing
} // namespace impeller
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,10 @@ Scalar GaussianBlurFilterContents::CalculateScale(Scalar sigma) {
}
Scalar result = 4.0 / sigma;
// Round to the nearest 1/(2^n) to get the best quality down scaling.
Scalar rounded = pow(2.0f, round(log2(result)));
Scalar exponent = round(log2f(result));
// Don't scale down below 1/16th to preserve signal.
exponent = std::max(-4.0f, exponent);
Scalar rounded = powf(2.0f, exponent);
return rounded;
};

Expand Down Expand Up @@ -454,8 +457,8 @@ KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
KernelPipeline::FragmentShader::KernelSamples result;
result.sample_count =
((2 * parameters.blur_radius) / parameters.step_size) + 1;
// 32 comes from kernel.glsl.
FML_CHECK(result.sample_count < 32);
// 48 comes from kernel.glsl.
FML_CHECK(result.sample_count < 48);

// Chop off the last samples if the radius >= 3 where they account for < 1.56%
// of the result.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ TEST(GaussianBlurFilterContentsTest, CalculateSigmaValues) {
EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(3.0f), 1);
EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(4.0f), 1);
EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(16.0f), 0.25);
EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(1024.0f), 4.f / 1024.f);
// Downsample clamped to 1/16th.
EXPECT_EQ(GaussianBlurFilterContents::CalculateScale(1024.0f), 0.0625);
}

TEST_P(GaussianBlurFilterContentsTest, RenderCoverageMatchesGetCoverage) {
Expand Down
2 changes: 1 addition & 1 deletion impeller/entity/shaders/gaussian_blur/kernel.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct KernelSample {

uniform KernelSamples {
int sample_count;
KernelSample samples[32];
KernelSample samples[48];
}
blur_info;

Expand Down
1 change: 1 addition & 0 deletions impeller/golden_tests/golden_playground_test_mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static const std::vector<std::string> kSkipTests = {
IMP_AIKSTEST(SceneColorSource),
IMP_AIKSTEST(SolidStrokesRenderCorrectly),
IMP_AIKSTEST(TextFrameSubpixelAlignment),
IMP_AIKSTEST(GaussianBlurAnimatedBackdrop),
// TextRotated is flakey and we can't seem to get it to stabilize on Skia
// Gold.
IMP_AIKSTEST(TextRotated),
Expand Down

0 comments on commit 7c7631b

Please sign in to comment.