mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[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:
parent
c86035488d
commit
d52f315f41
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user