[Impeller] new blur: limit uvs to blur region (flutter/engine#49299)

This will run the blur passes on a subset of the texture passed into it that will actually be blurred.  This is an optimization for backdrop filters.

issue: https://github.com/flutter/flutter/issues/131580
test coverage: devicelab `backdrop_filter_perf_ios__timeline_summary`

## performance results

This results in a 50% reduction in average GPU time in our benchmark, 79% reduction in the "99% percentile".
```
BEFORE

    "average_vsync_transitions_missed": 3.889423076923077,
    "90th_percentile_vsync_transitions_missed": 4.0,
    "99th_percentile_vsync_transitions_missed": 4.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": 16654.368333333332,
    "90th_percentile_frame_request_pending_latency": 16692.0,
    "99th_percentile_frame_request_pending_latency": 16749.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": 0.659,
    "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": 52.13341346153846,
    "90th_percentile_gpu_frame_time": 62.5,
    "99th_percentile_gpu_frame_time": 62.5,
    "worst_gpu_frame_time": 62.5,
    "average_cpu_usage": 69.08979595918369,
    "90th_percentile_cpu_usage": 70.4,
    "99th_percentile_cpu_usage": 71.699999,
    "average_gpu_usage": 100.0,
    "90th_percentile_gpu_usage": 100.0,
    "99th_percentile_gpu_usage": 100.0,
    "average_memory_usage": 118.79942602040816,
    "90th_percentile_memory_usage": 138.125,
    "99th_percentile_memory_usage": 143.65625

AFTER

    "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": 16635.025,
    "90th_percentile_frame_request_pending_latency": 16715.0,
    "99th_percentile_frame_request_pending_latency": 16802.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.732,
    "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": 25.01558603491272,
    "90th_percentile_gpu_frame_time": 31.25,
    "99th_percentile_gpu_frame_time": 31.25,
```

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
gaaclarke 2024-01-03 12:46:00 -08:00 committed by GitHub
parent c86035488d
commit d52f315f41
2 changed files with 66 additions and 8 deletions

View File

@ -3630,6 +3630,30 @@ TEST_P(AiksTest, GaussianBlurRotatedAndClipped) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, GaussianBlurScaledAndClipped) {
Canvas canvas;
std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
Rect bounds =
Rect::MakeXYWH(0, 0, boston->GetSize().width, boston->GetSize().height);
Vector2 image_center = Vector2(bounds.GetSize() / 2);
Paint paint = {.image_filter =
ImageFilter::MakeBlur(Sigma(20.0), Sigma(20.0),
FilterContents::BlurStyle::kNormal,
Entity::TileMode::kDecal)};
Vector2 clip_size = {150, 75};
Vector2 center = Vector2(1024, 768) / 2;
canvas.Scale(GetContentScale());
canvas.ClipRect(
Rect::MakeLTRB(center.x, center.y, center.x, center.y).Expand(clip_size));
canvas.Translate({center.x, center.y, 0});
canvas.Scale({0.6, 0.6, 1});
canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,
/*dest=*/bounds.Shift(-image_center), paint);
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
std::shared_ptr<Texture> boston = CreateTextureForFixture("boston.jpg");
@ -3640,11 +3664,13 @@ TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
Entity::TileMode::kMirror, Entity::TileMode::kDecal};
static float rotation = 0;
static float scale = 0.6;
static int selected_tile_mode = 3;
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
{
ImGui::SliderFloat("Rotation (degrees)", &rotation, -180, 180);
ImGui::SliderFloat("Scale", &scale, 0, 2.0);
ImGui::Combo("Tile mode", &selected_tile_mode, tile_mode_names,
sizeof(tile_mode_names) / sizeof(char*));
}
@ -3665,7 +3691,7 @@ TEST_P(AiksTest, GaussianBlurRotatedAndClippedInteractive) {
canvas.ClipRect(
Rect::MakeLTRB(handle_a.x, handle_a.y, handle_b.x, handle_b.y));
canvas.Translate({center.x, center.y, 0});
canvas.Scale({0.6, 0.6, 1});
canvas.Scale({scale, scale, 1});
canvas.Rotate(Degrees(rotation));
canvas.DrawImageRect(std::make_shared<Image>(boston), /*source=*/bounds,

View File

@ -121,7 +121,8 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
const SamplerDescriptor& sampler_descriptor,
Entity::TileMode tile_mode,
const GaussianBlurFragmentShader::BlurInfo& blur_info,
std::optional<RenderTarget> destination_target) {
std::optional<RenderTarget> destination_target,
const Quad& blur_uvs) {
if (blur_info.blur_sigma < kEhCloseEnough) {
return input_pass;
}
@ -153,10 +154,10 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
BindVertices<GaussianBlurVertexShader>(cmd, host_buffer,
{
{Point(0, 0), Point(0, 0)},
{Point(1, 0), Point(1, 0)},
{Point(0, 1), Point(0, 1)},
{Point(1, 1), Point(1, 1)},
{blur_uvs[0], blur_uvs[0]},
{blur_uvs[1], blur_uvs[1]},
{blur_uvs[2], blur_uvs[2]},
{blur_uvs[3], blur_uvs[3]},
});
SamplerDescriptor linear_sampler_descriptor = sampler_descriptor;
@ -183,6 +184,14 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
}
}
/// Returns `rect` relative to `reference`, where Rect::MakeXYWH(0,0,1,1) will
/// be returned when `rect` == `reference`.
Rect MakeReferenceUVs(const Rect& reference, const Rect& rect) {
Rect result = Rect::MakeOriginSize(rect.GetOrigin() - reference.GetOrigin(),
rect.GetSize());
return result.Scale(1.0f / Vector2(reference.GetSize()));
}
} // namespace
GaussianBlurFilterContents::GaussianBlurFilterContents(
@ -311,6 +320,29 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
Vector2 pass1_pixel_size =
1.0 / Vector2(pass1_out.value().GetRenderTargetTexture()->GetSize());
std::optional<Rect> input_snapshot_coverage = input_snapshot->GetCoverage();
Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)};
if (expanded_coverage_hint.has_value() &&
input_snapshot_coverage.has_value() &&
// TODO(https://github.com/flutter/flutter/issues/140890): Remove this
// condition. There is some flaw in coverage stopping us from using this
// today. I attempted to use source coordinates to calculate the uvs,
// but that didn't work either.
input_snapshot.has_value() &&
input_snapshot.value().transform.IsTranslationScaleOnly()) {
// Only process the uvs where the blur is happening, not the whole texture.
std::optional<Rect> uvs = MakeReferenceUVs(input_snapshot_coverage.value(),
expanded_coverage_hint.value())
.Intersection(Rect::MakeSize(Size(1, 1)));
FML_DCHECK(uvs.has_value());
if (uvs.has_value()) {
blur_uvs[0] = uvs->GetLeftTop();
blur_uvs[1] = uvs->GetRightTop();
blur_uvs[2] = uvs->GetLeftBottom();
blur_uvs[3] = uvs->GetRightBottom();
}
}
fml::StatusOr<RenderTarget> pass2_out =
MakeBlurSubpass(renderer, /*input_pass=*/pass1_out.value(),
input_snapshot->sampler_descriptor, tile_mode_,
@ -320,7 +352,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
.blur_radius = blur_radius.y * effective_scalar.y,
.step_size = 1.0,
},
/*destination_target=*/std::nullopt);
/*destination_target=*/std::nullopt, blur_uvs);
if (!pass2_out.ok()) {
return std::nullopt;
@ -341,7 +373,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
.blur_radius = blur_radius.x * effective_scalar.x,
.step_size = 1.0,
},
pass3_destination);
pass3_destination, blur_uvs);
if (!pass3_out.ok()) {
return std::nullopt;