Skip to content

Commit d048b9a

Browse files
author
Jonah Williams
authored
[Impeller] batch up filter graph command buffers. (flutter#51912)
The filter graph frequently creates and submits command buffers in serial. This has some unnecessary overhead, esp on Vulkan where submitting a command buffer has a non-trivial cost. While previously I had tried to batch up all submissions, doing this in a limited manner in the filter graph should be more straightforward. For gaussians this makes a big difference, as there is a mipmap generation, downsample, then 2 render passes, so we can compress the 4 command buffers into 1. flutter#142545
1 parent 6974dba commit d048b9a

File tree

7 files changed

+108
-33
lines changed

7 files changed

+108
-33
lines changed

impeller/entity/contents/content_context.cc

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -481,8 +481,9 @@ bool ContentContext::IsValid() const {
481481
}
482482

483483
fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
484-
const std::string& label,
484+
std::string_view label,
485485
ISize texture_size,
486+
const std::shared_ptr<CommandBuffer>& command_buffer,
486487
const SubpassCallback& subpass_callback,
487488
bool msaa_enabled,
488489
bool depth_stencil_enabled,
@@ -497,20 +498,21 @@ fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
497498
if (context->GetCapabilities()->SupportsOffscreenMSAA() && msaa_enabled) {
498499
subpass_target = GetRenderTargetCache()->CreateOffscreenMSAA(
499500
*context, texture_size,
500-
/*mip_count=*/mip_count, SPrintF("%s Offscreen", label.c_str()),
501+
/*mip_count=*/mip_count, SPrintF("%s Offscreen", label.data()),
501502
RenderTarget::kDefaultColorAttachmentConfigMSAA, depth_stencil_config);
502503
} else {
503504
subpass_target = GetRenderTargetCache()->CreateOffscreen(
504505
*context, texture_size,
505-
/*mip_count=*/mip_count, SPrintF("%s Offscreen", label.c_str()),
506+
/*mip_count=*/mip_count, SPrintF("%s Offscreen", label.data()),
506507
RenderTarget::kDefaultColorAttachmentConfig, depth_stencil_config);
507508
}
508-
return MakeSubpass(label, subpass_target, subpass_callback);
509+
return MakeSubpass(label, subpass_target, command_buffer, subpass_callback);
509510
}
510511

511512
fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
512-
const std::string& label,
513+
std::string_view label,
513514
const RenderTarget& subpass_target,
515+
const std::shared_ptr<CommandBuffer>& command_buffer,
514516
const SubpassCallback& subpass_callback) const {
515517
const std::shared_ptr<Context>& context = GetContext();
516518

@@ -519,17 +521,11 @@ fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
519521
return fml::Status(fml::StatusCode::kUnknown, "");
520522
}
521523

522-
auto sub_command_buffer = context->CreateCommandBuffer();
523-
sub_command_buffer->SetLabel(SPrintF("%s CommandBuffer", label.c_str()));
524-
if (!sub_command_buffer) {
525-
return fml::Status(fml::StatusCode::kUnknown, "");
526-
}
527-
528-
auto sub_renderpass = sub_command_buffer->CreateRenderPass(subpass_target);
524+
auto sub_renderpass = command_buffer->CreateRenderPass(subpass_target);
529525
if (!sub_renderpass) {
530526
return fml::Status(fml::StatusCode::kUnknown, "");
531527
}
532-
sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.c_str()));
528+
sub_renderpass->SetLabel(SPrintF("%s RenderPass", label.data()));
533529

