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"
14 
15 namespace impeller {
16 
17 //------------------------------------------------------------------------------
18 /// The maximum number of presents pending in the compositor after which the
19 /// acquire calls will block. This value is 2 images given to the system
20 /// compositor and one for the raster thread, Because the semaphore is acquired
21 /// when the CPU begins working on the texture
22 ///
23 static constexpr const size_t kMaxPendingPresents = 3u;
24 
26  const android::HardwareBufferDescriptor& ahb_desc) {
27  TextureDescriptor desc;
30  desc.format = ToPixelFormat(ahb_desc.format);
31  desc.size = ahb_desc.size;
32  desc.mip_count = 1u;
36  return desc;
37 }
38 
39 std::shared_ptr<AHBSwapchainImplVK> AHBSwapchainImplVK::Create(
40  const std::weak_ptr<Context>& context,
41  std::weak_ptr<android::SurfaceControl> surface_control,
42  const ISize& size,
43  bool enable_msaa,
44  size_t swapchain_image_count) {
45  auto impl = std::shared_ptr<AHBSwapchainImplVK>(
46  new AHBSwapchainImplVK(context, std::move(surface_control), size,
47  enable_msaa, swapchain_image_count));
48  return impl->IsValid() ? impl : nullptr;
49 }
50 
52  const std::weak_ptr<Context>& context,
53  std::weak_ptr<android::SurfaceControl> surface_control,
54  const ISize& size,
55  bool enable_msaa,
56  size_t swapchain_image_count)
57  : surface_control_(std::move(surface_control)),
58  pending_presents_(std::make_shared<fml::Semaphore>(kMaxPendingPresents)) {
60  pool_ =
61  std::make_shared<AHBTexturePoolVK>(context, desc_, swapchain_image_count);
62  if (!pool_->IsValid()) {
63  return;
64  }
65  transients_ = std::make_shared<SwapchainTransientsVK>(
66  context, ToSwapchainTextureDescriptor(desc_), enable_msaa);
67 
68  auto control = surface_control_.lock();
69  is_valid_ = control && control->IsValid();
70 }
71 
73 
75  return desc_.size;
76 }
77 
79  return is_valid_;
80 }
81 
83  const {
84  return desc_;
85 }
86 
87 std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
88  {
89  TRACE_EVENT0("impeller", "CompositorPendingWait");
90  if (!pending_presents_->Wait()) {
91  return nullptr;
92  }
93  }
94 
95  frame_index_ = (frame_index_ + 1) % kMaxPendingPresents;
96  AutoSemaSignaler auto_sema_signaler =
97  std::make_shared<fml::ScopedCleanupClosure>(
98  [sema = pending_presents_]() { sema->Signal(); });
99 
100  if (!is_valid_) {
101  return nullptr;
102  }
103 
104  auto pool_entry = pool_->Pop();
105 
106  if (!pool_entry.IsValid()) {
107  VALIDATION_LOG << "Could not create AHB texture source.";
108  return nullptr;
109  }
110 
111  // Import the render ready semaphore that will block onscreen rendering until
112  // it is ready.
113  if (!SubmitWaitForRenderReady(pool_entry.render_ready_fence,
114  pool_entry.texture)) {
115  VALIDATION_LOG << "Could wait on render ready fence.";
116  return nullptr;
117  }
118 
119 #if IMPELLER_DEBUG
120  auto context = transients_->GetContext().lock();
121  if (context) {
122  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameStart();
123  }
124 #endif // IMPELLER_DEBUG
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 #if IMPELLER_DEBUG
156  auto context = transients_->GetContext().lock();
157  if (context) {
158  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameEnd();
159  }
160 #endif // IMPELLER_DEBUG
161 
162  if (!texture) {
163  return false;
164  }
165 
166  auto fence = SubmitSignalForPresentReady(texture);
167 
168  if (!fence) {
169  VALIDATION_LOG << "Could not submit completion signal.";
170  return false;
171  }
172 
173  android::SurfaceTransaction transaction;
174  if (!transaction.SetContents(control.get(), //
175  texture->GetBackingStore(), //
176  fence->CreateFD() //
177  )) {
178  VALIDATION_LOG << "Could not set swapchain image contents on the surface "
179  "control.";
180  return false;
181  }
182  return transaction.Apply([signaler, texture, weak = weak_from_this()](
183  ASurfaceTransactionStats* stats) {
184  auto thiz = weak.lock();
185  if (!thiz) {
186  return;
187  }
188  thiz->OnTextureUpdatedOnSurfaceControl(signaler, texture, stats);
189  });
190 }
191 
192 void AHBSwapchainImplVK::AddFinalCommandBuffer(
193  std::shared_ptr<CommandBuffer> cmd_buffer) {
194  frame_data_[frame_index_].command_buffer = std::move(cmd_buffer);
195 }
196 
197 std::shared_ptr<ExternalFenceVK>
198 AHBSwapchainImplVK::SubmitSignalForPresentReady(
199  const std::shared_ptr<AHBTextureSourceVK>& texture) const {
200  auto context = transients_->GetContext().lock();
201  if (!context) {
202  return nullptr;
203  }
204  auto fence = std::make_shared<ExternalFenceVK>(context);
205  if (!fence || !fence->IsValid()) {
206  return nullptr;
207  }
208 
209  auto command_buffer = frame_data_[frame_index_].command_buffer;
210  if (!command_buffer) {
211  return nullptr;
212  }
213  CommandBufferVK& command_buffer_vk = CommandBufferVK::Cast(*command_buffer);
214  const auto command_encoder_vk = command_buffer_vk.GetCommandBuffer();
215  command_buffer_vk.Track(fence->GetSharedHandle());
216 
217  if (!command_buffer_vk.EndCommandBuffer()) {
218  return nullptr;
219  }
220 
221  vk::SubmitInfo submit_info;
222  vk::PipelineStageFlags wait_stage =
223  vk::PipelineStageFlagBits::eColorAttachmentOutput;
224  if (frame_data_[frame_index_].semaphore) {
225  submit_info.setPWaitSemaphores(&frame_data_[frame_index_].semaphore.get());
226  submit_info.setWaitSemaphoreCount(1);
227  submit_info.setWaitDstStageMask(wait_stage);
228  }
229  submit_info.setCommandBuffers(command_encoder_vk);
230 
231  auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
232  submit_info, fence->GetHandle());
233  if (result != vk::Result::eSuccess) {
234  return nullptr;
235  }
236  return fence;
237 }
238 
239 vk::UniqueSemaphore AHBSwapchainImplVK::CreateRenderReadySemaphore(
240  const std::shared_ptr<fml::UniqueFD>& fd) const {
241  if (!fd->is_valid()) {
242  return {};
243  }
244 
245  auto context = transients_->GetContext().lock();
246  if (!context) {
247  return {};
248  }
249 
250  const auto& context_vk = ContextVK::Cast(*context);
251  const auto& device = context_vk.GetDevice();
252 
253  auto signal_wait = device.createSemaphoreUnique({});
254 
255  if (signal_wait.result != vk::Result::eSuccess) {
256  return {};
257  }
258 
259  context_vk.SetDebugName(*signal_wait.value, "AHBRenderReadySemaphore");
260 
261  vk::ImportSemaphoreFdInfoKHR import_info;
262  import_info.semaphore = *signal_wait.value;
263  import_info.fd = fd->get();
264  import_info.handleType = vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd;
265  // From the spec: Sync FDs can only be imported temporarily.
266  import_info.flags = vk::SemaphoreImportFlagBitsKHR::eTemporary;
267 
268  const auto import_result = device.importSemaphoreFdKHR(import_info);
269 
270  if (import_result != vk::Result::eSuccess) {
271  VALIDATION_LOG << "Could not import semaphore FD: "
272  << vk::to_string(import_result);
273  return {};
274  }
275 
276  // From the spec: Importing a semaphore payload from a file descriptor
277  // transfers ownership of the file descriptor from the application to the
278  // Vulkan implementation. The application must not perform any operations on
279  // the file descriptor after a successful import.
280  [[maybe_unused]] auto released = fd->release();
281 
282  return std::move(signal_wait.value);
283 }
284 
285 bool AHBSwapchainImplVK::SubmitWaitForRenderReady(
286  const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
287  const std::shared_ptr<AHBTextureSourceVK>& texture) {
288  // If there is no render ready fence, we are already ready to render into
289  // the texture. There is nothing more to do.
290  if (!render_ready_fence || !render_ready_fence->is_valid()) {
291  frame_data_[frame_index_].semaphore = {};
292  return true;
293  }
294 
295  auto context = transients_->GetContext().lock();
296  if (!context) {
297  return false;
298  }
299 
300  auto semaphore = CreateRenderReadySemaphore(render_ready_fence);
301  if (!semaphore) {
302  return false;
303  }
304  // This semaphore will be later used to block the onscreen render pass
305  // from starting until the system is done reading the onscreen.
306  frame_data_[frame_index_].semaphore = std::move(semaphore);
307  return true;
308 }
309 
310 void AHBSwapchainImplVK::OnTextureUpdatedOnSurfaceControl(
311  const AutoSemaSignaler& signaler,
312  std::shared_ptr<AHBTextureSourceVK> texture,
313  ASurfaceTransactionStats* stats) {
314  auto control = surface_control_.lock();
315  if (!control) {
316  return;
317  }
318 
319  // Ask for an FD that gets signaled when the previous buffer is released. This
320  // can be invalid if there is no wait necessary.
321  auto render_ready_fence =
322  android::CreatePreviousReleaseFence(*control, stats);
323 
324  // The transaction completion indicates that the surface control now
325  // references the hardware buffer. We can recycle the previous set buffer
326  // safely.
327  Lock lock(currently_displayed_texture_mutex_);
328  auto old_texture = currently_displayed_texture_;
329  currently_displayed_texture_ = std::move(texture);
330  pool_->Push(std::move(old_texture), std::move(render_ready_fence));
331 }
332 
333 } // namespace impeller
AHBSwapchainImplVK(const AHBSwapchainImplVK &)=delete
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.
std::unique_ptr< Surface > AcquireNextDrawable()
Acquire the next surface that can be used to present to the swapchain.
const android::HardwareBufferDescriptor & GetDescriptor() const
Get the descriptor used to create the hardware buffers that will be displayed on the surface control.
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
std::shared_ptr< GPUTracerVK > GetGPUTracer() const
Definition: context_vk.cc:637
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
fml::UniqueFD CreatePreviousReleaseFence(const SurfaceControl &control, ASurfaceTransactionStats *stats)
static constexpr const size_t kMaxPendingPresents
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
static TextureDescriptor ToSwapchainTextureDescriptor(const android::HardwareBufferDescriptor &ahb_desc)
Definition: comparable.h:95
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
A descriptor use to specify hardware buffer allocations.
static HardwareBufferDescriptor MakeForSwapchainImage(const ISize &size)
Create a descriptor of the given size that is suitable for use as a swapchain image.
#define VALIDATION_LOG
Definition: validation.h:91