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

Commit ca99a7c

Browse files
committed
[Impeller] Wait for the previous AHB texture to be fully recyclable.
The Android surface transaction completion handler documentation states: ``` Buffers which are replaced or removed from the scene in the transaction invoking this callback may be reused after this point. ``` However, this is NOT sufficient. One also needs to be obtain the previous release fence and perform a wait for the buffer to be safely reusable. This patch adds a GPU side wait for this fence using available extensions.
1 parent 5d94a52 commit ca99a7c

13 files changed

+287
-63
lines changed

impeller/renderer/backend/vulkan/capabilities_vk.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ static const char* GetExtensionName(RequiredAndroidDeviceExtensionVK ext) {
183183
return VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME;
184184
case RequiredAndroidDeviceExtensionVK::kKHRExternalFence:
185185
return VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME;
186+
case RequiredAndroidDeviceExtensionVK::kKHRExternalSemaphoreFd:
187+
return VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME;
188+
case RequiredAndroidDeviceExtensionVK::kKHRExternalSemaphore:
189+
return VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME;
186190
case RequiredAndroidDeviceExtensionVK::kLast:
187191
return "Unknown";
188192
}

impeller/renderer/backend/vulkan/capabilities_vk.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ enum class RequiredAndroidDeviceExtensionVK : uint32_t {
9292
///
9393
kKHRExternalFence,
9494

95+
//----------------------------------------------------------------------------
96+
/// For importing sync file descriptors as semaphores so the GPU can wait for
97+
/// semaphore to be signaled.
98+
///
99+
/// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_semaphore_fd.html
100+
kKHRExternalSemaphoreFd,
101+
102+
//----------------------------------------------------------------------------
103+
/// Dependency of kKHRExternalSemaphoreFd
104+
///
105+
/// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_semaphore.html
106+
kKHRExternalSemaphore,
107+
95108
kLast,
96109
};
97110

impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.cc

Lines changed: 158 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
#include "impeller/renderer/backend/vulkan/barrier_vk.h"
1010
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
1111
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
12+
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
1213
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
1314
#include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h"
1415
#include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h"
1516
#include "impeller/toolkit/android/surface_transaction.h"
17+
#include "impeller/toolkit/android/surface_transaction_stats.h"
1618

1719
namespace impeller {
1820

@@ -96,9 +98,9 @@ std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
9698
return nullptr;
9799
}
98100

99-
auto texture = pool_->Pop();
101+
auto pool_entry = pool_->Pop();
100102

101-
if (!texture) {
103+
if (!pool_entry.IsValid()) {
102104
VALIDATION_LOG << "Could not create AHB texture source.";
103105
return nullptr;
104106
}
@@ -108,9 +110,19 @@ std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
108110
ContextVK::Cast(*context).GetGPUTracer()->MarkFrameStart();
109111
}
110112

