Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
ahb_texture_source_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
11
12// vulkan.hpp generates some clang-tidy warnings.
13// NOLINTBEGIN(clang-analyzer-security.PointerSub)
14// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
15
16namespace impeller {
17
18namespace {
19
20using AHBProperties = AHBTextureSourceVK::AHBProperties;
21
22bool RequiresYCBCRConversion(vk::Format format) {
23 switch (format) {
24 case vk::Format::eG8B8R83Plane420Unorm:
25 case vk::Format::eG8B8R82Plane420Unorm:
26 case vk::Format::eG8B8R83Plane422Unorm:
27 case vk::Format::eG8B8R82Plane422Unorm:
28 case vk::Format::eG8B8R83Plane444Unorm:
29 return true;
30 default:
31 // NOTE: NOT EXHAUSTIVE.
32 break;
33 }
34 return false;
35}
36
37bool IsOpaque(AHardwareBuffer_Format format) {
38 return format == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
39}
40
41vk::UniqueDeviceMemory ImportVKDeviceMemoryFromAndroidHarwareBuffer(
42 const vk::Device& device,
43 const vk::PhysicalDevice& physical_device,
44 const vk::Image& image,
45 struct AHardwareBuffer* hardware_buffer,
46 const AHBProperties& ahb_props) {
47 vk::PhysicalDeviceMemoryProperties memory_properties;
48 physical_device.getMemoryProperties(&memory_properties);
49 int memory_type_index = AllocatorVK::FindMemoryTypeIndex(
50 ahb_props.get().memoryTypeBits, memory_properties);
51 if (memory_type_index < 0) {
52 VALIDATION_LOG << "Could not find memory type of external image.";
53 return {};
54 }
55
56 vk::StructureChain<vk::MemoryAllocateInfo,
57 // Core in 1.1
58 vk::MemoryDedicatedAllocateInfo,
59 // For VK_ANDROID_external_memory_android_hardware_buffer
60 vk::ImportAndroidHardwareBufferInfoANDROID>
61 memory_chain;
62
63 auto& mem_alloc_info = memory_chain.get<vk::MemoryAllocateInfo>();
64 mem_alloc_info.allocationSize = ahb_props.get().allocationSize;
65 mem_alloc_info.memoryTypeIndex = memory_type_index;
66
67 auto& dedicated_alloc_info =
68 memory_chain.get<vk::MemoryDedicatedAllocateInfo>();
69 dedicated_alloc_info.image = image;
70
71 auto& ahb_import_info =
72 memory_chain.get<vk::ImportAndroidHardwareBufferInfoANDROID>();
73 ahb_import_info.buffer = hardware_buffer;
74
75 auto device_memory = device.allocateMemoryUnique(memory_chain.get());
76 if (device_memory.result != vk::Result::eSuccess) {
77 VALIDATION_LOG << "Could not allocate device memory for external image : "
78 << vk::to_string(device_memory.result);
79 return {};
80 }
81
82 return std::move(device_memory.value);
83}
84
85std::shared_ptr<YUVConversionVK> CreateYUVConversion(
86 const ContextVK& context,
87 const AHBProperties& ahb_props) {
88 YUVConversionDescriptorVK conversion_chain;
89
90 const auto& ahb_format =
91 ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
92
93 // See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5806
94 // Both features are required.
95 const bool supports_linear_filtering =
96 !!(ahb_format.formatFeatures &
97 vk::FormatFeatureFlagBits::eSampledImageYcbcrConversionLinearFilter) &&
98 !!(ahb_format.formatFeatures &
99 vk::FormatFeatureFlagBits::eSampledImageFilterLinear);
100 auto& conversion_info = conversion_chain.get();
101
102 conversion_info.format = ahb_format.format;
103 conversion_info.ycbcrModel = ahb_format.suggestedYcbcrModel;
104 conversion_info.ycbcrRange = ahb_format.suggestedYcbcrRange;
105 conversion_info.components = ahb_format.samplerYcbcrConversionComponents;
106 conversion_info.xChromaOffset = ahb_format.suggestedXChromaOffset;
107 conversion_info.yChromaOffset = ahb_format.suggestedYChromaOffset;
108 conversion_info.chromaFilter =
109 supports_linear_filtering ? vk::Filter::eLinear : vk::Filter::eNearest;
110
111 conversion_info.forceExplicitReconstruction = false;
112
113 if (conversion_info.format == vk::Format::eUndefined) {
114 auto& external_format = conversion_chain.get<vk::ExternalFormatANDROID>();
115 external_format.externalFormat = ahb_format.externalFormat;
116 } else {
117 conversion_chain.unlink<vk::ExternalFormatANDROID>();
118 }
119
120 return context.GetYUVConversionLibrary()->GetConversion(conversion_chain);
121}
122
123PixelFormat ToPixelFormat(AHardwareBuffer_Format format) {
124 switch (format) {
125 case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
127 case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
129 case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
131 case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
133 case AHARDWAREBUFFER_FORMAT_S8_UINT:
135 case AHARDWAREBUFFER_FORMAT_R8_UNORM:
137 case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
138 case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
139 case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
140 case AHARDWAREBUFFER_FORMAT_R16_UINT:
141 case AHARDWAREBUFFER_FORMAT_D24_UNORM:
142 case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
143 case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
144 case AHARDWAREBUFFER_FORMAT_BLOB:
145 case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
146 case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
147 case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
148 case AHARDWAREBUFFER_FORMAT_D16_UNORM:
149 case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
150 case AHARDWAREBUFFER_FORMAT_YCbCr_P210:
151 // Not understood by the rest of Impeller. Use a placeholder but create
152 // the native image and image views using the right external format.
153 break;
154 }
156}
157
158TextureType ToTextureType(const AHardwareBuffer_Desc& ahb_desc) {
159 if (ahb_desc.layers == 1u) {
161 }
162 if (ahb_desc.layers % 6u == 0 &&
163 (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP)) {
165 }
166 // Our texture types seem to understand external OES textures. Should these be
167 // wired up instead?
169}
170
171TextureDescriptor ToTextureDescriptor(const AHardwareBuffer_Desc& ahb_desc) {
172 const auto ahb_size = ISize{ahb_desc.width, ahb_desc.height};
173 TextureDescriptor desc;
174 // We are not going to touch hardware buffers on the CPU or use them as
175 // transient attachments. Just treat them as device private.
176 desc.storage_mode = StorageMode::kDevicePrivate;
177 desc.format =
178 ToPixelFormat(static_cast<AHardwareBuffer_Format>(ahb_desc.format));
179 desc.size = ahb_size;
180 desc.type = ToTextureType(ahb_desc);
181 desc.sample_count = SampleCount::kCount1;
182 desc.compression_type = CompressionType::kLossless;
183 desc.mip_count = (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
184 ? ahb_size.MipCount()
185 : 1u;
186 if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
187 desc.usage = TextureUsage::kRenderTarget;
188 }
189 return desc;
190}
191} // namespace
192
194 const std::shared_ptr<Context>& p_context,
195 struct AHardwareBuffer* ahb,
196 const AHardwareBuffer_Desc& ahb_desc)
197 : TextureSourceVK(ToTextureDescriptor(ahb_desc)) {
198 if (!p_context) {
199 return;
200 }
201
202 const auto& context = ContextVK::Cast(*p_context);
203 const auto& device = context.GetDevice();
204 const auto& physical_device = context.GetPhysicalDevice();
205
206 AHBProperties ahb_props;
207
208 if (device.getAndroidHardwareBufferPropertiesANDROID(ahb, &ahb_props.get()) !=
209 vk::Result::eSuccess) {
210 VALIDATION_LOG << "Could not determine properties of the Android hardware "
211 "buffer.";
212 return;
213 }
214
215 const auto& ahb_format =
216 ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
217
218 // Create an image to refer to our external image.
219 auto image =
221 if (!image) {
222 return;
223 }
224
225 // Create a device memory allocation to refer to our external image.
226 auto device_memory = ImportVKDeviceMemoryFromAndroidHarwareBuffer(
227 device, physical_device, image.get(), ahb, ahb_props);
228 if (!device_memory) {
229 return;
230 }
231
232 // Bind the image to the image memory.
233 if (auto result = device.bindImageMemory(image.get(), device_memory.get(), 0);
234 result != vk::Result::eSuccess) {
235 VALIDATION_LOG << "Could not bind external device memory to image : "
236 << vk::to_string(result);
237 return;
238 }
239
240 // Figure out how to perform YUV conversions.
241 needs_yuv_conversion_ = ahb_format.format == vk::Format::eUndefined ||
242 RequiresYCBCRConversion(ahb_format.format);
243 std::shared_ptr<YUVConversionVK> yuv_conversion;
244 if (needs_yuv_conversion_) {
245 yuv_conversion = CreateYUVConversion(context, ahb_props);
246 if (!yuv_conversion || !yuv_conversion->IsValid()) {
247 return;
248 }
249 }
250
251 // Create image view for the newly created image.
252 auto image_info = CreateImageViewInfo(image.get(), //
253 yuv_conversion, //
254 ahb_props, //
255 ahb_desc //
256 );
257 auto image_view = device.createImageViewUnique(image_info.get());
258 if (image_view.result != vk::Result::eSuccess) {
259 VALIDATION_LOG << "Could not create external image view: "
260 << vk::to_string(image_view.result);
261 return;
262 }
263
264 device_holder_ = context.GetDeviceHolder();
265 device_memory_ = std::move(device_memory);
266 image_ = std::move(image);
267 yuv_conversion_ = std::move(yuv_conversion);
268 image_view_ = std::move(image_view.value);
269
270#ifdef IMPELLER_DEBUG
271 context.SetDebugName(device_memory_.get(), "AHB Device Memory");
272 context.SetDebugName(image_.get(), "AHB Image");
273 if (yuv_conversion_) {
274 context.SetDebugName(yuv_conversion_->GetConversion(),
275 "AHB YUV Conversion");
276 }
277 context.SetDebugName(image_view_.get(), "AHB ImageView");
278#endif // IMPELLER_DEBUG
279
280 is_valid_ = true;
281}
282
284 const std::shared_ptr<Context>& context,
285 std::unique_ptr<android::HardwareBuffer> backing_store,
286 bool is_swapchain_image)
288 backing_store->GetHandle(),
289 backing_store->GetAndroidDescriptor()) {
290 backing_store_ = std::move(backing_store);
291 is_swapchain_image_ = is_swapchain_image;
292}
293
294// |TextureSourceVK|
296 // The ASurfaceTransaction completion callback may hold the last reference
297 // to the AHBTextureSourceVK and can be deleted after the ContextVK and
298 // VkDevice have been destroyed. If the VkDevice is gone, then do not call
299 // APIs to delete Vulkan objects.
300 if (auto device = device_holder_.lock(); !device) {
301 image_view_.release();
302 image_.release();
303 device_memory_.release();
304 }
305}
306
308 return is_valid_;
309}
310
311// |TextureSourceVK|
313 return image_.get();
314}
315
316// |TextureSourceVK|
317vk::ImageView AHBTextureSourceVK::GetImageView() const {
318 return image_view_.get();
319}
320
321// |TextureSourceVK|
323 uint32_t mip_level,
324 uint32_t array_layer) const {
325 // Hardware buffer textures are always a single 2D mip and layer.
326 return image_view_.get();
327}
328
329// |TextureSourceVK|
331 return is_swapchain_image_;
332}
333
334// |TextureSourceVK|
335std::shared_ptr<YUVConversionVK> AHBTextureSourceVK::GetYUVConversion() const {
336 return needs_yuv_conversion_ ? yuv_conversion_ : nullptr;
337}
338
340 return backing_store_.get();
341}
342
344 const vk::Device& device,
345 const AHBProperties& ahb_props,
346 const AHardwareBuffer_Desc& ahb_desc) {
347 const auto& ahb_format =
348 ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
349
350 vk::StructureChain<vk::ImageCreateInfo,
351 // For VK_KHR_external_memory
352 vk::ExternalMemoryImageCreateInfo,
353 // For VK_ANDROID_external_memory_android_hardware_buffer
354 vk::ExternalFormatANDROID>
355 image_chain;
356
357 auto& image_info = image_chain.get<vk::ImageCreateInfo>();
358
359 vk::ImageUsageFlags image_usage_flags;
360 if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
361 image_usage_flags |= vk::ImageUsageFlagBits::eSampled;
362 }
363 if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
364 image_usage_flags |= vk::ImageUsageFlagBits::eColorAttachment;
365 image_usage_flags |= vk::ImageUsageFlagBits::eInputAttachment;
366 }
367
368 vk::ImageCreateFlags image_create_flags;
369 if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
370 image_create_flags |= vk::ImageCreateFlagBits::eProtected;
371 }
372 if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
373 image_create_flags |= vk::ImageCreateFlagBits::eCubeCompatible;
374 }
375
376 image_info.imageType = vk::ImageType::e2D;
377 image_info.format = ahb_format.format;
378 image_info.extent.width = ahb_desc.width;
379 image_info.extent.height = ahb_desc.height;
380 image_info.extent.depth = 1;
381 image_info.mipLevels =
382 (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
383 ? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
384 : 1u;
385 image_info.arrayLayers = ahb_desc.layers;
386 image_info.samples = vk::SampleCountFlagBits::e1;
387 image_info.tiling = vk::ImageTiling::eOptimal;
388 image_info.usage = image_usage_flags;
389 image_info.flags = image_create_flags;
390 image_info.sharingMode = vk::SharingMode::eExclusive;
391 image_info.initialLayout = vk::ImageLayout::eUndefined;
392
393 image_chain.get<vk::ExternalMemoryImageCreateInfo>().handleTypes =
394 vk::ExternalMemoryHandleTypeFlagBits::eAndroidHardwareBufferANDROID;
395
396 // If the format isn't natively supported by Vulkan (i.e, be a part of the
397 // base vkFormat enum), an untyped "external format" must be specified when
398 // creating the image and the image views. Usually includes YUV formats.
399 if (ahb_format.format == vk::Format::eUndefined) {
400 image_chain.get<vk::ExternalFormatANDROID>().externalFormat =
401 ahb_format.externalFormat;
402 } else {
403 image_chain.unlink<vk::ExternalFormatANDROID>();
404 }
405
406 auto image = device.createImageUnique(image_chain.get());
407 if (image.result != vk::Result::eSuccess) {
408 VALIDATION_LOG << "Could not create image for external buffer: "
409 << vk::to_string(image.result);
410 return {};
411 }
412
413 return std::move(image.value);
414}
415
417 const vk::Image& image,
418 const std::shared_ptr<YUVConversionVK>& yuv_conversion_wrapper,
419 const AHBProperties& ahb_props,
420 const AHardwareBuffer_Desc& ahb_desc) {
421 const auto& ahb_format =
422 ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
423
424 ImageViewInfo view_chain;
425
426 auto& view_info = view_chain.get();
427
428 view_info.image = image;
429 view_info.viewType = vk::ImageViewType::e2D;
430 view_info.format = ahb_format.format;
431 if (IsOpaque(static_cast<AHardwareBuffer_Format>(ahb_desc.format))) {
432 view_info.components.a = vk::ComponentSwizzle::eOne;
433 }
434 view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
435 view_info.subresourceRange.baseMipLevel = 0u;
436 view_info.subresourceRange.baseArrayLayer = 0u;
437 view_info.subresourceRange.levelCount =
438 (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
439 ? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
440 : 1u;
441 view_info.subresourceRange.layerCount = ahb_desc.layers;
442
443 // We need a custom YUV conversion only if we don't recognize the format.
444 if (view_info.format == vk::Format::eUndefined ||
445 RequiresYCBCRConversion(view_info.format)) {
446 view_chain.get<vk::SamplerYcbcrConversionInfo>().conversion =
447 yuv_conversion_wrapper->GetConversion();
448 } else {
449 view_chain.unlink<vk::SamplerYcbcrConversionInfo>();
450 }
451
452 return view_chain;
453}
454
455} // namespace impeller
456
457// NOLINTEND(clang-analyzer-core.StackAddressEscape)
458// NOLINTEND(clang-analyzer-security.PointerSub)
A texture source that wraps an instance of AHardwareBuffer.
vk::StructureChain< vk::ImageViewCreateInfo, vk::SamplerYcbcrConversionInfo > ImageViewInfo
vk::ImageView GetRenderTargetView(uint32_t mip_level, uint32_t array_layer) const override
Retrieve the image view used to attach a specific subresource of this texture as a render target.
bool IsSwapchainImage() const override
Determines if swapchain image. That is, an image used as the root render target.
vk::Image GetImage() const override
Get the image handle for this texture source.
static vk::UniqueImage CreateVKImageWrapperForAndroidHarwareBuffer(const vk::Device &device, const AHBProperties &ahb_props, const AHardwareBuffer_Desc &ahb_desc)
Create a VkImage that wraps an Android hardware buffer.
const android::HardwareBuffer * GetBackingStore() const
std::shared_ptr< YUVConversionVK > GetYUVConversion() const override
When sampling from textures whose formats are not known to Vulkan, a custom conversion is necessary t...
vk::StructureChain< vk::AndroidHardwareBufferPropertiesANDROID, vk::AndroidHardwareBufferFormatPropertiesANDROID > AHBProperties
AHBTextureSourceVK(const std::shared_ptr< Context > &context, struct AHardwareBuffer *hardware_buffer, const AHardwareBuffer_Desc &hardware_buffer_desc)
vk::ImageView GetImageView() const override
Retrieve the image view used for sampling/blitting/compute with this texture source.
static ImageViewInfo CreateImageViewInfo(const vk::Image &image, const std::shared_ptr< YUVConversionVK > &yuv_conversion_wrapper, const AHBProperties &ahb_props, const AHardwareBuffer_Desc &ahb_desc)
static int32_t FindMemoryTypeIndex(uint32_t memory_type_bits_requirement, vk::PhysicalDeviceMemoryProperties &memory_properties)
Select a matching memory type for the given [memory_type_bits_requirement], or -1 if none is found.
static ContextVK & Cast(Context &base)
Abstract base class that represents a vkImage and an vkImageView.
A wrapper for AHardwareBuffer https://developer.android.com/ndk/reference/group/a-hardware-buffer.
FlutterVulkanImage * image
VkPhysicalDevice physical_device
Definition main.cc:67
VkDevice device
Definition main.cc:69
uint32_t uint32_t * format
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition formats_vk.h:219
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
constexpr GLenum ToTextureType(TextureType type)
ISize64 ISize
Definition size.h:162
vk::StructureChain< vk::SamplerYcbcrConversionCreateInfo > YUVConversionDescriptorVK
std::shared_ptr< ContextGLES > context
uint32_t format
The VkFormat of the image (for example: VK_FORMAT_R8G8B8A8_UNORM).
Definition embedder.h:940
Type width
Definition size.h:28
#define VALIDATION_LOG
Definition validation.h:91