| Flutter Impeller |

ahb_swapchain_impl_vk.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_swapchain_impl_vk.h"
6
7 #include "flutter/fml/trace_event.h"
8 #include "impeller/base/validation.h"
9 #include "impeller/renderer/backend/vulkan/barrier_vk.h"
10 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
11 #include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
12 #include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
13 #include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
14 #include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h"
15 #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h"
16 #include "impeller/toolkit/android/surface_transaction.h"
17 #include "impeller/toolkit/android/surface_transaction_stats.h"
18
19 namespace impeller {
20
21 //------------------------------------------------------------------------------
22 /// The maximum number of presents pending in the compositor after which the
23 /// acquire calls will block. This value is 2 images given to the system
24 /// compositor and one for the raster thread, Because the semaphore is acquired
25 /// when the CPU Begins working on the texture
26 ///
27 static constexpr const size_t kMaxPendingPresents = 3u;
28
29 static TextureDescriptor ToSwapchainTextureDescriptor(
30 const android::HardwareBufferDescriptor& ahb_desc) {
31 TextureDescriptor desc;
32 desc.storage_mode = StorageMode::kDevicePrivate;
33 desc.type = TextureType::kTexture2D;
34 desc.format = ToPixelFormat(ahb_desc.format);
35 desc.size = ahb_desc.size;
36 desc.mip_count = 1u;
37 desc.usage = TextureUsage::kRenderTarget;
38 desc.sample_count = SampleCount::kCount1;
39 desc.compression_type = CompressionType::kLossless;
40 return desc;
41 }
42
43 std::shared_ptr<AHBSwapchainImplVK> AHBSwapchainImplVK::Create(
44 const std::weak_ptr<Context>& context,
45 std::weak_ptr<android::SurfaceControl> surface_control,
46 const ISize& size,
47 bool enable_msaa,
48 size_t swapchain_image_count) {
49 auto impl = std::shared_ptr<AHBSwapchainImplVK>(
50 new AHBSwapchainImplVK(context, std::move(surface_control), size,
51 enable_msaa, swapchain_image_count));
52 return impl->IsValid() ? impl : nullptr;
53 }
54
55 AHBSwapchainImplVK::AHBSwapchainImplVK(
56 const std::weak_ptr<Context>& context,
57 std::weak_ptr<android::SurfaceControl> surface_control,
58 const ISize& size,
59 bool enable_msaa,
60 size_t swapchain_image_count)
61 : surface_control_(std::move(surface_control)),
62 pending_presents_(std::make_shared<fml::Semaphore>(kMaxPendingPresents)) {
63 desc_ = android::HardwareBufferDescriptor::MakeForSwapchainImage(size);
64 pool_ =
65 std::make_shared<AHBTexturePoolVK>(context, desc_, swapchain_image_count);
66 if (!pool_->IsValid()) {
67 return;
68 }
69 transients_ = std::make_shared<SwapchainTransientsVK>(
70 context, ToSwapchainTextureDescriptor(desc_), enable_msaa);
71 is_valid_ = true;
72 }
73
74 AHBSwapchainImplVK::~AHBSwapchainImplVK() = default;
75
76 const ISize& AHBSwapchainImplVK::GetSize() const {
77 return desc_.size;
78 }
79
80 bool AHBSwapchainImplVK::IsValid() const {
81 return is_valid_;
82 }
83
84 const android::HardwareBufferDescriptor& AHBSwapchainImplVK::GetDescriptor()
85 const {
86 return desc_;
87 }
88
89 std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
90 {
91 TRACE_EVENT0("impeller", "CompositorPendingWait");
92 if (!pending_presents_->Wait()) {
93 return nullptr;
94 }
95 }
96
97 AutoSemaSignaler auto_sema_signaler =
98 std::make_shared<fml::ScopedCleanupClosure>(
99 [sema = pending_presents_]() { sema->Signal(); });
100
101 if (!is_valid_) {
102 return nullptr;
103 }
104
105 auto pool_entry = pool_->Pop();
106
107 if (!pool_entry.IsValid()) {
108 VALIDATION_LOG << "Could not create AHB texture source.";
109 return nullptr;
110 }
111
112 auto context = transients_->GetContext().lock();
113 if (context) {
114 ContextVK::Cast(*context).GetGPUTracer()->MarkFrameStart();
115 }
116
117 // Ask the GPU to wait for the render ready semaphore to be signaled before
118 // performing rendering operations.
119 if (!SubmitWaitForRenderReady(pool_entry.render_ready_fence,
120 pool_entry.texture)) {
121 VALIDATION_LOG << "Could not submit a command to the GPU to wait on render "
122 "readiness.";
123 return nullptr;
124 }
125
126 auto surface = SurfaceVK::WrapSwapchainImage(
127 transients_, pool_entry.texture,
128 [signaler = auto_sema_signaler, weak = weak_from_this(),
129 texture = pool_entry.texture]() {
130 auto thiz = weak.lock();
131 if (!thiz) {
132 VALIDATION_LOG << "Swapchain died before image could be presented.";
133 return false;
134 }
135 return thiz->Present(signaler, texture);
136 });
137
138 if (!surface) {
139 return nullptr;
140 }
141
142 return surface;
143 }
144
145 bool AHBSwapchainImplVK::Present(
146 const AutoSemaSignaler& signaler,
147 const std::shared_ptr<AHBTextureSourceVK>& texture) {
148 auto control = surface_control_.lock();
149 if (!control || !control->IsValid()) {
150 VALIDATION_LOG << "Surface control died before swapchain image could be "
151 "presented.";
152 return false;
153 }
154
155 auto context = transients_->GetContext().lock();
156 if (context) {
157 ContextVK::Cast(*context).GetGPUTracer()->MarkFrameEnd();
158 }
159
160 if (!texture) {
161 return false;
162 }
163
164 auto fence = SubmitSignalForPresentReady(texture);
165
166 if (!fence) {
167 VALIDATION_LOG << "Could not submit completion signal.";
168 return false;
169 }
170
171 android::SurfaceTransaction transaction;
172 if (!transaction.SetContents(control.get(), //
173 texture->GetBackingStore(), //
174 fence->CreateFD() //
175 )) {
176 VALIDATION_LOG << "Could not set swapchain image contents on the surface "
177 "control.";
178 return false;
179 }
180 return transaction.Apply([signaler, texture, weak = weak_from_this()](
181 ASurfaceTransactionStats* stats) {
182 auto thiz = weak.lock();
183 if (!thiz) {
184 return;
185 }
186 thiz->OnTextureUpdatedOnSurfaceControl(signaler, texture, stats);
187 });
188 }
189
190 std::shared_ptr<ExternalFenceVK>
191 AHBSwapchainImplVK::SubmitSignalForPresentReady(
192 const std::shared_ptr<AHBTextureSourceVK>& texture) const {
193 auto context = transients_->GetContext().lock();
194 if (!context) {
195 return nullptr;
196 }
197 auto fence = std::make_shared<ExternalFenceVK>(context);
198 if (!fence || !fence->IsValid()) {
199 return nullptr;
200 }
201
202 auto command_buffer = context->CreateCommandBuffer();
203 if (!command_buffer) {
204 return nullptr;
205 }
206 command_buffer->SetLabel("AHBSubmitSignalForPresentReadyCB");
207 const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder();
208
209 const auto command_encoder_vk = encoder->GetCommandBuffer();
210
211 BarrierVK barrier;
212 barrier.cmd_buffer = command_encoder_vk;
213 barrier.new_layout = vk::ImageLayout::eGeneral;
214 barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
215 barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
216 barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
217 barrier.dst_access = {};
218
219 if (!texture->SetLayout(barrier).ok()) {
220 return nullptr;
221 }
222
223 encoder->Track(fence->GetSharedHandle());
224
225 if (!encoder->EndCommandBuffer()) {
226 return nullptr;
227 }
228
229 vk::SubmitInfo submit_info;
230 submit_info.setCommandBuffers(command_encoder_vk);
231
232 auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
233 submit_info, fence->GetHandle());
234 if (result != vk::Result::eSuccess) {
235 return nullptr;
236 }
237 return fence;
238 }
239
240 vk::UniqueSemaphore AHBSwapchainImplVK::CreateRenderReadySemaphore(
241 const std::shared_ptr<fml::UniqueFD>& fd) const {
242 if (!fd->is_valid()) {
243 return {};
244 }
245
246 auto context = transients_->GetContext().lock();
247 if (!context) {
248 return {};
249 }
250
251 const auto& context_vk = ContextVK::Cast(*context);
252 const auto& device = context_vk.GetDevice();
253
254 auto signal_wait = device.createSemaphoreUnique({});
255
256 if (signal_wait.result != vk::Result::eSuccess) {
257 return {};
258 }
259
260 context_vk.SetDebugName(*signal_wait.value, "AHBRenderReadySemaphore");
261
262 vk::ImportSemaphoreFdInfoKHR import_info;
263 import_info.semaphore = *signal_wait.value;
264 import_info.fd = fd->get();
265 import_info.handleType = vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd;
266 // From the spec: Sync FDs can only be imported temporarily.
267 import_info.flags = vk::SemaphoreImportFlagBitsKHR::eTemporary;
268
269 const auto import_result = device.importSemaphoreFdKHR(import_info);
270
271 if (import_result != vk::Result::eSuccess) {
272 VALIDATION_LOG << "Could not import semaphore FD: "
273 << vk::to_string(import_result);
274 return {};
275 }
276
277 // From the spec: Importing a semaphore payload from a file descriptor
278 // transfers ownership of the file descriptor from the application to the
279 // Vulkan implementation. The application must not perform any operations on
280 // the file descriptor after a successful import.
281 [[maybe_unused]] auto released = fd->release();
282
283 return std::move(signal_wait.value);
284 }
285
286 bool AHBSwapchainImplVK::SubmitWaitForRenderReady(
287 const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
288 const std::shared_ptr<AHBTextureSourceVK>& texture) const {
289 // If there is no render ready fence, we are already ready to render into
290 // the texture. There is nothing more to do.
291 if (!render_ready_fence || !render_ready_fence->is_valid()) {
292 return true;
293 }
294
295 auto context = transients_->GetContext().lock();
296 if (!context) {
297 return false;
298 }
299
300 auto completion_fence =
301 ContextVK::Cast(*context).GetDevice().createFenceUnique({}).value;
302 if (!completion_fence) {
303 return false;
304 }
305
306 auto command_buffer = context->CreateCommandBuffer();
307 if (!command_buffer) {
308 return false;
309 }
310 command_buffer->SetLabel("AHBSubmitWaitForRenderReadyCB");
311 const auto& encoder = CommandBufferVK::Cast(*command_buffer).GetEncoder();
312
313 const auto command_buffer_vk = encoder->GetCommandBuffer();
314
315 BarrierVK barrier;
316 barrier.cmd_buffer = command_buffer_vk;
317 barrier.new_layout = vk::ImageLayout::eColorAttachmentOptimal;
318 barrier.src_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
319 barrier.src_access = {};
320 barrier.dst_stage = vk::PipelineStageFlagBits::eTopOfPipe;
321 barrier.dst_access = {};
322
323 if (!texture->SetLayout(barrier).ok()) {
324 return false;
325 }
326
327 auto render_ready_semaphore =
328 MakeSharedVK(CreateRenderReadySemaphore(render_ready_fence));
329 encoder->Track(render_ready_semaphore);
330
331 if (!encoder->EndCommandBuffer()) {
332 return false;
333 }
334
335 vk::SubmitInfo submit_info;
336
337 if (render_ready_semaphore) {
338 static constexpr const auto kWaitStages =
339 vk::PipelineStageFlagBits::eColorAttachmentOutput |
340 vk::PipelineStageFlagBits::eFragmentShader |
341 vk::PipelineStageFlagBits::eTransfer;
342 submit_info.setWaitSemaphores(render_ready_semaphore->Get());
343 submit_info.setWaitDstStageMask(kWaitStages);
344 }
345
346 submit_info.setCommandBuffers(command_buffer_vk);
347
348 auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
349 submit_info, *completion_fence);
350 if (result != vk::Result::eSuccess) {
351 return false;
352 }
353
354 ContextVK::Cast(*context).GetFenceWaiter()->AddFence(
355 std::move(completion_fence), [encoder]() {});
356
357 return true;
358 }
359
360 void AHBSwapchainImplVK::OnTextureUpdatedOnSurfaceControl(
361 const AutoSemaSignaler& signaler,
362 std::shared_ptr<AHBTextureSourceVK> texture,
363 ASurfaceTransactionStats* stats) {
364 auto control = surface_control_.lock();
365 if (!control) {
366 return;
367 }
368
369 // Ask for an FD that gets signaled when the previous buffer is released. This
370 // can be invalid if there is no wait necessary.
371 auto render_ready_fence =
372 android::CreatePreviousReleaseFence(*control, stats);
373
374 // The transaction completion indicates that the surface control now
375 // references the hardware buffer. We can recycle the previous set buffer
376 // safely.
377 Lock lock(currently_displayed_texture_mutex_);
378 auto old_texture = currently_displayed_texture_;
379 currently_displayed_texture_ = std::move(texture);
380 pool_->Push(std::move(old_texture), std::move(render_ready_fence));
381 }
382
383 } // namespace impeller
impeller::AHBSwapchainImplVK::GetDescriptor
const android::HardwareBufferDescriptor & GetDescriptor() const
Get the descriptor used to create the hardware buffers that will be displayed on the surface control.
Definition: ahb_swapchain_impl_vk.cc:84
fence_waiter_vk.h
impeller::SurfaceVK::WrapSwapchainImage
static std::unique_ptr< SurfaceVK > WrapSwapchainImage(const std::shared_ptr< SwapchainTransientsVK > &transients, const std::shared_ptr< TextureSourceVK > &swapchain_image, SwapCallback swap_callback)
Wrap the swapchain image in a Surface, which provides the additional configuration required for usage...
Definition: surface_vk.cc:13
impeller::AHBSwapchainImplVK::AcquireNextDrawable
std::unique_ptr< Surface > AcquireNextDrawable()
Acquire the next surface that can be used to present to the swapchain.
Definition: ahb_swapchain_impl_vk.cc:89
gpu_tracer_vk.h
barrier_vk.h
ahb_formats.h
surface_vk.h
command_encoder_vk.h
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
surface_transaction_stats.h
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:43
impeller::android::CreatePreviousReleaseFence
fml::UniqueFD CreatePreviousReleaseFence(const SurfaceControl &control, ASurfaceTransactionStats *stats)
Definition: surface_transaction_stats.cc:11
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::AHBSwapchainImplVK::Create
static std::shared_ptr< AHBSwapchainImplVK > Create(const std::weak_ptr< Context > &context, std::weak_ptr< android::SurfaceControl > surface_control, const ISize &size, bool enable_msaa, size_t swapchain_image_count)
Create a swapchain of a specific size whose images will be presented to the provided surface control.
Definition: ahb_swapchain_impl_vk.cc:43
impeller::TextureDescriptor::sample_count
SampleCount sample_count
Definition: texture_descriptor.h:45
validation.h
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:44
impeller::ToSwapchainTextureDescriptor
static TextureDescriptor ToSwapchainTextureDescriptor(const android::HardwareBufferDescriptor &ahb_desc)
Definition: ahb_swapchain_impl_vk.cc:29
impeller::kMaxPendingPresents
static constexpr const size_t kMaxPendingPresents
Definition: ahb_swapchain_impl_vk.cc:27
impeller::android::HardwareBufferDescriptor::MakeForSwapchainImage
static HardwareBufferDescriptor MakeForSwapchainImage(const ISize &size)
Create a descriptor of the given size that is suitable for use as a swapchain image.
Definition: hardware_buffer.cc:87
impeller::TextureDescriptor::type
TextureType type
Definition: texture_descriptor.h:40
impeller::AHBSwapchainImplVK::AHBSwapchainImplVK
AHBSwapchainImplVK(const AHBSwapchainImplVK &)=delete
command_buffer_vk.h
impeller::TSize
Definition: size.h:19
ahb_swapchain_impl_vk.h
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::CompressionType::kLossless
@ kLossless
impeller::android::HardwareBufferDescriptor
A descriptor use to specify hardware buffer allocations.
Definition: hardware_buffer.h:47
impeller::TextureType::kTexture2D
@ kTexture2D
impeller::ContextVK::GetGPUTracer
std::shared_ptr< GPUTracerVK > GetGPUTracer() const
Definition: context_vk.cc:568
impeller::android::HardwareBufferDescriptor::size
ISize size
Definition: hardware_buffer.h:49
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
surface_transaction.h
impeller::MakeSharedVK
auto MakeSharedVK(vk::UniqueHandle< T, VULKAN_HPP_DEFAULT_DISPATCHER_TYPE > handle)
Definition: shared_object_vk.h:44
std
Definition: comparable.h:95
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
impeller::SampleCount::kCount1
@ kCount1
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::android::HardwareBufferDescriptor::format
HardwareBufferFormat format
Definition: hardware_buffer.h:48
impeller::AHBSwapchainImplVK::~AHBSwapchainImplVK
~AHBSwapchainImplVK()
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:38
impeller::AHBSwapchainImplVK::GetSize
const ISize & GetSize() const
Definition: ahb_swapchain_impl_vk.cc:76
impeller::AHBSwapchainImplVK::IsValid
bool IsValid() const
Definition: ahb_swapchain_impl_vk.cc:80
impeller::TextureDescriptor::compression_type
CompressionType compression_type
Definition: texture_descriptor.h:46
impeller
Definition: aiks_blend_unittests.cc:18
impeller::ToPixelFormat
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
Definition: ahb_texture_source_vk.cc:221