113+
// Ask the GPU to wait for the render ready semaphore to be signaled before
114+
// performing rendering operations.
115+
if (!SubmitWaitForRenderReady(pool_entry.render_ready_fence,
116+
pool_entry.texture)) {
117+
VALIDATION_LOG << "Could not submit a command to the GPU to wait on render "
118+
"readiness.";
119+
return nullptr;
120+
}
121+
111122
auto surface = SurfaceVK::WrapSwapchainImage(
112-
transients_, texture,
113-
[signaler = auto_sema_signaler, weak = weak_from_this(), texture]() {
123+
transients_, pool_entry.texture,
124+
[signaler = auto_sema_signaler, weak = weak_from_this(),
125+
texture = pool_entry.texture]() {
114126
auto thiz = weak.lock();
115127
if (!thiz) {
116128
VALIDATION_LOG << "Swapchain died before image could be presented.";
@@ -145,7 +157,7 @@ bool AHBSwapchainImplVK::Present(
145157
return false;
146158
}
147159

148-
auto fence = SubmitCompletionSignal(texture);
160+
auto fence = SubmitSignalForPresentReady(texture);
149161

150162
if (!fence) {
151163
VALIDATION_LOG << "Could not submit completion signal.";
@@ -161,16 +173,18 @@ bool AHBSwapchainImplVK::Present(
161173
"control.";
162174
return false;
163175
}
164-
return transaction.Apply([signaler, texture, weak = weak_from_this()]() {
176+
return transaction.Apply([signaler, texture, weak = weak_from_this()](
177+
ASurfaceTransactionStats* stats) {
165178
auto thiz = weak.lock();
166179
if (!thiz) {
167180
return;
168181
}
169-
thiz->OnTextureSetOnSurfaceControl(signaler, texture);
182+
thiz->OnTextureUpdatedOnSurfaceControl(signaler, texture, stats);
170183
});
171184
}
172185

173-
std::shared_ptr<ExternalFenceVK> AHBSwapchainImplVK::SubmitCompletionSignal(
186+
std::shared_ptr<ExternalFenceVK>
187+
AHBSwapchainImplVK::SubmitSignalForPresentReady(
174188
const std::shared_ptr<AHBTextureSourceVK>& texture) const {
175189
auto context = transients_->GetContext().lock();
176190
if (!context) {
@@ -185,7 +199,7 @@ std::shared_ptr<ExternalFenceVK> AHBSwapchainImplVK::SubmitCompletionSignal(
185199
if (!command_buffer) {
186200
return nullptr;
187201
}
188-
command_buffer->SetLabel("AHBPresentCommandBuffer");
202+
command_buffer->SetLabel("AHBSubmitSignalForPresentReadyCB");
189203
const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder();
190204

191205
const auto command_encoder_vk = encoder->GetCommandBuffer();
@@ -219,17 +233,148 @@ std::shared_ptr<ExternalFenceVK> AHBSwapchainImplVK::SubmitCompletionSignal(
219233
return fence;
220234
}
221235

222-
void AHBSwapchainImplVK::OnTextureSetOnSurfaceControl(
236+
vk::UniqueSemaphore AHBSwapchainImplVK::CreateRenderReadySemaphore(
237+
const std::shared_ptr<fml::UniqueFD>& fd) const {
238+
if (!fd->is_valid()) {
239+
return {};
240+
}
241+
242+
auto context = transients_->GetContext().lock();
243+
if (!context) {
244+
return {};
245+
}
246+
247+
const auto& context_vk = ContextVK::Cast(*context);
248+
249+
const auto& device = context_vk.GetDevice();
250+
251+
auto signal_wait = device.createSemaphoreUnique({});
252+
253+
if (signal_wait.result != vk::Result::eSuccess) {
254+
return {};
255+
}
256+
257+
context_vk.SetDebugName(*signal_wait.value, "AHBRenderReadySemaphore");
258+
259+
vk::ImportSemaphoreFdInfoKHR import_info;
260+
import_info.semaphore = *signal_wait.value;
261+
import_info.fd = fd->get();
262+
import_info.handleType = vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd;
263+
// From the spec: Sync FDs can only be imported temporarily.
264+
import_info.flags = vk::SemaphoreImportFlagBitsKHR::eTemporary;
265+
266+
const auto import_result = device.importSemaphoreFdKHR(import_info);
267+
268+
if (import_result != vk::Result::eSuccess) {
269+
VALIDATION_LOG << "Could not import semaphore FD: "
270+
<< vk::to_string(import_result);
271+
return {};
272+
}
273+
274+
// From the spec: Importing a semaphore payload from a file descriptor
275+
// transfers ownership of the file descriptor from the application to the
276+
// Vulkan implementation. The application must not perform any operations on
277+
// the file descriptor after a successful import.
278+
[[maybe_unused]] auto released = fd->release();
279+
280+
return std::move(signal_wait.value);
281+
}
282+
283+
bool AHBSwapchainImplVK::SubmitWaitForRenderReady(
284+
const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
285+
const std::shared_ptr<AHBTextureSourceVK>& texture) const {
286+
// If there is no render ready fence, we are already ready to render into
287+
// the texture. There is nothing more to do.
288+
if (!render_ready_fence || !render_ready_fence->is_valid()) {
289+
return true;
290+
}
291+
292+
auto context = transients_->GetContext().lock();
293+
if (!context) {
294+
return false;
295+
}
296+
297+
auto completion_fence =
298+
ContextVK::Cast(*context).GetDevice().createFenceUnique({}).value;
299+
if (!completion_fence) {
300+
return false;
301+
}
302+
303+
auto command_buffer = context->CreateCommandBuffer();
304+
if (!command_buffer) {
305+
return false;
306+
}
307+
command_buffer->SetLabel("AHBSubmitWaitForRenderReadyCB");
308+
const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder();
309+
310+
const auto command_buffer_vk = encoder->GetCommandBuffer();
311+
312+
BarrierVK barrier;
313+
barrier.cmd_buffer = command_buffer_vk;
314+
barrier.new_layout = vk::ImageLayout::eColorAttachmentOptimal;
315+
barrier.src_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
316+
barrier.src_access = {};
317+
barrier.dst_stage = vk::PipelineStageFlagBits::eTopOfPipe;
318+
barrier.dst_access = {};
319+
320+
if (!texture->SetLayout(barrier).ok()) {
321+
return false;
322+
}
323+
324+
auto render_ready_semaphore =
325+
MakeSharedVK(CreateRenderReadySemaphore(render_ready_fence));
326+
encoder->Track(render_ready_semaphore);
327+
328+
if (!encoder->EndCommandBuffer()) {
329+
return false;
330+
}
331+
332+
vk::SubmitInfo submit_info;
333+
334+
if (render_ready_semaphore) {
335+
constexpr const auto kWaitStages =
336+
vk::PipelineStageFlagBits::eColorAttachmentOutput |
337+
vk::PipelineStageFlagBits::eFragmentShader |
338+
vk::PipelineStageFlagBits::eTransfer;
339+
submit_info.setWaitSemaphores(render_ready_semaphore->Get());
340+
submit_info.setWaitDstStageMask(kWaitStages);
341+
}
342+
343+
submit_info.setCommandBuffers(command_buffer_vk);
344+
345+
auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
346+
submit_info, *completion_fence);
347+
if (result != vk::Result::eSuccess) {
348+
return false;
349+
}
350+
351+
ContextVK::Cast(*context).GetFenceWaiter()->AddFence(
352+
std::move(completion_fence), [encoder]() {});
353+
354+
return true;
355+
}
356+
357+
void AHBSwapchainImplVK::OnTextureUpdatedOnSurfaceControl(
223358
const AutoSemaSignaler& signaler,
224-
std::shared_ptr<AHBTextureSourceVK> texture) {
225-
signaler->Reset();
359+
std::shared_ptr<AHBTextureSourceVK> texture,
360+
ASurfaceTransactionStats* stats) {
361+
auto control = surface_control_.lock();
362+
if (!control) {
363+
return;
364+
}
365+
366+
// Ask for an FD that gets signaled when the previous buffer is released. This
367+
// can be invalid if there is no wait necessary.
368+
auto render_ready_fence =
369+
android::CreatePreviousReleaseFence(*control, stats);
370+
226371
// The transaction completion indicates that the surface control now
227372
// references the hardware buffer. We can recycle the previous set buffer
228373
// safely.
229374
Lock lock(currently_displayed_texture_mutex_);
230375
auto old_texture = currently_displayed_texture_;
231376
currently_displayed_texture_ = std::move(texture);
232-
pool_->Push(std::move(old_texture));
377+
pool_->Push(std::move(old_texture), std::move(render_ready_fence));
233378
}
234379

235380
} // namespace impeller

impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,20 @@ class AHBSwapchainImplVK final
112112
bool Present(const AutoSemaSignaler& signaler,
113113
const std::shared_ptr<AHBTextureSourceVK>& texture);
114114

115-
std::shared_ptr<ExternalFenceVK> SubmitCompletionSignal(
115+
vk::UniqueSemaphore CreateRenderReadySemaphore(
116+
const std::shared_ptr<fml::UniqueFD>& fd) const;
117+
118+
bool SubmitWaitForRenderReady(
119+
const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
120+
const std::shared_ptr<AHBTextureSourceVK>& texture) const;
121+
122+
std::shared_ptr<ExternalFenceVK> SubmitSignalForPresentReady(
116123
const std::shared_ptr<AHBTextureSourceVK>& texture) const;
117124

118-
void OnTextureSetOnSurfaceControl(
125+
void OnTextureUpdatedOnSurfaceControl(
119126
const AutoSemaSignaler& signaler,
120-
std::shared_ptr<AHBTextureSourceVK> texture);
127+
std::shared_ptr<AHBTextureSourceVK> texture,
128+
ASurfaceTransactionStats* stats);
121129
};
122130

123131
} // namespace impeller

impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,25 @@ AHBTexturePoolVK::AHBTexturePoolVK(std::weak_ptr<Context> context,
2525

2626
AHBTexturePoolVK::~AHBTexturePoolVK() = default;
2727

28-
std::shared_ptr<AHBTextureSourceVK> AHBTexturePoolVK::Pop() {
28+
AHBTexturePoolVK::PoolEntry AHBTexturePoolVK::Pop() {
2929
{
3030
Lock lock(pool_mutex_);
3131
if (!pool_.empty()) {
32-
auto texture = pool_.back().item;
32+
auto entry = pool_.back();
3333
pool_.pop_back();
34-
return texture;
34+
return entry;
3535
}
3636
}
37-
return CreateTexture();
37+
return PoolEntry{CreateTexture()};
3838
}
3939

40-
void AHBTexturePoolVK::Push(std::shared_ptr<AHBTextureSourceVK> texture) {
40+
void AHBTexturePoolVK::Push(std::shared_ptr<AHBTextureSourceVK> texture,
41+
fml::UniqueFD render_ready_fence) {
4142
if (!texture) {
4243
return;
4344
}
4445
Lock lock(pool_mutex_);
45-
pool_.push_back(PoolEntry{std::move(texture)});
46+
pool_.push_back(PoolEntry{std::move(texture), std::move(render_ready_fence)});
4647
PerformGCLocked();
4748
}
4849

impeller/renderer/backend/vulkan/swapchain/ahb/ahb_texture_pool_vk.h

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <deque>
99

10+
#include "flutter/fml/unique_fd.h"
1011
#include "impeller/base/thread.h"
1112
#include "impeller/base/timing.h"
1213
#include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h"
@@ -29,6 +30,21 @@ namespace impeller {
2930
///
3031
class AHBTexturePoolVK {
3132
public:
33+
struct PoolEntry {
34+
TimePoint last_access_time;
35+
std::shared_ptr<AHBTextureSourceVK> texture;
36+
std::shared_ptr<fml::UniqueFD> render_ready_fence;
37+
38+
explicit PoolEntry(std::shared_ptr<AHBTextureSourceVK> p_item,
39+
fml::UniqueFD p_render_ready_fence = {})
40+
: last_access_time(Clock::now()),
41+
texture(std::move(p_item)),
42+
render_ready_fence(std::make_shared<fml::UniqueFD>(
43+
std::move(p_render_ready_fence))) {}
44+
45+
constexpr bool IsValid() const { return !!texture; }
46+
};
47+
3248
//----------------------------------------------------------------------------
3349
/// @brief Create a new (empty) texture pool.
3450
///
@@ -72,7 +88,7 @@ class AHBTexturePoolVK {
7288
/// @return A texture source that can be used as a swapchain image. This
7389
/// can be nullptr in case of resource exhaustion.
7490
///
75-
std::shared_ptr<AHBTextureSourceVK> Pop();
91+
PoolEntry Pop();
7692

7793
//----------------------------------------------------------------------------
7894
/// @brief Push a popped texture back into the pool. This also performs a
@@ -86,7 +102,8 @@ class AHBTexturePoolVK {
86102
///
87103
/// @param[in] texture The texture to be returned to the pool.
88104
///
89-
void Push(std::shared_ptr<AHBTextureSourceVK> texture);
105+
void Push(std::shared_ptr<AHBTextureSourceVK> texture,
106+
fml::UniqueFD render_ready_fence);
90107

91108
//----------------------------------------------------------------------------
92109
/// @brief Perform an explicit GC of the pool items. This happens
@@ -97,14 +114,6 @@ class AHBTexturePoolVK {
97114
void PerformGC();
98115

99116
private:
100-
struct PoolEntry {
101-
TimePoint last_access_time;
102-
std::shared_ptr<AHBTextureSourceVK> item;
103-
104-
explicit PoolEntry(std::shared_ptr<AHBTextureSourceVK> p_item)
105-
: last_access_time(Clock::now()), item(std::move(p_item)) {}
106-
};
107-
108117
const std::weak_ptr<Context> context_;
109118
const android::HardwareBufferDescriptor desc_;
110119
const size_t max_entries_;

impeller/toolkit/android/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ impeller_component("android") {
2323
"surface_control.h",
2424
"surface_transaction.cc",
2525
"surface_transaction.h",
26+
"surface_transaction_stats.cc",
27+
"surface_transaction_stats.h",
2628
]
2729

2830
public_deps = [

0 commit comments

Comments
 (0)