| 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 "flutter/fml/trace_event.h"
8 #include "impeller/base/validation.h"
9 #include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
10 #include "impeller/renderer/backend/vulkan/swapchain/ahb/ahb_formats.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 //------------------------------------------------------------------------------
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
25 static TextureDescriptor ToSwapchainTextureDescriptor(
26 const android::HardwareBufferDescriptor& ahb_desc) {
27 TextureDescriptor desc;
28 desc.storage_mode = StorageMode::kDevicePrivate;
29 desc.type = TextureType::kTexture2D;
30 desc.format = ToPixelFormat(ahb_desc.format);
31 desc.size = ahb_desc.size;
32 desc.mip_count = 1u;
33 desc.usage = TextureUsage::kRenderTarget;
34 desc.sample_count = SampleCount::kCount1;
35 desc.compression_type = CompressionType::kLossless;
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
51 AHBSwapchainImplVK::AHBSwapchainImplVK(
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)) {
59 desc_ = android::HardwareBufferDescriptor::MakeForSwapchainImage(size);
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
72 AHBSwapchainImplVK::~AHBSwapchainImplVK() = default;
73
74 const ISize& AHBSwapchainImplVK::GetSize() const {
75 return desc_.size;
76 }
77
78 bool AHBSwapchainImplVK::IsValid() const {
79 return is_valid_;
80 }
81
82 const android::HardwareBufferDescriptor& AHBSwapchainImplVK::GetDescriptor()
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
ahb_formats.h
ahb_swapchain_impl_vk.h
impeller::AHBSwapchainImplVK::IsValid
bool IsValid() const
Definition: ahb_swapchain_impl_vk.cc:78
impeller::AHBSwapchainImplVK::AHBSwapchainImplVK
AHBSwapchainImplVK(const AHBSwapchainImplVK &)=delete
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:39
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:87
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:82
impeller::AHBSwapchainImplVK::~AHBSwapchainImplVK
~AHBSwapchainImplVK()
impeller::AHBSwapchainImplVK::GetSize
const ISize & GetSize() const
Definition: ahb_swapchain_impl_vk.cc:74
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
impeller::ContextVK::GetGPUTracer
std::shared_ptr< GPUTracerVK > GetGPUTracer() const
Definition: context_vk.cc:637
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
fml
Definition: metal_screenshot.h:16
impeller::android::CreatePreviousReleaseFence
fml::UniqueFD CreatePreviousReleaseFence(const SurfaceControl &control, ASurfaceTransactionStats *stats)
Definition: surface_transaction_stats.cc:11
impeller
Definition: allocation.cc:12
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::kMaxPendingPresents
static constexpr const size_t kMaxPendingPresents
Definition: ahb_swapchain_impl_vk.cc:23
impeller::TextureType::kTexture2D
@ kTexture2D
impeller::CompressionType::kLossless
@ kLossless
impeller::ToPixelFormat
static PixelFormat ToPixelFormat(AHardwareBuffer_Format format)
Definition: ahb_texture_source_vk.cc:225
impeller::ToSwapchainTextureDescriptor
static TextureDescriptor ToSwapchainTextureDescriptor(const android::HardwareBufferDescriptor &ahb_desc)
Definition: ahb_swapchain_impl_vk.cc:25
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::SampleCount::kCount1
@ kCount1
std
Definition: comparable.h:95
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
surface_vk.h
validation.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91