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 
6 
7 #include "flutter/fml/trace_event.h"
17 #include "vulkan/vulkan_to_string.hpp"
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 
30  const android::HardwareBufferDescriptor& ahb_desc) {
31  TextureDescriptor desc;
34  desc.format = ToPixelFormat(ahb_desc.format);
35  desc.size = ahb_desc.size;
36  desc.mip_count = 1u;
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 
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)) {
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 
72  auto control = surface_control_.lock();
73  is_valid_ = control && control->IsValid();
74 }
75 
77 
79  return desc_.size;
80 }
81 
83  return is_valid_;
84 }
85 
87  const {
88  return desc_;
89 }
90 
91 std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
92  {
93  TRACE_EVENT0("impeller", "CompositorPendingWait");
94  if (!pending_presents_->Wait()) {
95  return nullptr;
96  }
97  }
98 
99  AutoSemaSignaler auto_sema_signaler =
100  std::make_shared<fml::ScopedCleanupClosure>(
101  [sema = pending_presents_]() { sema->Signal(); });
102 
103  if (!is_valid_) {
104  return nullptr;
105  }
106 
107  auto pool_entry = pool_->Pop();
108 
109  if (!pool_entry.IsValid()) {
110  VALIDATION_LOG << "Could not create AHB texture source.";
111  return nullptr;
112  }
113 
114  // Ask the GPU to wait for the render ready semaphore to be signaled before
115  // performing rendering operations.
116  if (!SubmitWaitForRenderReady(pool_entry.render_ready_fence,
117  pool_entry.texture)) {
118  VALIDATION_LOG << "Could wait on render ready fence.";
119  return nullptr;
120  }
121 
122 #if IMPELLER_DEBUG
123  auto context = transients_->GetContext().lock();
124  if (context) {
125  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameStart();
126  }
127 #endif // IMPELLER_DEBUG
128 
129  auto surface = SurfaceVK::WrapSwapchainImage(
130  transients_, pool_entry.texture,
131  [signaler = auto_sema_signaler, weak = weak_from_this(),
132  texture = pool_entry.texture]() {
133  auto thiz = weak.lock();
134  if (!thiz) {
135  VALIDATION_LOG << "Swapchain died before image could be presented.";
136  return false;
137  }
138  return thiz->Present(signaler, texture);
139  });
140 
141  if (!surface) {
142  return nullptr;
143  }
144 
145  return surface;
146 }
147 
148 bool AHBSwapchainImplVK::Present(
149  const AutoSemaSignaler& signaler,
150  const std::shared_ptr<AHBTextureSourceVK>& texture) {
151  auto control = surface_control_.lock();
152  if (!control || !control->IsValid()) {
153  VALIDATION_LOG << "Surface control died before swapchain image could be "
154  "presented.";
155  return false;
156  }
157 
158 #if IMPELLER_DEBUG
159  auto context = transients_->GetContext().lock();
160  if (context) {
161  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameEnd();
162  }
163 #endif // IMPELLER_DEBUG
164 
165  if (!texture) {
166  return false;
167  }
168 
169  auto fence = SubmitSignalForPresentReady(texture);
170 
171  if (!fence) {
172  VALIDATION_LOG << "Could not submit completion signal.";
173  return false;
174  }
175 
176  android::SurfaceTransaction transaction;
177  if (!transaction.SetContents(control.get(), //
178  texture->GetBackingStore(), //
179  fence->CreateFD() //
180  )) {
181  VALIDATION_LOG << "Could not set swapchain image contents on the surface "
182  "control.";
183  return false;
184  }
185  return transaction.Apply([signaler, texture, weak = weak_from_this()](
186  ASurfaceTransactionStats* stats) {
187  auto thiz = weak.lock();
188  if (!thiz) {
189  return;
190  }
191  thiz->OnTextureUpdatedOnSurfaceControl(signaler, texture, stats);
192  });
193 }
194 
195 std::shared_ptr<ExternalFenceVK>
196 AHBSwapchainImplVK::SubmitSignalForPresentReady(
197  const std::shared_ptr<AHBTextureSourceVK>& texture) const {
198  auto context = transients_->GetContext().lock();
199  if (!context) {
200  return nullptr;
201  }
202  auto fence = std::make_shared<ExternalFenceVK>(context);
203  if (!fence || !fence->IsValid()) {
204  return nullptr;
205  }
206 
207  auto command_buffer = context->CreateCommandBuffer();
208  if (!command_buffer) {
209  return nullptr;
210  }
211  command_buffer->SetLabel("AHBSubmitSignalForPresentReadyCB");
212  CommandBufferVK& command_buffer_vk = CommandBufferVK::Cast(*command_buffer);
213 
214  const auto command_encoder_vk = command_buffer_vk.GetCommandBuffer();
215 
216  BarrierVK barrier;
217  barrier.cmd_buffer = command_encoder_vk;
218  barrier.new_layout = vk::ImageLayout::eGeneral;
219  barrier.src_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
220  barrier.src_access = vk::AccessFlagBits::eColorAttachmentWrite;
221  barrier.dst_stage = vk::PipelineStageFlagBits::eBottomOfPipe;
222  barrier.dst_access = {};
223 
224  if (!texture->SetLayout(barrier).ok()) {
225  return nullptr;
226  }
227 
228  command_buffer_vk.Track(fence->GetSharedHandle());
229 
230  if (!command_buffer_vk.EndCommandBuffer()) {
231  return nullptr;
232  }
233 
234  vk::SubmitInfo submit_info;
235  submit_info.setCommandBuffers(command_encoder_vk);
236 
237  auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
238  submit_info, fence->GetHandle());
239  if (result != vk::Result::eSuccess) {
240  return nullptr;
241  }
242  return fence;
243 }
244 
245 vk::UniqueFence AHBSwapchainImplVK::CreateRenderReadyFence(
246  const std::shared_ptr<fml::UniqueFD>& fd) const {
247  if (!fd->is_valid()) {
248  return {};
249  }
250 
251  auto context = transients_->GetContext().lock();
252  if (!context) {
253  return {};
254  }
255 
256  const auto& context_vk = ContextVK::Cast(*context);
257  const auto& device = context_vk.GetDevice();
258 
259  auto signal_wait = device.createFenceUnique({});
260 
261  if (signal_wait.result != vk::Result::eSuccess) {
262  return {};
263  }
264 
265  context_vk.SetDebugName(*signal_wait.value, "AHBRenderReadyFence");
266 
267  vk::ImportFenceFdInfoKHR import_info;
268  import_info.fence = *signal_wait.value;
269  import_info.fd = fd->get();
270  import_info.handleType = vk::ExternalFenceHandleTypeFlagBits::eSyncFd;
271  // From the spec: Sync FDs can only be imported temporarily.
272  import_info.flags = vk::FenceImportFlagBitsKHR::eTemporary;
273 
274  const auto import_result = device.importFenceFdKHR(import_info);
275 
276  if (import_result != vk::Result::eSuccess) {
277  VALIDATION_LOG << "Could not import semaphore FD: "
278  << vk::to_string(import_result);
279  return {};
280  }
281 
282  // From the spec: Importing a semaphore payload from a file descriptor
283  // transfers ownership of the file descriptor from the application to the
284  // Vulkan implementation. The application must not perform any operations on
285  // the file descriptor after a successful import.
286  [[maybe_unused]] auto released = fd->release();
287 
288  return std::move(signal_wait.value);
289 }
290 
291 bool AHBSwapchainImplVK::SubmitWaitForRenderReady(
292  const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
293  const std::shared_ptr<AHBTextureSourceVK>& texture) const {
294  // If there is no render ready fence, we are already ready to render into
295  // the texture. There is nothing more to do.
296  if (!render_ready_fence || !render_ready_fence->is_valid()) {
297  return true;
298  }
299 
300  auto context = transients_->GetContext().lock();
301  if (!context) {
302  return false;
303  }
304 
305  auto fence = CreateRenderReadyFence(render_ready_fence);
306 
307  auto result = ContextVK::Cast(*context).GetDevice().waitForFences(
308  *fence, // fence
309  true, // wait all
310  std::numeric_limits<uint64_t>::max() // timeout (ns)
311  );
312 
313  if (!(result == vk::Result::eSuccess || result == vk::Result::eTimeout)) {
314  VALIDATION_LOG << "Encountered error while waiting on swapchain image: "
315  << vk::to_string(result);
316  return false;
317  }
318 
319  return true;
320 }
321 
322 void AHBSwapchainImplVK::OnTextureUpdatedOnSurfaceControl(
323  const AutoSemaSignaler& signaler,
324  std::shared_ptr<AHBTextureSourceVK> texture,
325  ASurfaceTransactionStats* stats) {
326  auto control = surface_control_.lock();
327  if (!control) {
328  return;
329  }
330 
331  // Ask for an FD that gets signaled when the previous buffer is released. This
332  // can be invalid if there is no wait necessary.
333  auto render_ready_fence =
334  android::CreatePreviousReleaseFence(*control, stats);
335 
336  // The transaction completion indicates that the surface control now
337  // references the hardware buffer. We can recycle the previous set buffer
338  // safely.
339  Lock lock(currently_displayed_texture_mutex_);
340  auto old_texture = currently_displayed_texture_;
341  currently_displayed_texture_ = std::move(texture);
342  pool_->Push(std::move(old_texture), std::move(render_ready_fence));
343 }
344 
345 } // 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:86
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:91
gpu_tracer_vk.h
barrier_vk.h
ahb_formats.h
surface_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:580
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:91
surface_transaction.h
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:78
impeller::AHBSwapchainImplVK::IsValid
bool IsValid() const
Definition: ahb_swapchain_impl_vk.cc:82
impeller::TextureDescriptor::compression_type
CompressionType compression_type
Definition: texture_descriptor.h:46
impeller
Definition: allocation.cc:12
impeller::ToPixelFormat
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
Definition: ahb_texture_source_vk.cc:221