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 
14 
15 namespace impeller {
16 
18  const android::HardwareBufferDescriptor& ahb_desc) {
19  TextureDescriptor desc;
22  desc.format = ToPixelFormat(ahb_desc.format);
23  desc.size = ahb_desc.size;
24  desc.mip_count = 1u;
28  return desc;
29 }
30 
32  auto acquire_res = device.createFenceUnique({});
33  if (acquire_res.result != vk::Result::eSuccess) {
34  VALIDATION_LOG << "Could not create synchronizer.";
35  return;
36  }
37  acquire = std::move(acquire_res.value);
38  is_valid = true;
39 }
40 
42 
44  return is_valid;
45 }
46 
47 bool AHBFrameSynchronizerVK::WaitForFence(const vk::Device& device) {
48  if (!acquire_fence_pending) {
49  return true;
50  }
51  if (auto result = device.waitForFences(
52  *acquire, // fence
53  true, // wait all
54  std::numeric_limits<uint64_t>::max() // timeout (ns)
55  );
56  result != vk::Result::eSuccess) {
57  VALIDATION_LOG << "Fence wait failed: " << vk::to_string(result);
58  return false;
59  }
60  acquire_fence_pending = false;
61  if (auto result = device.resetFences(*acquire);
62  result != vk::Result::eSuccess) {
63  VALIDATION_LOG << "Could not reset fence: " << vk::to_string(result);
64  return false;
65  }
66  return true;
67 }
68 
69 std::shared_ptr<AHBSwapchainImplVK> AHBSwapchainImplVK::Create(
70  const std::weak_ptr<Context>& context,
71  std::weak_ptr<android::SurfaceControl> surface_control,
72  const CreateTransactionCB& cb,
73  const ISize& size,
74  bool enable_msaa) {
75  auto impl = std::shared_ptr<AHBSwapchainImplVK>(new AHBSwapchainImplVK(
76  context, std::move(surface_control), cb, size, enable_msaa));
77  return impl->IsValid() ? impl : nullptr;
78 }
79 
81  const std::weak_ptr<Context>& context,
82  std::weak_ptr<android::SurfaceControl> surface_control,
83  const CreateTransactionCB& cb,
84  const ISize& size,
85  bool enable_msaa)
86  : surface_control_(std::move(surface_control)), cb_(cb) {
88  pool_ = std::make_shared<AHBTexturePoolVK>(context, desc_);
89  if (!pool_->IsValid()) {
90  return;
91  }
92  transients_ = std::make_shared<SwapchainTransientsVK>(
93  context, ToSwapchainTextureDescriptor(desc_), enable_msaa);
94 
95  for (auto i = 0u; i < kMaxPendingPresents; i++) {
96  auto sync = std::make_unique<AHBFrameSynchronizerVK>(
97  ContextVK::Cast(*context.lock()).GetDeviceHolder()->GetDevice());
98  if (!sync->IsValid()) {
99  return;
100  }
101  frame_data_.push_back(std::move(sync));
102  }
103 
104  auto control = surface_control_.lock();
105  is_valid_ = control && control->IsValid();
106 }
107 
109  // Ensure that the Vulkan device is idle before destroying objects that the
110  // device may have been using.
111  WaitIdle();
112 }
113 
115  return desc_.size;
116 }
117 
119  return is_valid_;
120 }
121 
123  const {
124  return desc_;
125 }
126 
127 void AHBSwapchainImplVK::WaitIdle() const {
128  if (transients_) {
129  if (auto context = transients_->GetContext().lock()) {
130  auto result = ContextVK::Cast(*context).GetDevice().waitIdle();
131  if (result != vk::Result::eSuccess) {
132  FML_LOG(INFO) << "Device waitIdle failed: " << vk::to_string(result);
133  }
134  }
135  }
136 }
137 
138 std::unique_ptr<Surface> AHBSwapchainImplVK::AcquireNextDrawable() {
139  auto context = transients_->GetContext().lock();
140  if (!context) {
141  return nullptr;
142  }
143 
144  frame_index_ = (frame_index_ + 1) % kMaxPendingPresents;
145 
146  if (!frame_data_[frame_index_]->WaitForFence(
147  ContextVK::Cast(*context).GetDevice())) {
148  return nullptr;
149  }
150 
151  if (!is_valid_) {
152  return nullptr;
153  }
154 
155  auto pool_entry = pool_->Pop();
156 
157  if (!pool_entry.IsValid()) {
158  VALIDATION_LOG << "Could not create AHB texture source.";
159  return nullptr;
160  }
161 
162  // Import the render ready semaphore that will block onscreen rendering until
163  // it is ready.
164  if (!ImportRenderReady(pool_entry.render_ready_fence, pool_entry.texture)) {
165  VALIDATION_LOG << "Could wait on render ready fence.";
166  return nullptr;
167  }
168 
169 #if IMPELLER_DEBUG
170  if (context) {
171  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameStart();
172  }
173 #endif // IMPELLER_DEBUG
174 
175  auto surface = SurfaceVK::WrapSwapchainImage(
176  transients_, pool_entry.texture,
177  [weak = weak_from_this(), texture = pool_entry.texture]() {
178  auto thiz = weak.lock();
179  if (!thiz) {
180  VALIDATION_LOG << "Swapchain died before image could be presented.";
181  return false;
182  }
183  return thiz->Present(texture);
184  });
185 
186  if (!surface) {
187  return nullptr;
188  }
189 
190  return surface;
191 }
192 
193 bool AHBSwapchainImplVK::Present(
194  const std::shared_ptr<AHBTextureSourceVK>& texture) {
195  auto control = surface_control_.lock();
196  if (!control || !control->IsValid()) {
197  VALIDATION_LOG << "Surface control died before swapchain image could be "
198  "presented.";
199  return false;
200  }
201 
202 #if IMPELLER_DEBUG
203  auto context = transients_->GetContext().lock();
204  if (context) {
205  ContextVK::Cast(*context).GetGPUTracer()->MarkFrameEnd();
206  }
207 #endif // IMPELLER_DEBUG
208 
209  if (!texture) {
210  return false;
211  }
212 
213  auto present_ready = SubmitSignalForPresentReady(texture);
214 
215  if (!present_ready) {
216  VALIDATION_LOG << "Could not submit completion signal.";
217  return false;
218  }
219 
220  android::SurfaceTransaction transaction =
221  (cb_) ? cb_() : impeller::android::SurfaceTransaction();
222  if (!transaction.SetContents(control.get(), //
223  texture->GetBackingStore(), //
224  present_ready->CreateFD() //
225  )) {
226  VALIDATION_LOG << "Could not set swapchain image contents on the surface "
227  "control.";
228  return false;
229  }
230  return transaction.Apply(
231  [texture, weak = weak_from_this()](ASurfaceTransactionStats* stats) {
232  auto thiz = weak.lock();
233  if (!thiz) {
234  return;
235  }
236  thiz->OnTextureUpdatedOnSurfaceControl(texture, stats);
237  });
238 }
239 
240 void AHBSwapchainImplVK::AddFinalCommandBuffer(
241  std::shared_ptr<CommandBuffer> cmd_buffer) {
242  frame_data_[frame_index_]->final_cmd_buffer = std::move(cmd_buffer);
243 }
244 
245 std::shared_ptr<ExternalSemaphoreVK>
246 AHBSwapchainImplVK::SubmitSignalForPresentReady(
247  const std::shared_ptr<AHBTextureSourceVK>& texture) const {
248  auto context = transients_->GetContext().lock();
249  if (!context) {
250  return nullptr;
251  }
252 
253  auto present_ready = std::make_shared<ExternalSemaphoreVK>(context);
254  if (!present_ready || !present_ready->IsValid()) {
255  return nullptr;
256  }
257 
258  auto& sync = frame_data_[frame_index_];
259  auto command_buffer = sync->final_cmd_buffer;
260  if (!command_buffer) {
261  return nullptr;
262  }
263  CommandBufferVK& command_buffer_vk = CommandBufferVK::Cast(*command_buffer);
264  const auto command_encoder_vk = command_buffer_vk.GetCommandBuffer();
265  if (!command_buffer_vk.EndCommandBuffer()) {
266  return nullptr;
267  }
268  sync->present_ready = present_ready;
269 
270  vk::SubmitInfo submit_info;
271  vk::PipelineStageFlags wait_stage =
272  vk::PipelineStageFlagBits::eColorAttachmentOutput;
273  if (sync->render_ready) {
274  submit_info.setPWaitSemaphores(&sync->render_ready.get());
275  submit_info.setWaitSemaphoreCount(1);
276  submit_info.setWaitDstStageMask(wait_stage);
277  }
278  submit_info.setCommandBuffers(command_encoder_vk);
279  submit_info.setPSignalSemaphores(&sync->present_ready->GetHandle());
280  submit_info.setSignalSemaphoreCount(1);
281 
282  auto result = ContextVK::Cast(*context).GetGraphicsQueue()->Submit(
283  submit_info, *sync->acquire);
284  if (result != vk::Result::eSuccess) {
285  return nullptr;
286  }
287  sync->acquire_fence_pending = true;
288  return present_ready;
289 }
290 
291 vk::UniqueSemaphore AHBSwapchainImplVK::CreateRenderReadySemaphore(
292  const std::shared_ptr<fml::UniqueFD>& fd) const {
293  if (!fd->is_valid()) {
294  return {};
295  }
296 
297  auto context = transients_->GetContext().lock();
298  if (!context) {
299  return {};
300  }
301 
302  const auto& context_vk = ContextVK::Cast(*context);
303  const auto& device = context_vk.GetDevice();
304 
305  auto signal_wait = device.createSemaphoreUnique({});
306  if (signal_wait.result != vk::Result::eSuccess) {
307  return {};
308  }
309 
310  context_vk.SetDebugName(*signal_wait.value, "AHBRenderReadySemaphore");
311 
312  vk::ImportSemaphoreFdInfoKHR import_info;
313  import_info.semaphore = *signal_wait.value;
314  import_info.fd = fd->get();
315  import_info.handleType = vk::ExternalSemaphoreHandleTypeFlagBits::eSyncFd;
316  // From the spec: Sync FDs can only be imported temporarily.
317  import_info.flags = vk::SemaphoreImportFlagBitsKHR::eTemporary;
318 
319  const auto import_result = device.importSemaphoreFdKHR(import_info);
320 
321  if (import_result != vk::Result::eSuccess) {
322  VALIDATION_LOG << "Could not import semaphore FD: "
323  << vk::to_string(import_result);
324  return {};
325  }
326 
327  // From the spec: Importing a semaphore payload from a file descriptor
328  // transfers ownership of the file descriptor from the application to the
329  // Vulkan implementation. The application must not perform any operations on
330  // the file descriptor after a successful import.
331  [[maybe_unused]] auto released = fd->release();
332 
333  return std::move(signal_wait.value);
334 }
335 
336 bool AHBSwapchainImplVK::ImportRenderReady(
337  const std::shared_ptr<fml::UniqueFD>& render_ready_fence,
338  const std::shared_ptr<AHBTextureSourceVK>& texture) {
339  auto context = transients_->GetContext().lock();
340  if (!context) {
341  return false;
342  }
343 
344  // If there is no render ready fence, we are already ready to render into
345  // the texture. There is nothing more to do.
346  if (!render_ready_fence || !render_ready_fence->is_valid()) {
347  frame_data_[frame_index_]->render_ready = {};
348  return true;
349  }
350 
351  auto semaphore = CreateRenderReadySemaphore(render_ready_fence);
352  if (!semaphore) {
353  return false;
354  }
355  // This semaphore will be later used to block the onscreen render pass
356  // from starting until the system is done reading the onscreen.
357  frame_data_[frame_index_]->render_ready = std::move(semaphore);
358  return true;
359 }
360 
361 void AHBSwapchainImplVK::OnTextureUpdatedOnSurfaceControl(
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
static std::shared_ptr< AHBSwapchainImplVK > Create(const std::weak_ptr< Context > &context, std::weak_ptr< android::SurfaceControl > surface_control, const CreateTransactionCB &cb, const ISize &size, bool enable_msaa)
Create a swapchain of a specific size whose images will be presented to the provided surface control.
AHBSwapchainImplVK(const AHBSwapchainImplVK &)=delete
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
const vk::Device & GetDevice() const
Definition: context_vk.cc:591
std::shared_ptr< GPUTracerVK > GetGPUTracer() const
Definition: context_vk.cc:641
std::shared_ptr< DeviceHolderVK > GetDeviceHolder() const
Definition: context_vk.h:191
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
A wrapper for ASurfaceTransaction. https://developer.android.com/ndk/reference/group/native-activity#...
fml::UniqueFD CreatePreviousReleaseFence(const SurfaceControl &control, ASurfaceTransactionStats *stats)
std::function< android::SurfaceTransaction()> CreateTransactionCB
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition: formats_vk.h:219
static constexpr const size_t kMaxPendingPresents
static TextureDescriptor ToSwapchainTextureDescriptor(const android::HardwareBufferDescriptor &ahb_desc)
Definition: comparable.h:93
std::shared_ptr< ContextGLES > context
std::shared_ptr< CommandBuffer > command_buffer
AHBFrameSynchronizerVK(const vk::Device &device)
bool WaitForFence(const vk::Device &device)
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