534530
if (!subpass_callback(*this, *sub_renderpass)) {
535531
return fml::Status(fml::StatusCode::kUnknown, "");
@@ -543,16 +539,12 @@ fml::StatusOr<RenderTarget> ContentContext::MakeSubpass(
543539
subpass_target.GetRenderTargetTexture();
544540
if (target_texture->GetMipCount() > 1) {
545541
fml::Status mipmap_status =
546-
AddMipmapGeneration(sub_command_buffer, context, target_texture);
542+
AddMipmapGeneration(command_buffer, context, target_texture);
547543
if (!mipmap_status.ok()) {
548544
return mipmap_status;
549545
}
550546
}
551547

552-
if (!context->GetCommandQueue()->Submit({sub_command_buffer}).ok()) {
553-
return fml::Status(fml::StatusCode::kUnknown, "");
554-
}
555-
556548
return subpass_target;
557549
}
558550

impeller/entity/contents/content_context.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,17 +783,19 @@ class ContentContext {
783783
/// @brief Creates a new texture of size `texture_size` and calls
784784
/// `subpass_callback` with a `RenderPass` for drawing to the texture.
785785
fml::StatusOr<RenderTarget> MakeSubpass(
786-
const std::string& label,
786+
std::string_view label,
787787
ISize texture_size,
788+
const std::shared_ptr<CommandBuffer>& command_buffer,
788789
const SubpassCallback& subpass_callback,
789790
bool msaa_enabled = true,
790791
bool depth_stencil_enabled = false,
791792
int32_t mip_count = 1) const;
792793

793794
/// Makes a subpass that will render to `subpass_target`.
794795
fml::StatusOr<RenderTarget> MakeSubpass(
795-
const std::string& label,
796+
std::string_view label,
796797
const RenderTarget& subpass_target,
798+
const std::shared_ptr<CommandBuffer>& command_buffer,
797799
const SubpassCallback& subpass_callback) const;
798800

799801
const std::shared_ptr<LazyGlyphAtlas>& GetLazyGlyphAtlas() const {

impeller/entity/contents/contents.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ std::optional<Snapshot> Contents::RenderToSnapshot(
7676
return std::nullopt;
7777
}
7878

79+
std::shared_ptr<CommandBuffer> command_buffer =
80+
renderer.GetContext()->CreateCommandBuffer();
81+
if (!command_buffer) {
82+
return std::nullopt;
83+
}
84+
7985
// Pad Contents snapshots with 1 pixel borders to ensure correct sampling
8086
// behavior. Not doing so results in a coverage leak for filters that support
8187
// customizing the input sampling mode. Snapshots of contents should be
@@ -91,7 +97,7 @@ std::optional<Snapshot> Contents::RenderToSnapshot(
9197

9298
ISize subpass_size = ISize::Ceil(coverage->GetSize());
9399
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
94-
label, subpass_size,
100+
label, subpass_size, command_buffer,
95101
[&contents = *this, &entity, &coverage](const ContentContext& renderer,
96102
RenderPass& pass) -> bool {
97103
Entity sub_entity;
@@ -107,6 +113,12 @@ std::optional<Snapshot> Contents::RenderToSnapshot(
107113
if (!render_target.ok()) {
108114
return std::nullopt;
109115
}
116+
if (!renderer.GetContext()
117+
->GetCommandQueue()
118+
->Submit(/*buffers=*/{std::move(command_buffer)})
119+
.ok()) {
120+
return std::nullopt;
121+
}
110122

111123
auto snapshot = Snapshot{
112124
.texture = render_target.value().GetRenderTargetTexture(),

impeller/entity/contents/filters/blend_filter_contents.cc

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,23 @@ static std::optional<Entity> AdvancedBlend(
223223
return pass.Draw().ok();
224224
};
225225

226+
std::shared_ptr<CommandBuffer> command_buffer =
227+
renderer.GetContext()->CreateCommandBuffer();
228+
if (!command_buffer) {
229+
return std::nullopt;
230+
}
226231
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
227-
"Advanced Blend Filter", ISize(subpass_coverage.GetSize()), callback);
232+
"Advanced Blend Filter", ISize(subpass_coverage.GetSize()),
233+
command_buffer, callback);
228234
if (!render_target.ok()) {
229235
return std::nullopt;
230236
}
237+
if (!renderer.GetContext()
238+
->GetCommandQueue()
239+
->Submit(/*buffers=*/{std::move(command_buffer)})
240+
.ok()) {
241+
return std::nullopt;
242+
}
231243

232244
return Entity::FromSnapshot(
233245
Snapshot{
@@ -525,13 +537,27 @@ static std::optional<Entity> PipelineBlend(
525537
return true;
526538
};
527539

540+
std::shared_ptr<CommandBuffer> command_buffer =
541+
renderer.GetContext()->CreateCommandBuffer();
542+
if (!command_buffer) {
543+
return std::nullopt;
544+
}
545+
528546
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
529-
"Pipeline Blend Filter", ISize(subpass_coverage.GetSize()), callback);
547+
"Pipeline Blend Filter", ISize(subpass_coverage.GetSize()),
548+
command_buffer, callback);
530549

531550
if (!render_target.ok()) {
532551
return std::nullopt;
533552
}
534553

554+
if (!renderer.GetContext()
555+
->GetCommandQueue()
556+
->Submit(/*buffers=*/{std::move(command_buffer)})
557+
.ok()) {
558+
return std::nullopt;
559+
}
560+
535561
return Entity::FromSnapshot(
536562
Snapshot{
537563
.texture = render_target.value().GetRenderTargetTexture(),

impeller/entity/contents/filters/gaussian_blur_filter_contents.cc

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ void SetTileMode(SamplerDescriptor* descriptor,
7777
/// transparent gutter required for the blur halo.
7878
fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
7979
const ContentContext& renderer,
80+
const std::shared_ptr<CommandBuffer>& command_buffer,
8081
std::shared_ptr<Texture> input_texture,
8182
const SamplerDescriptor& sampler_descriptor,
8283
const Quad& uvs,
@@ -118,12 +119,13 @@ fml::StatusOr<RenderTarget> MakeDownsampleSubpass(
118119
return pass.Draw().ok();
119120
};
120121
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
121-
"Gaussian Blur Filter", subpass_size, subpass_callback);
122+
"Gaussian Blur Filter", subpass_size, command_buffer, subpass_callback);
122123
return render_target;
123124
}
124125

125126
fml::StatusOr<RenderTarget> MakeBlurSubpass(
126127
const ContentContext& renderer,
128+
const std::shared_ptr<CommandBuffer>& command_buffer,
127129
const RenderTarget& input_pass,
128130
const SamplerDescriptor& sampler_descriptor,
129131
Entity::TileMode tile_mode,
@@ -184,10 +186,11 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass(
184186
};
185187
if (destination_target.has_value()) {
186188
return renderer.MakeSubpass("Gaussian Blur Filter",
187-
destination_target.value(), subpass_callback);
189+
destination_target.value(), command_buffer,
190+
subpass_callback);
188191
} else {
189192
return renderer.MakeSubpass("Gaussian Blur Filter", subpass_size,
190-
subpass_callback);
193+
command_buffer, subpass_callback);
191194
}
192195
}
193196

@@ -459,9 +462,15 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
459462
Quad uvs = CalculateUVs(inputs[0], entity, source_rect_padded,
460463
input_snapshot->texture->GetSize());
461464

465+
std::shared_ptr<CommandBuffer> command_buffer =
466+
renderer.GetContext()->CreateCommandBuffer();
467+
if (!command_buffer) {
468+
return std::nullopt;
469+
}
470+
462471
fml::StatusOr<RenderTarget> pass1_out = MakeDownsampleSubpass(
463-
renderer, input_snapshot->texture, input_snapshot->sampler_descriptor,
464-
uvs, subpass_size, tile_mode_);
472+
renderer, command_buffer, input_snapshot->texture,
473+
input_snapshot->sampler_descriptor, uvs, subpass_size, tile_mode_);
465474

466475
if (!pass1_out.ok()) {
467476
return std::nullopt;
@@ -494,7 +503,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
494503
}
495504

496505
fml::StatusOr<RenderTarget> pass2_out = MakeBlurSubpass(
497-
renderer, /*input_pass=*/pass1_out.value(),
506+
renderer, command_buffer, /*input_pass=*/pass1_out.value(),
498507
input_snapshot->sampler_descriptor, tile_mode_,
499508
BlurParameters{
500509
.blur_uv_offset = Point(0.0, pass1_pixel_size.y),
@@ -515,7 +524,7 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
515524
: std::optional<RenderTarget>(std::nullopt);
516525

517526
fml::StatusOr<RenderTarget> pass3_out = MakeBlurSubpass(
518-
renderer, /*input_pass=*/pass2_out.value(),
527+
renderer, command_buffer, /*input_pass=*/pass2_out.value(),
519528
input_snapshot->sampler_descriptor, tile_mode_,
520529
BlurParameters{
521530
.blur_uv_offset = Point(pass1_pixel_size.x, 0.0),
@@ -529,6 +538,13 @@ std::optional<Entity> GaussianBlurFilterContents::RenderFilter(
529538
return std::nullopt;
530539
}
531540

541+
if (!renderer.GetContext()
542+
->GetCommandQueue()
543+
->Submit(/*buffers=*/{command_buffer})
544+
.ok()) {
545+
return std::nullopt;
546+
}
547+
532548
// The ping-pong approach requires that each render pass output has the same
533549
// size.
534550
FML_DCHECK((pass1_out.value().GetRenderTargetSize() ==

impeller/entity/contents/filters/gaussian_blur_filter_contents_unittests.cc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,24 @@ class GaussianBlurFilterContentsTest : public EntityPlayground {
7474
public:
7575
/// Create a texture that has been cleared to transparent black.
7676
std::shared_ptr<Texture> MakeTexture(ISize size) {
77+
std::shared_ptr<CommandBuffer> command_buffer =
78+
GetContentContext()->GetContext()->CreateCommandBuffer();
79+
if (!command_buffer) {
80+
return nullptr;
81+
}
82+
7783
auto render_target = GetContentContext()->MakeSubpass(
78-
"Clear Subpass", size,
84+
"Clear Subpass", size, command_buffer,
7985
[](const ContentContext&, RenderPass&) { return true; });
86+
87+
if (!GetContentContext()
88+
->GetContext()
89+
->GetCommandQueue()
90+
->Submit(/*buffers=*/{command_buffer})
91+
.ok()) {
92+
return nullptr;
93+
}
94+
8095
if (render_target.ok()) {
8196
return render_target.value().GetRenderTargetTexture();
8297
}

impeller/entity/contents/filters/morphology_filter_contents.cc

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,24 @@ std::optional<Entity> DirectionalMorphologyFilterContents::RenderFilter(
134134

135135
return pass.Draw().ok();
136136
};
137+
std::shared_ptr<CommandBuffer> command_buffer =
138+
renderer.GetContext()->CreateCommandBuffer();
139+
if (command_buffer == nullptr) {
140+
return std::nullopt;
141+
}
137142

138-
fml::StatusOr<RenderTarget> render_target = renderer.MakeSubpass(
139-
"Directional Morphology Filter", ISize(coverage.GetSize()), callback);
143+
fml::StatusOr<RenderTarget> render_target =
144+
renderer.MakeSubpass("Directional Morphology Filter",
145+
ISize(coverage.GetSize()), command_buffer, callback);
140146
if (!render_target.ok()) {
141147
return std::nullopt;
142148
}
149+
if (!renderer.GetContext()
150+
->GetCommandQueue()
151+
->Submit(/*buffers=*/{std::move(command_buffer)})
152+
.ok()) {
153+
return std::nullopt;
154+
}
143155

144156
SamplerDescriptor sampler_desc;
145157
sampler_desc.min_filter = MinMagFilter::kLinear;

0 commit comments

Comments
 (0)