| 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 "impeller/base/validation.h"
8 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
9 #include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.h"
10 #include "impeller/renderer/backend/vulkan/swapchain/ahb/external_semaphore_vk.h"
11 #include "impeller/renderer/backend/vulkan/swapchain/surface_vk.h"
12 #include "impeller/toolkit/android/surface_transaction.h"
13 #include "impeller/toolkit/android/surface_transaction_stats.h"
14
15 namespace impeller {
16
17 static TextureDescriptor ToSwapchainTextureDescriptor(
18 const android::HardwareBufferDescriptor& ahb_desc) {
19 TextureDescriptor desc;
20 desc.storage_mode = StorageMode::kDevicePrivate;
21 desc.type = TextureType::kTexture2D;
22 desc.format = ToPixelFormat(ahb_desc.format);
23 desc.size = ahb_desc.size;
24 desc.mip_count = 1u;
25 desc.usage = TextureUsage::kRenderTarget;
26 desc.sample_count = SampleCount::kCount1;
27 desc.compression_type = CompressionType::kLossless;
28 return desc;
29 }
30
31 AHBFrameSynchronizerVK::AHBFrameSynchronizerVK(const vk::Device& device) {
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
42 AHBFrameSynchronizerVK::~AHBFrameSynchronizerVK() = default;
43
44 bool AHBFrameSynchronizerVK::IsValid() const {
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
79 AHBSwapchainImplVK::AHBSwapchainImplVK(
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) {
87 desc_ = android::HardwareBufferDescriptor::MakeForSwapchainImage(size);
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
109 AHBSwapchainImplVK::~AHBSwapchainImplVK() = default;
110
111 const ISize& AHBSwapchainImplVK::GetSize() const {
112 return desc_.size;
113 }
114
115 bool AHBSwapchainImplVK::IsValid() const {
116 return is_valid_;
117 }
118
119 const android::HardwareBufferDescriptor& AHBSwapchainImplVK::GetDescriptor()
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
ahb_formats.h
ahb_swapchain_impl_vk.h
impeller::AHBSwapchainImplVK::IsValid
bool IsValid() const
Definition: ahb_swapchain_impl_vk.cc:115
impeller::AHBSwapchainImplVK::AHBSwapchainImplVK
AHBSwapchainImplVK(const AHBSwapchainImplVK &)=delete
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:124
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:119
impeller::AHBSwapchainImplVK::Create
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.
Definition: ahb_swapchain_impl_vk.cc:66
impeller::AHBSwapchainImplVK::~AHBSwapchainImplVK
~AHBSwapchainImplVK()
impeller::AHBSwapchainImplVK::GetSize
const ISize & GetSize() const
Definition: ahb_swapchain_impl_vk.cc:111
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
impeller::ContextVK::GetDevice
const vk::Device & GetDevice() const
Definition: context_vk.cc:591
impeller::ContextVK::GetGPUTracer
std::shared_ptr< GPUTracerVK > GetGPUTracer() const
Definition: context_vk.cc:641
impeller::ContextVK::GetDeviceHolder
std::shared_ptr< DeviceHolderVK > GetDeviceHolder() const
Definition: context_vk.h:191
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
command_buffer_vk.h
external_semaphore_vk.h
impeller::android::CreatePreviousReleaseFence
fml::UniqueFD CreatePreviousReleaseFence(const SurfaceControl &control, ASurfaceTransactionStats *stats)
Definition: surface_transaction_stats.cc:11
impeller
Definition: allocation.cc:12
impeller::CreateTransactionCB
std::function< android::SurfaceTransaction()> CreateTransactionCB
Definition: ahb_swapchain_impl_vk.h:24
impeller::ToPixelFormat
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition: formats_vk.h:183
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::kMaxPendingPresents
static constexpr const size_t kMaxPendingPresents
Definition: ahb_swapchain_impl_vk.h:26
impeller::TextureType::kTexture2D
@ kTexture2D
impeller::CompressionType::kLossless
@ kLossless
impeller::ToSwapchainTextureDescriptor
static TextureDescriptor ToSwapchainTextureDescriptor(const android::HardwareBufferDescriptor &ahb_desc)
Definition: ahb_swapchain_impl_vk.cc:17
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::SampleCount::kCount1
@ kCount1
std
Definition: comparable.h:95
surface_vk.h
impeller::AHBFrameSynchronizerVK::~AHBFrameSynchronizerVK
~AHBFrameSynchronizerVK()
impeller::AHBFrameSynchronizerVK::is_valid
bool is_valid
Definition: ahb_swapchain_impl_vk.h:33
impeller::AHBFrameSynchronizerVK::IsValid
bool IsValid() const
Definition: ahb_swapchain_impl_vk.cc:44
impeller::AHBFrameSynchronizerVK::AHBFrameSynchronizerVK
AHBFrameSynchronizerVK(const vk::Device &device)
Definition: ahb_swapchain_impl_vk.cc:31
impeller::AHBFrameSynchronizerVK::WaitForFence
bool WaitForFence(const vk::Device &device)
Definition: ahb_swapchain_impl_vk.cc:48
impeller::AHBFrameSynchronizerVK::acquire
vk::UniqueFence acquire
Definition: ahb_swapchain_impl_vk.h:29
impeller::TSize
Definition: size.h:25
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::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:44
impeller::TextureDescriptor::compression_type
CompressionType compression_type
Definition: texture_descriptor.h:46
impeller::TextureDescriptor::type
TextureType type
Definition: texture_descriptor.h:40
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:42
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:39
impeller::TextureDescriptor::sample_count
SampleCount sample_count
Definition: texture_descriptor.h:45
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:43
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:41
impeller::android::HardwareBufferDescriptor
A descriptor use to specify hardware buffer allocations.
Definition: hardware_buffer.h:47
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::android::HardwareBufferDescriptor::size
ISize size
Definition: hardware_buffer.h:49
impeller::android::HardwareBufferDescriptor::format
HardwareBufferFormat format
Definition: hardware_buffer.h:48
surface_transaction.h
surface_transaction_stats.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91