Skip to content

Commit 5fa7922

Browse files
committed
[Impeller] Use 32 bit Gaussian function in the 2-pass blur (flutter#42069)
Resolves flutter/flutter#126487. Increases the 2-pass blur quality and distribution limit. It turns out sigma was breaking down beyond ~255 (moderately high, but not unreasonable for users to rely on). The Gaussian function computes sigma^2, and half precision floats only have 5 bit exponents and overflow for numbers above 65k. Coincidentally, this also returns us to a state where we look a lot more like Skia's blurs for larger blur sigmas. Medium blurs have much less visual banding (although it's still there if you look closely). I suspect half precision isn't really enough for tracking the integral. Unfortunately, this means our SIMD pipelining isn't going to be as good. I'll be interested in watching the blur-driven benchmarks for the perf hit. (cherry picked from commit 87a03e1)
1 parent 8cbd2bf commit 5fa7922

File tree

4 files changed

+25
-11
lines changed

4 files changed

+25
-11
lines changed

impeller/compiler/shader_lib/impeller/gaussian.glsl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
#include <impeller/types.glsl>
1010

1111
/// Gaussian distribution function.
12-
float16_t IPGaussian(float16_t x, float16_t sigma) {
12+
float IPGaussian(float x, float sigma) {
13+
float variance = sigma * sigma;
14+
return exp(-0.5f * x * x / variance) / (kSqrtTwoPi * sigma);
15+
}
16+
17+
/// Gaussian distribution function.
18+
float16_t IPHalfGaussian(float16_t x, float16_t sigma) {
1319
float16_t variance = sigma * sigma;
1420
return exp(-0.5hf * x * x / variance) / (float16_t(kSqrtTwoPi) * sigma);
1521
}

impeller/entity/entity_unittests.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -997,7 +997,8 @@ TEST_P(EntityTest, GaussianBlurFilter) {
997997
static Color input_color = Color::Black();
998998
static int selected_blur_type = 0;
999999
static int selected_pass_variation = 0;
1000-
static float blur_amount[2] = {10, 10};
1000+
static float blur_amount_coarse[2] = {0, 0};
1001+
static float blur_amount_fine[2] = {10, 10};
10011002
static int selected_blur_style = 0;
10021003
static int selected_tile_mode = 3;
10031004
static Color cover_color(1, 0, 0, 0.2);
@@ -1027,7 +1028,8 @@ TEST_P(EntityTest, GaussianBlurFilter) {
10271028
pass_variation_names,
10281029
sizeof(pass_variation_names) / sizeof(char*));
10291030
}
1030-
ImGui::SliderFloat2("Sigma", blur_amount, 0, 10);
1031+
ImGui::SliderFloat2("Sigma (coarse)", blur_amount_coarse, 0, 1000);
1032+
ImGui::SliderFloat2("Sigma (fine)", blur_amount_fine, 0, 10);
10311033
ImGui::Combo("Blur style", &selected_blur_style, blur_style_names,
10321034
sizeof(blur_style_names) / sizeof(char*));
10331035
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
@@ -1044,6 +1046,9 @@ TEST_P(EntityTest, GaussianBlurFilter) {
10441046
}
10451047
ImGui::End();
10461048

1049+
auto blur_sigma_x = Sigma{blur_amount_coarse[0] + blur_amount_fine[0]};
1050+
auto blur_sigma_y = Sigma{blur_amount_coarse[1] + blur_amount_fine[1]};
1051+
10471052
std::shared_ptr<Contents> input;
10481053
Size input_size;
10491054

@@ -1071,18 +1076,17 @@ TEST_P(EntityTest, GaussianBlurFilter) {
10711076
std::shared_ptr<FilterContents> blur;
10721077
if (selected_pass_variation == 0) {
10731078
blur = FilterContents::MakeGaussianBlur(
1074-
FilterInput::Make(input), Sigma{blur_amount[0]},
1075-
Sigma{blur_amount[1]}, blur_styles[selected_blur_style],
1076-
tile_modes[selected_tile_mode]);
1079+
FilterInput::Make(input), blur_sigma_x, blur_sigma_y,
1080+
blur_styles[selected_blur_style], tile_modes[selected_tile_mode]);
10771081
} else {
1078-
Vector2 blur_vector(blur_amount[0], blur_amount[1]);
1082+
Vector2 blur_vector(blur_sigma_x.sigma, blur_sigma_y.sigma);
10791083
blur = FilterContents::MakeDirectionalGaussianBlur(
10801084
FilterInput::Make(input), Sigma{blur_vector.GetLength()},
10811085
blur_vector.Normalize());
10821086
}
10831087

10841088
auto mask_blur = FilterContents::MakeBorderMaskBlur(
1085-
FilterInput::Make(input), Sigma{blur_amount[0]}, Sigma{blur_amount[1]},
1089+
FilterInput::Make(input), blur_sigma_x, blur_sigma_y,
10861090
blur_styles[selected_blur_style]);
10871091

10881092
auto ctm = Matrix::MakeScale(GetContentScale()) *

impeller/entity/shaders/gaussian_blur/gaussian_blur.glsl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ uniform BlurInfo {
2727
// host-side, but both are useful controls here. Sigma (pixels per standard
2828
// deviation) is used to define the gaussian function itself, whereas the
2929
// radius is used to limit how much of the function is integrated.
30-
float16_t blur_sigma;
30+
float blur_sigma;
3131
float16_t blur_radius;
3232
}
3333
blur_info;
@@ -62,7 +62,11 @@ void main() {
6262

6363
for (float16_t i = -blur_info.blur_radius; i <= blur_info.blur_radius;
6464
i += 2.0hf) {
65-
float16_t gaussian = IPGaussian(i, blur_info.blur_sigma);
65+
// Use the 32 bit Gaussian function because the 16 bit variation results in
66+
// quality loss/visible banding. Also, 16 bit variation internally breaks
67+
// down at a moderately high (but still reasonable) blur sigma of >255 when
68+
// computing sigma^2 due to the exponent only having 5 bits.
69+
float16_t gaussian = float16_t(IPGaussian(float(i), blur_info.blur_sigma));
6670
gaussian_integral += gaussian;
6771
total_color +=
6872
gaussian * Sample(texture_sampler, // sampler

impeller/entity/shaders/rrect_blur.frag

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ float16_t RRectShadow(f16vec2 sample_position, f16vec2 half_size) {
6262
float16_t y = begin_y + interval * (float16_t(sample_i) + 0.5hf);
6363
result += RRectShadowX(f16vec2(sample_position.x, sample_position.y - y),
6464
half_size) *
65-
IPGaussian(y, frag_info.blur_sigma) * interval;
65+
IPHalfGaussian(y, frag_info.blur_sigma) * interval;
6666
}
6767

6868
return result;

0 commit comments

Comments
 (0)