From 4385a96ee16374ebbaf75b0aae756913f25fde4c Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 28 Aug 2024 18:23:31 -0700 Subject: [PATCH] [Impeller] Use multiple command buffers for blur submission. (flutter/engine#54846) Fixes https://github.com/flutter/flutter/issues/154046 Use three different command buffers when it would be possible to combine the operations into a single buffer. From testing and user bug reports (see https://github.com/flutter/flutter/issues/154046 ), this sometimes causes deviceLost errors on older Adreno devices. Breaking the work up into three different command buffers seems to prevent this crash. I suspect this is due to some internal timeouts we may be hitting in the adreno driver. --- .../filters/gaussian_blur_filter_contents.cc | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc index cf39b60e33..c8fda3376c 100644 --- a/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/filters/gaussian_blur_filter_contents.cc @@ -727,9 +727,15 @@ std::optional GaussianBlurFilterContents::RenderFilter( return result; } - std::shared_ptr command_buffer = + // Note: The code below uses three different command buffers when it would be + // possible to combine the operations into a single buffer. From testing and + // user bug reports (see https://github.com/flutter/flutter/issues/154046 ), + // this sometimes causes deviceLost errors on older Adreno devices. Breaking + // the work up into three different command buffers seems to prevent this + // crash. + std::shared_ptr command_buffer_1 = renderer.GetContext()->CreateCommandBuffer(); - if (!command_buffer) { + if (!command_buffer_1) { return std::nullopt; } @@ -738,7 +744,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( source_expanded_coverage_hint, inputs[0], snapshot_entity); fml::StatusOr pass1_out = MakeDownsampleSubpass( - renderer, command_buffer, input_snapshot->texture, + renderer, command_buffer_1, input_snapshot->texture, input_snapshot->sampler_descriptor, downsample_pass_args, tile_mode_); if (!pass1_out.ok()) { @@ -750,8 +756,14 @@ std::optional GaussianBlurFilterContents::RenderFilter( Quad blur_uvs = {Point(0, 0), Point(1, 0), Point(0, 1), Point(1, 1)}; + std::shared_ptr command_buffer_2 = + renderer.GetContext()->CreateCommandBuffer(); + if (!command_buffer_2) { + return std::nullopt; + } + fml::StatusOr pass2_out = MakeBlurSubpass( - renderer, command_buffer, /*input_pass=*/pass1_out.value(), + renderer, command_buffer_2, /*input_pass=*/pass1_out.value(), input_snapshot->sampler_descriptor, tile_mode_, BlurParameters{ .blur_uv_offset = Point(0.0, pass1_pixel_size.y), @@ -767,6 +779,12 @@ std::optional GaussianBlurFilterContents::RenderFilter( return std::nullopt; } + std::shared_ptr command_buffer_3 = + renderer.GetContext()->CreateCommandBuffer(); + if (!command_buffer_3) { + return std::nullopt; + } + // Only ping pong if the first pass actually created a render target. auto pass3_destination = pass2_out.value().GetRenderTargetTexture() != pass1_out.value().GetRenderTargetTexture() @@ -774,7 +792,7 @@ std::optional GaussianBlurFilterContents::RenderFilter( : std::optional(std::nullopt); fml::StatusOr pass3_out = MakeBlurSubpass( - renderer, command_buffer, /*input_pass=*/pass2_out.value(), + renderer, command_buffer_3, /*input_pass=*/pass2_out.value(), input_snapshot->sampler_descriptor, tile_mode_, BlurParameters{ .blur_uv_offset = Point(pass1_pixel_size.x, 0.0), @@ -792,7 +810,8 @@ std::optional GaussianBlurFilterContents::RenderFilter( if (!renderer.GetContext() ->GetCommandQueue() - ->Submit(/*buffers=*/{command_buffer}) + ->Submit(/*buffers=*/{command_buffer_1, command_buffer_2, + command_buffer_3}) .ok()) { return std::nullopt; }