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

Commit f178e19

Browse files
author
Jonah Williams
authored
[Impeller] add async command submission for blit pass. (#48040)
This is a requirement for the "Remove Drawable Acquisition Latency" Change, but is otherwise a harmless improvement. --- Submitting a command buffer causes the backend specific encoding logic to run. Metal is unique in that it is fairly easy to move this work into a background thread, allowing the engine to move onto creating the next command buffer. This improves throughput of the engine, at the cost of needing two slightly different APIs. Currently the GLES and Vulkan versions of this method still submit synchronously - for now that is out of scope as doing background work with those APIs has proved more challenging. See also: * #42028 * flutter/flutter#131698 Separately, as a requirement for the design in "Remove Drawable Acquisition Latency", we need to be able to defer drawable acquisition to this background thread. While this almost already works for render passes, it does not work for blit passes today. if the engine renders a backdrop filter, then the final command buffer submitted will be a blit pass that copies an offscreen onto the drawable. Therefore we need to add an async version of the blit submission, so that we have a hook to move the drawable acquisition onto a background thread for metal. This hadn't been done until now because most blit cmd buffers have 1 or 2 cmds on them so the benefit of moving to a background thread is minimal. Part of flutter/flutter#138490
1 parent 8372f6c commit f178e19

File tree

10 files changed

+86
-34
lines changed

10 files changed

+86
-34
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ TEST_P(AiksTest, CanRenderColorFilterWithInvertColorsDrawPaint) {
165165
namespace {
166166
bool GenerateMipmap(const std::shared_ptr<Context>& context,
167167
std::shared_ptr<Texture> texture,
168-
std::string label) {
168+
std::string label,
169+
bool async_submit) {
169170
auto buffer = context->CreateCommandBuffer();
170171
if (!buffer) {
171172
return false;
@@ -175,18 +176,23 @@ bool GenerateMipmap(const std::shared_ptr<Context>& context,
175176
return false;
176177
}
177178
pass->GenerateMipmap(std::move(texture), std::move(label));
179+
if (async_submit) {
180+
return buffer->EncodeAndSubmit(pass, context->GetResourceAllocator());
181+
}
182+
178183
pass->EncodeCommands(context->GetResourceAllocator());
179184
return buffer->SubmitCommands();
180185
}
181186

182187
void CanRenderTiledTexture(AiksTest* aiks_test,
183188
Entity::TileMode tile_mode,
189+
bool async_submit = false,
184190
Matrix local_matrix = {}) {
185191
auto context = aiks_test->GetContext();
186192
ASSERT_TRUE(context);
187193
auto texture = aiks_test->CreateTextureForFixture("table_mountain_nx.png",
188194
/*enable_mipmapping=*/true);
189-
GenerateMipmap(context, texture, "table_mountain_nx");
195+
GenerateMipmap(context, texture, "table_mountain_nx", async_submit);
190196
Canvas canvas;
191197
canvas.Scale(aiks_test->GetContentScale());
192198
canvas.Translate({100.0f, 100.0f, 0});
@@ -233,6 +239,10 @@ TEST_P(AiksTest, CanRenderTiledTextureClamp) {
233239
CanRenderTiledTexture(this, Entity::TileMode::kClamp);
234240
}
235241

242+
TEST_P(AiksTest, CanRenderTiledTextureClampAsync) {
243+
CanRenderTiledTexture(this, Entity::TileMode::kClamp, /*async_submit=*/true);
244+
}
245+
236246
TEST_P(AiksTest, CanRenderTiledTextureRepeat) {
237247
CanRenderTiledTexture(this, Entity::TileMode::kRepeat);
238248
}
@@ -246,7 +256,7 @@ TEST_P(AiksTest, CanRenderTiledTextureDecal) {
246256
}
247257

248258
TEST_P(AiksTest, CanRenderTiledTextureClampWithTranslate) {
249-
CanRenderTiledTexture(this, Entity::TileMode::kClamp,
259+
CanRenderTiledTexture(this, Entity::TileMode::kClamp, /*async_submit=*/false,
250260
Matrix::MakeTranslation({172.f, 172.f, 0.f}));
251261
}
252262

impeller/aiks/testing/context_mock.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ class CommandBufferMock : public CommandBuffer {
2525

2626
MOCK_METHOD(void, SetLabel, (const std::string& label), (const, override));
2727

28-
MOCK_METHOD(bool,
29-
SubmitCommandsAsync,
30-
(std::shared_ptr<RenderPass> render_pass),
31-
(override));
32-
3328
MOCK_METHOD(std::shared_ptr<RenderPass>,
3429
OnCreateRenderPass,
3530
(RenderTarget render_target),

impeller/aiks/testing/context_spy.cc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,6 @@ std::shared_ptr<ContextMock> ContextSpy::MakeContext(
6363
return real_buffer->SetLabel(label);
6464
});
6565

66-
ON_CALL(*spy, SubmitCommandsAsync)
67-
.WillByDefault([real_buffer](
68-
std::shared_ptr<RenderPass> render_pass) {
69-
return real_buffer->SubmitCommandsAsync(std::move(render_pass));
70-
});
71-
7266
ON_CALL(*spy, OnCreateRenderPass)
7367
.WillByDefault(
7468
[real_buffer, shared_this](const RenderTarget& render_target) {

impeller/entity/contents/content_context.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ std::shared_ptr<Texture> ContentContext::MakeSubpass(
430430
return nullptr;
431431
}
432432

433-
if (!sub_command_buffer->SubmitCommandsAsync(std::move(sub_renderpass))) {
433+
if (!sub_command_buffer->EncodeAndSubmit(sub_renderpass)) {
434434
return nullptr;
435435
}
436436

impeller/entity/entity_pass.cc

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,8 @@ bool EntityPass::Render(ContentContext& renderer,
371371
offscreen_target.GetRenderTarget().GetRenderTargetTexture(),
372372
root_render_target.GetRenderTargetTexture());
373373

374-
if (!blit_pass->EncodeCommands(
375-
renderer.GetContext()->GetResourceAllocator())) {
374+
if (!command_buffer->EncodeAndSubmit(
375+
blit_pass, renderer.GetContext()->GetResourceAllocator())) {
376376
VALIDATION_LOG << "Failed to encode root pass blit command.";
377377
return false;
378378
}
@@ -399,15 +399,11 @@ bool EntityPass::Render(ContentContext& renderer,
399399
}
400400
}
401401

402-
if (!render_pass->EncodeCommands()) {
402+
if (!command_buffer->EncodeAndSubmit(render_pass)) {
403403
VALIDATION_LOG << "Failed to encode root pass command buffer.";
404404
return false;
405405
}
406406
}
407-
if (!command_buffer->SubmitCommands()) {
408-
VALIDATION_LOG << "Failed to submit root pass command buffer.";
409-
return false;
410-
}
411407

412408
return true;
413409
}

impeller/entity/inline_pass_context.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ bool InlinePassContext::EndPass() {
5454
}
5555

5656
if (command_buffer_) {
57-
if (!command_buffer_->SubmitCommandsAsync(std::move(pass_))) {
57+
if (!command_buffer_->EncodeAndSubmit(pass_)) {
5858
VALIDATION_LOG
5959
<< "Failed to encode and submit command buffer while ending "
6060
"render pass.";

impeller/renderer/backend/metal/command_buffer_mtl.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <Metal/Metal.h>
88

99
#include "flutter/fml/macros.h"
10+
#include "impeller/core/allocator.h"
1011
#include "impeller/renderer/command_buffer.h"
1112

1213
namespace impeller {
@@ -37,7 +38,11 @@ class CommandBufferMTL final : public CommandBuffer {
3738
void OnWaitUntilScheduled() override;
3839

3940
// |CommandBuffer|
40-
bool SubmitCommandsAsync(std::shared_ptr<RenderPass> render_pass) override;
41+
bool EncodeAndSubmit(const std::shared_ptr<RenderPass>& render_pass) override;
42+
43+
// |CommandBuffer|
44+
bool EncodeAndSubmit(const std::shared_ptr<BlitPass>& blit_ass,
45+
const std::shared_ptr<Allocator>& allocator) override;
4146

4247
// |CommandBuffer|
4348
std::shared_ptr<RenderPass> OnCreateRenderPass(RenderTarget target) override;

impeller/renderer/backend/metal/command_buffer_mtl.mm

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,9 @@ static bool LogMTLCommandBufferErrorIfPresent(id<MTLCommandBuffer> buffer) {
183183
return true;
184184
}
185185

186-
bool CommandBufferMTL::SubmitCommandsAsync(
187-
std::shared_ptr<RenderPass> render_pass) {
188-
TRACE_EVENT0("impeller", "CommandBufferMTL::SubmitCommandsAsync");
186+
bool CommandBufferMTL::EncodeAndSubmit(
187+
const std::shared_ptr<RenderPass>& render_pass) {
188+
TRACE_EVENT0("impeller", "CommandBufferMTL::EncodeAndSubmit");
189189
if (!IsValid() || !render_pass->IsValid()) {
190190
return false;
191191
}
@@ -239,6 +239,34 @@ static bool LogMTLCommandBufferErrorIfPresent(id<MTLCommandBuffer> buffer) {
239239
return true;
240240
}
241241

242+
bool CommandBufferMTL::EncodeAndSubmit(
243+
const std::shared_ptr<BlitPass>& blit_pass,
244+
const std::shared_ptr<Allocator>& allocator) {
245+
if (!IsValid() || !blit_pass->IsValid()) {
246+
return false;
247+
}
248+
auto context = context_.lock();
249+
if (!context) {
250+
return false;
251+
}
252+
[buffer_ enqueue];
253+
auto buffer = buffer_;
254+
buffer_ = nil;
255+
256+
auto worker_task_runner = ContextMTL::Cast(*context).GetWorkerTaskRunner();
257+
auto task = fml::MakeCopyable(
258+
[blit_pass, buffer, weak_context = context_, allocator]() {
259+
auto context = weak_context.lock();
260+
if (!blit_pass->EncodeCommands(allocator)) {
261+
VALIDATION_LOG << "Failed to encode blit pass.";
262+
return;
263+
}
264+
[buffer commit];
265+
});
266+
worker_task_runner->PostTask(task);
267+
return true;
268+
}
269+
242270
void CommandBufferMTL::OnWaitUntilScheduled() {}
243271

244272
std::shared_ptr<RenderPass> CommandBufferMTL::OnCreateRenderPass(

impeller/renderer/command_buffer.cc

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,9 @@ void CommandBuffer::WaitUntilScheduled() {
3636
return OnWaitUntilScheduled();
3737
}
3838

39-
bool CommandBuffer::SubmitCommandsAsync(
40-
std::shared_ptr<RenderPass>
41-
render_pass // NOLINT(performance-unnecessary-value-param)
42-
) {
43-
TRACE_EVENT0("impeller", "CommandBuffer::SubmitCommandsAsync");
39+
bool CommandBuffer::EncodeAndSubmit(
40+
const std::shared_ptr<RenderPass>& render_pass) {
41+
TRACE_EVENT0("impeller", "CommandBuffer::EncodeAndSubmit");
4442
if (!render_pass->IsValid() || !IsValid()) {
4543
return false;
4644
}
@@ -51,6 +49,20 @@ bool CommandBuffer::SubmitCommandsAsync(
5149
return SubmitCommands(nullptr);
5250
}
5351

52+
bool CommandBuffer::EncodeAndSubmit(
53+
const std::shared_ptr<BlitPass>& blit_pass,
54+
const std::shared_ptr<Allocator>& allocator) {
55+
TRACE_EVENT0("impeller", "CommandBuffer::EncodeAndSubmit");
56+
if (!blit_pass->IsValid() || !IsValid()) {
57+
return false;
58+
}
59+
if (!blit_pass->EncodeCommands(allocator)) {
60+
return false;
61+
}
62+
63+
return SubmitCommands(nullptr);
64+
}
65+
5466
std::shared_ptr<RenderPass> CommandBuffer::CreateRenderPass(
5567
const RenderTarget& render_target) {
5668
auto pass = OnCreateRenderPass(render_target);

impeller/renderer/command_buffer.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,20 @@ class CommandBuffer {
8080
///
8181
/// A command buffer may only be committed once.
8282
///
83-
[[nodiscard]] virtual bool SubmitCommandsAsync(
84-
std::shared_ptr<RenderPass> render_pass);
83+
[[nodiscard]] virtual bool EncodeAndSubmit(
84+
const std::shared_ptr<RenderPass>& render_pass);
85+
86+
//----------------------------------------------------------------------------
87+
/// @brief Schedule the command encoded by blit passes within this
88+
/// command buffer on the GPU. The enqueing of this buffer is
89+
/// performed immediately but encoding is pushed to a worker
90+
/// thread if possible.
91+
///
92+
/// A command buffer may only be committed once.
93+
///
94+
[[nodiscard]] virtual bool EncodeAndSubmit(
95+
const std::shared_ptr<BlitPass>& blit_pass,
96+
const std::shared_ptr<Allocator>& allocator);
8597

8698
//----------------------------------------------------------------------------
8799
/// @brief Force execution of pending GPU commands.

0 commit comments

Comments
 (0)