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