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

Commit f60d9a9

Browse files
authored
[Impeller] migrate blur to calculating coefficients on the cpu (#49512)
issue: flutter/flutter#131580 This factors out the gaussian blur fragment shader to be a more generic kernel fragment shader. We can't use this for the old gaussian blur because it's kernel is unbounded in one dimension. I implemented this by passing in the coefficients into the UBO so it can't support that. Profile after change: ``` "average_vsync_transitions_missed": 2.0, "90th_percentile_vsync_transitions_missed": 2.0, "99th_percentile_vsync_transitions_missed": 2.0, "average_vsync_frame_lag": 0.0, "90th_percentile_vsync_frame_lag": 0.0, "99th_percentile_vsync_frame_lag": 0.0, "average_layer_cache_count": 0.0, "90th_percentile_layer_cache_count": 0.0, "99th_percentile_layer_cache_count": 0.0, "average_frame_request_pending_latency": 16636.624373956594, "90th_percentile_frame_request_pending_latency": 16718.0, "99th_percentile_frame_request_pending_latency": 16761.0, "worst_layer_cache_count": 0.0, "average_layer_cache_memory": 0.0, "90th_percentile_layer_cache_memory": 0.0, "99th_percentile_layer_cache_memory": 0.0, "worst_layer_cache_memory": 0.0, "average_picture_cache_count": 0.0, "90th_percentile_picture_cache_count": 0.0, "99th_percentile_picture_cache_count": 0.0, "worst_picture_cache_count": 0.0, "average_picture_cache_memory": 0.0, "90th_percentile_picture_cache_memory": 0.0, "99th_percentile_picture_cache_memory": 0.0, "worst_picture_cache_memory": 0.0, "total_ui_gc_time": 1.65, "30hz_frame_percentage": 0.0, "60hz_frame_percentage": 100.0, "80hz_frame_percentage": 0.0, "90hz_frame_percentage": 0.0, "120hz_frame_percentage": 0.0, "illegal_refresh_rate_frame_count": 0, "average_gpu_frame_time": 21.875, "90th_percentile_gpu_frame_time": 31.25, "99th_percentile_gpu_frame_time": 31.25, "worst_gpu_frame_time": 31.25, "average_cpu_usage": 162.86326532653064, "90th_percentile_cpu_usage": 167.3, "99th_percentile_cpu_usage": 168.500003, "average_gpu_usage": 93.26530612244898, "90th_percentile_gpu_usage": 94.0, "99th_percentile_gpu_usage": 95.0, "average_memory_usage": 113.56696428571429, "90th_percentile_memory_usage": 145.484375, "99th_percentile_memory_usage": 152.75 ``` This is a 13% (21.25 / 25 ) drop in `average_gpu_frame_time` from the last profile in #49299. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 0bbb4d6 commit f60d9a9

File tree

11 files changed

+563
-27
lines changed

11 files changed

+563
-27
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5231,6 +5231,9 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.gls
52315231
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.vert + ../../../flutter/LICENSE
52325232
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag + ../../../flutter/LICENSE
52335233
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag + ../../../flutter/LICENSE
5234+
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel.glsl + ../../../flutter/LICENSE
5235+
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_decal.frag + ../../../flutter/LICENSE
5236+
ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag + ../../../flutter/LICENSE
52345237
ORIGIN: ../../../flutter/impeller/entity/shaders/geometry/points.comp + ../../../flutter/LICENSE
52355238
ORIGIN: ../../../flutter/impeller/entity/shaders/geometry/uv.comp + ../../../flutter/LICENSE
52365239
ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag + ../../../flutter/LICENSE
@@ -8041,6 +8044,9 @@ FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.glsl
80418044
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur.vert
80428045
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag
80438046
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag
8047+
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel.glsl
8048+
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_decal.frag
8049+
FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/kernel_nodecal.frag
80448050
FILE: ../../../flutter/impeller/entity/shaders/geometry/points.comp
80458051
FILE: ../../../flutter/impeller/entity/shaders/geometry/uv.comp
80468052
FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag

impeller/entity/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ impeller_shaders("entity_shaders") {
2828
"shaders/gaussian_blur/gaussian_blur.vert",
2929
"shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag",
3030
"shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag",
31+
"shaders/gaussian_blur/kernel_decal.frag",
32+
"shaders/gaussian_blur/kernel_nodecal.frag",
3133
"shaders/glyph_atlas.frag",
3234
"shaders/glyph_atlas_color.frag",
3335
"shaders/glyph_atlas.vert",
@@ -245,6 +247,7 @@ impeller_component("entity") {
245247
}
246248

247249
deps = [ "//flutter/fml" ]
250+
defines = [ "_USE_MATH_DEFINES" ]
248251
}
249252

250253
impeller_component("entity_test_helpers") {

impeller/entity/contents/content_context.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ ContentContext::ContentContext(
336336
options_trianglestrip);
337337
gaussian_blur_noalpha_nodecal_pipelines_.CreateDefault(*context_,
338338
options_trianglestrip);
339+
kernel_decal_pipelines_.CreateDefault(*context_, options_trianglestrip);
340+
kernel_nodecal_pipelines_.CreateDefault(*context_, options_trianglestrip);
339341
border_mask_blur_pipelines_.CreateDefault(*context_, options_trianglestrip);
340342
morphology_filter_pipelines_.CreateDefault(*context_, options_trianglestrip,
341343
{supports_decal});

impeller/entity/contents/content_context.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
#include "impeller/entity/gaussian_blur.vert.h"
6868
#include "impeller/entity/gaussian_blur_noalpha_decal.frag.h"
6969
#include "impeller/entity/gaussian_blur_noalpha_nodecal.frag.h"
70+
#include "impeller/entity/kernel_decal.frag.h"
71+
#include "impeller/entity/kernel_nodecal.frag.h"
7072

7173
#include "impeller/entity/position_color.vert.h"
7274

@@ -137,6 +139,10 @@ using GaussianBlurDecalPipeline =
137139
using GaussianBlurPipeline =
138140
RenderPipelineT<GaussianBlurVertexShader,
139141
GaussianBlurNoalphaNodecalFragmentShader>;
142+
using KernelDecalPipeline =
143+
RenderPipelineT<GaussianBlurVertexShader, KernelDecalFragmentShader>;
144+
using KernelPipeline =
145+
RenderPipelineT<GaussianBlurVertexShader, KernelNodecalFragmentShader>;
140146
using BorderMaskBlurPipeline =
141147
RenderPipelineT<BorderMaskBlurVertexShader, BorderMaskBlurFragmentShader>;
142148
using MorphologyFilterPipeline =
@@ -447,6 +453,16 @@ class ContentContext {
447453
return GetPipeline(gaussian_blur_noalpha_nodecal_pipelines_, opts);
448454
}
449455

456+
std::shared_ptr<Pipeline<PipelineDescriptor>> GetKernelDecalPipeline(
457+
ContentContextOptions opts) const {
458+
return GetPipeline(kernel_decal_pipelines_, opts);
459+
}
460+
461+
std::shared_ptr<Pipeline<PipelineDescriptor>> GetKernelPipeline(
462+
ContentContextOptions opts) const {
463+
return GetPipeline(kernel_nodecal_pipelines_, opts);
464+
}
465+
450466
std::shared_ptr<Pipeline<PipelineDescriptor>> GetBorderMaskBlurPipeline(
451467
ContentContextOptions opts) const {
452468
return GetPipeline(border_mask_blur_pipelines_, opts);
@@ -813,6 +829,8 @@ class ContentContext {
813829
gaussian_blur_noalpha_decal_pipelines_;
814830
mutable Variants<GaussianBlurPipeline>
815831
gaussian_blur_noalpha_nodecal_pipelines_;
832+
mutable Variants<KernelDecalPipeline> kernel_decal_pipelines_;
833+
mutable Variants<KernelPipeline> kernel_nodecal_pipelines_;
816834
mutable Variants<BorderMaskBlurPipeline> border_mask_blur_pipelines_;
817835
mutable Variants<MorphologyFilterPipeline> morphology_filter_pipelines_;
818836
mutable Variants<ColorMatrixColorFilterPipeline>

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include "impeller/entity/contents/filters/gaussian_blur_filter_contents.h"
66

7+
#include <cmath>
8+
79
#include "impeller/entity/contents/content_context.h"
810
#include "impeller/entity/texture_fill.frag.h"
911
#include "impeller/entity/texture_fill.vert.h"
@@ -14,8 +16,8 @@
1416

1517
namespace impeller {
1618

17-
using GaussianBlurVertexShader = GaussianBlurPipeline::VertexShader;
18-
using GaussianBlurFragmentShader = GaussianBlurPipeline::FragmentShader;
19+
using GaussianBlurVertexShader = KernelPipeline::VertexShader;
20+
using GaussianBlurFragmentShader = KernelPipeline::FragmentShader;
1921

2022
namespace {
2123

@@ -120,7 +122,7 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
120122
const RenderTarget& input_pass,
121123
const SamplerDescriptor& sampler_descriptor,
122124
Entity::TileMode tile_mode,
123-
const GaussianBlurFragmentShader::BlurInfo& blur_info,
125+
const BlurParameters& blur_info,
124126
std::optional<RenderTarget> destination_target,
125127
const Quad& blur_uvs) {
126128
if (blur_info.blur_sigma < kEhCloseEnough) {
@@ -147,9 +149,9 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
147149
if (tile_mode == Entity::TileMode::kDecal &&
148150
!renderer.GetDeviceCapabilities()
149151
.SupportsDecalSamplerAddressMode()) {
150-
cmd.pipeline = renderer.GetGaussianBlurDecalPipeline(options);
152+
cmd.pipeline = renderer.GetKernelDecalPipeline(options);
151153
} else {
152-
cmd.pipeline = renderer.GetGaussianBlurPipeline(options);
154+
cmd.pipeline = renderer.GetKernelPipeline(options);
153155
}
154156

155157
BindVertices<GaussianBlurVertexShader>(cmd, host_buffer,
@@ -169,8 +171,8 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
169171
linear_sampler_descriptor));
170172
GaussianBlurVertexShader::BindFrameInfo(
171173
cmd, host_buffer.EmplaceUniform(frame_info));
172-
GaussianBlurFragmentShader::BindBlurInfo(
173-
cmd, host_buffer.EmplaceUniform(blur_info));
174+
GaussianBlurFragmentShader::BindKernelSamples(
175+
cmd, host_buffer.EmplaceUniform(GenerateBlurInfo(blur_info)));
174176
pass.AddCommand(std::move(cmd));
175177

176178
return true;
@@ -343,16 +345,17 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
343345
}
344346
}
345347

346-
fml::StatusOr<RenderTarget> pass2_out =
347-
MakeBlurSubpass(renderer, /*input_pass=*/pass1_out.value(),
348-
input_snapshot->sampler_descriptor, tile_mode_,
349-
GaussianBlurFragmentShader::BlurInfo{
350-
.blur_uv_offset = Point(0.0, pass1_pixel_size.y),
351-
.blur_sigma = scaled_sigma.y * effective_scalar.y,
352-
.blur_radius = blur_radius.y * effective_scalar.y,
353-
.step_size = 1.0,
354-
},
355-
/*destination_target=*/std::nullopt, blur_uvs);
348+
fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
349+
renderer, /*input_pass=*/pass1_out.value(),
350+
input_snapshot->sampler_descriptor, tile_mode_,
351+
BlurParameters{
352+
.blur_uv_offset = Point(0.0, pass1_pixel_size.y),
353+
.blur_sigma = scaled_sigma.y * effective_scalar.y,
354+
.blur_radius =
355+
static_cast<int>(std::round(blur_radius.y * effective_scalar.y)),
356+
.step_size = 1,
357+
},
358+
/*destination_target=*/std::nullopt, blur_uvs);
356359

357360
if (!pass2_out.ok()) {
358361
return std::nullopt;
@@ -364,16 +367,17 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
364367
? std::optional<RenderTarget>(pass1_out.value())
365368
: std::optional<RenderTarget>(std::nullopt);
366369

367-
fml::StatusOr<RenderTarget> pass3_out =
368-
MakeBlurSubpass(renderer, /*input_pass=*/pass2_out.value(),
369-
input_snapshot->sampler_descriptor, tile_mode_,
370-
GaussianBlurFragmentShader::BlurInfo{
371-
.blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
372-
.blur_sigma = scaled_sigma.x * effective_scalar.x,
373-
.blur_radius = blur_radius.x * effective_scalar.x,
374-
.step_size = 1.0,
375-
},
376-
pass3_destination, blur_uvs);
370+
fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
371+
renderer, /*input_pass=*/pass2_out.value(),
372+
input_snapshot->sampler_descriptor, tile_mode_,
373+
BlurParameters{
374+
.blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
375+
.blur_sigma = scaled_sigma.x * effective_scalar.x,
376+
.blur_radius =
377+
static_cast<int>(std::round(blur_radius.x * effective_scalar.x)),
378+
.step_size = 1,
379+
},
380+
pass3_destination, blur_uvs);
377381

378382
if (!pass3_out.ok()) {
379383
return std::nullopt;
@@ -429,4 +433,31 @@ Scalar GaussianBlurFilterContents::ScaleSigma(Scalar sigma) {
429433
return clamped * scalar;
430434
}
431435

436+
KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
437+
BlurParameters parameters) {
438+
KernelPipeline::FragmentShader::KernelSamples result;
439+
result.sample_count =
440+
((2 * parameters.blur_radius) / parameters.step_size) + 1;
441+
FML_CHECK(result.sample_count < 24);
442+
443+
Scalar tally = 0.0f;
444+
for (int i = 0; i < result.sample_count; ++i) {
445+
int x = (i * parameters.step_size) - parameters.blur_radius;
446+
result.samples[i] = KernelPipeline::FragmentShader::KernelSample{
447+
.uv_offset = parameters.blur_uv_offset * x,
448+
.coefficient = expf(-0.5f * (x * x) /
449+
(parameters.blur_sigma * parameters.blur_sigma)) /
450+
(sqrtf(2.0f * M_PI) * parameters.blur_sigma),
451+
};
452+
tally += result.samples[i].coefficient;
453+
}
454+
455+
// Make sure everything adds up to 1.
456+
for (auto& sample : result.samples) {
457+
sample.coefficient /= tally;
458+
}
459+
460+
return result;
461+
}
462+
432463
} // namespace impeller

impeller/entity/contents/filters/gaussian_blur_filter_contents.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,21 @@
66
#define FLUTTER_IMPELLER_ENTITY_CONTENTS_FILTERS_GAUSSIAN_BLUR_FILTER_CONTENTS_H_
77

88
#include <optional>
9+
#include "impeller/entity/contents/content_context.h"
910
#include "impeller/entity/contents/filters/filter_contents.h"
1011

1112
namespace impeller {
1213

14+
struct BlurParameters {
15+
Point blur_uv_offset;
16+
Scalar blur_sigma;
17+
int blur_radius;
18+
int step_size;
19+
};
20+
21+
KernelPipeline::FragmentShader::KernelSamples GenerateBlurInfo(
22+
BlurParameters parameters);
23+
1324
/// Performs a bidirectional Gaussian blur.
1425
///
1526
/// This is accomplished by rendering multiple passes in multiple directions.

impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,5 +381,30 @@ TEST(GaussianBlurFilterContentsTest, CalculateSigmaForBlurRadius) {
381381
EXPECT_NEAR(sigma, derived_sigma, 0.01f);
382382
}
383383

384+
TEST(GaussianBlurFilterContentsTest, Coefficients) {
385+
BlurParameters parameters = {.blur_uv_offset = Point(1, 0),
386+
.blur_sigma = 1,
387+
.blur_radius = 5,
388+
.step_size = 1};
389+
KernelPipeline::FragmentShader::KernelSamples samples =
390+
GenerateBlurInfo(parameters);
391+
EXPECT_EQ(samples.sample_count, 11);
392+
393+
// Coefficients should add up to 1.
394+
Scalar tally = 0;
395+
for (int i = 0; i < samples.sample_count; ++i) {
396+
tally += samples.samples[i].coefficient;
397+
}
398+
EXPECT_FLOAT_EQ(tally, 1.0f);
399+
400+
// Verify the shape of the curve.
401+
for (int i = 0; i < 5; ++i) {
402+
EXPECT_FLOAT_EQ(samples.samples[i].coefficient,
403+
samples.samples[10 - i].coefficient);
404+
EXPECT_TRUE(samples.samples[i + 1].coefficient >
405+
samples.samples[i].coefficient);
406+
}
407+
}
408+
384409
} // namespace testing
385410
} // namespace impeller
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include <impeller/constants.glsl>
6+
#include <impeller/gaussian.glsl>
7+
#include <impeller/texture.glsl>
8+
#include <impeller/types.glsl>
9+
10+
uniform f16sampler2D texture_sampler;
11+
12+
struct KernelSample {
13+
vec2 uv_offset;
14+
float coefficient;
15+
};
16+
17+
uniform KernelSamples {
18+
int sample_count;
19+
KernelSample samples[24];
20+
}
21+
blur_info;
22+
23+
f16vec4 Sample(f16sampler2D tex, vec2 coords) {
24+
#if ENABLE_DECAL_SPECIALIZATION
25+
return IPHalfSampleDecal(tex, coords);
26+
#else
27+
return texture(tex, coords);
28+
#endif
29+
}
30+
31+
in vec2 v_texture_coords;
32+
33+
out f16vec4 frag_color;
34+
35+
void main() {
36+
f16vec4 total_color = f16vec4(0.0hf);
37+
38+
for (int i = 0; i < blur_info.sample_count; ++i) {
39+
float16_t coefficient = float16_t(blur_info.samples[i].coefficient);
40+
total_color +=
41+
coefficient * Sample(texture_sampler,
42+
v_texture_coords + blur_info.samples[i].uv_offset);
43+
}
44+
45+
frag_color = total_color;
46+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
precision mediump float;
6+
7+
#define ENABLE_DECAL_SPECIALIZATION 1
8+
9+
#include "kernel.glsl"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
precision mediump float;
6+
7+
#define ENABLE_DECAL_SPECIALIZATION 0
8+
9+
#include "kernel.glsl"

0 commit comments

Comments
 (0)