Flutter Impeller
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 
16 namespace impeller {
17 
18 namespace {
19 
20 using AHBProperties = AHBTextureSourceVK::AHBProperties;
21 
22 bool 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 
37 bool IsOpaque(AHardwareBuffer_Format format) {
38  return format == AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
39 }
40 
41 vk::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 
85 std::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 
123 PixelFormat 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:
134  return PixelFormat::kS8UInt;
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 
158 TextureType 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 
171 TextureDescriptor 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 =
220  CreateVKImageWrapperForAndroidHarwareBuffer(device, ahb_props, ahb_desc);
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_memory_ = std::move(device_memory);
265  image_ = std::move(image);
266  yuv_conversion_ = std::move(yuv_conversion);
267  image_view_ = std::move(image_view.value);
268 
269 #ifdef IMPELLER_DEBUG
270  context.SetDebugName(device_memory_.get(), "AHB Device Memory");
271  context.SetDebugName(image_.get(), "AHB Image");
272  if (yuv_conversion_) {
273  context.SetDebugName(yuv_conversion_->GetConversion(),
274  "AHB YUV Conversion");
275  }
276  context.SetDebugName(image_view_.get(), "AHB ImageView");
277 #endif // IMPELLER_DEBUG
278 
279  is_valid_ = true;
280 }
281 
283  const std::shared_ptr<Context>& context,
284  std::unique_ptr<android::HardwareBuffer> backing_store,
285  bool is_swapchain_image)
286  : AHBTextureSourceVK(context,
287  backing_store->GetHandle(),
288  backing_store->GetAndroidDescriptor()) {
289  backing_store_ = std::move(backing_store);
290  is_swapchain_image_ = is_swapchain_image;
291 }
292 
293 // |TextureSourceVK|
295 
297  return is_valid_;
298 }
299 
300 // |TextureSourceVK|
301 vk::Image AHBTextureSourceVK::GetImage() const {
302  return image_.get();
303 }
304 
305 // |TextureSourceVK|
306 vk::ImageView AHBTextureSourceVK::GetImageView() const {
307  return image_view_.get();
308 }
309 
310 // |TextureSourceVK|
312  return image_view_.get();
313 }
314 
315 // |TextureSourceVK|
317  return is_swapchain_image_;
318 }
319 
320 // |TextureSourceVK|
321 std::shared_ptr<YUVConversionVK> AHBTextureSourceVK::GetYUVConversion() const {
322  return needs_yuv_conversion_ ? yuv_conversion_ : nullptr;
323 }
324 
326  return backing_store_.get();
327 }
328 
330  const vk::Device& device,
331  const AHBProperties& ahb_props,
332  const AHardwareBuffer_Desc& ahb_desc) {
333  const auto& ahb_format =
334  ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
335 
336  vk::StructureChain<vk::ImageCreateInfo,
337  // For VK_KHR_external_memory
338  vk::ExternalMemoryImageCreateInfo,
339  // For VK_ANDROID_external_memory_android_hardware_buffer
340  vk::ExternalFormatANDROID>
341  image_chain;
342 
343  auto& image_info = image_chain.get<vk::ImageCreateInfo>();
344 
345  vk::ImageUsageFlags image_usage_flags;
346  if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
347  image_usage_flags |= vk::ImageUsageFlagBits::eSampled;
348  }
349  if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
350  image_usage_flags |= vk::ImageUsageFlagBits::eColorAttachment;
351  image_usage_flags |= vk::ImageUsageFlagBits::eInputAttachment;
352  }
353 
354  vk::ImageCreateFlags image_create_flags;
355  if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
356  image_create_flags |= vk::ImageCreateFlagBits::eProtected;
357  }
358  if (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP) {
359  image_create_flags |= vk::ImageCreateFlagBits::eCubeCompatible;
360  }
361 
362  image_info.imageType = vk::ImageType::e2D;
363  image_info.format = ahb_format.format;
364  image_info.extent.width = ahb_desc.width;
365  image_info.extent.height = ahb_desc.height;
366  image_info.extent.depth = 1;
367  image_info.mipLevels =
368  (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
369  ? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
370  : 1u;
371  image_info.arrayLayers = ahb_desc.layers;
372  image_info.samples = vk::SampleCountFlagBits::e1;
373  image_info.tiling = vk::ImageTiling::eOptimal;
374  image_info.usage = image_usage_flags;
375  image_info.flags = image_create_flags;
376  image_info.sharingMode = vk::SharingMode::eExclusive;
377  image_info.initialLayout = vk::ImageLayout::eUndefined;
378 
379  image_chain.get<vk::ExternalMemoryImageCreateInfo>().handleTypes =
380  vk::ExternalMemoryHandleTypeFlagBits::eAndroidHardwareBufferANDROID;
381 
382  // If the format isn't natively supported by Vulkan (i.e, be a part of the
383  // base vkFormat enum), an untyped "external format" must be specified when
384  // creating the image and the image views. Usually includes YUV formats.
385  if (ahb_format.format == vk::Format::eUndefined) {
386  image_chain.get<vk::ExternalFormatANDROID>().externalFormat =
387  ahb_format.externalFormat;
388  } else {
389  image_chain.unlink<vk::ExternalFormatANDROID>();
390  }
391 
392  auto image = device.createImageUnique(image_chain.get());
393  if (image.result != vk::Result::eSuccess) {
394  VALIDATION_LOG << "Could not create image for external buffer: "
395  << vk::to_string(image.result);
396  return {};
397  }
398 
399  return std::move(image.value);
400 }
401 
403  const vk::Image& image,
404  const std::shared_ptr<YUVConversionVK>& yuv_conversion_wrapper,
405  const AHBProperties& ahb_props,
406  const AHardwareBuffer_Desc& ahb_desc) {
407  const auto& ahb_format =
408  ahb_props.get<vk::AndroidHardwareBufferFormatPropertiesANDROID>();
409 
410  ImageViewInfo view_chain;
411 
412  auto& view_info = view_chain.get();
413 
414  view_info.image = image;
415  view_info.viewType = vk::ImageViewType::e2D;
416  view_info.format = ahb_format.format;
417  if (IsOpaque(static_cast<AHardwareBuffer_Format>(ahb_desc.format))) {
418  view_info.components.a = vk::ComponentSwizzle::eOne;
419  }
420  view_info.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
421  view_info.subresourceRange.baseMipLevel = 0u;
422  view_info.subresourceRange.baseArrayLayer = 0u;
423  view_info.subresourceRange.levelCount =
424  (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE)
425  ? ISize{ahb_desc.width, ahb_desc.height}.MipCount()
426  : 1u;
427  view_info.subresourceRange.layerCount = ahb_desc.layers;
428 
429  // We need a custom YUV conversion only if we don't recognize the format.
430  if (view_info.format == vk::Format::eUndefined ||
431  RequiresYCBCRConversion(view_info.format)) {
432  view_chain.get<vk::SamplerYcbcrConversionInfo>().conversion =
433  yuv_conversion_wrapper->GetConversion();
434  } else {
435  view_chain.unlink<vk::SamplerYcbcrConversionInfo>();
436  }
437 
438  return view_chain;
439 }
440 
441 } // namespace impeller
442 
443 // NOLINTEND(clang-analyzer-core.StackAddressEscape)
444 // NOLINTEND(clang-analyzer-security.PointerSub)
A texture source that wraps an instance of AHardwareBuffer.
vk::StructureChain< vk::ImageViewCreateInfo, vk::SamplerYcbcrConversionInfo > ImageViewInfo
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.
vk::ImageView GetRenderTargetView() const override
Retrieve the image view used for render target attachments 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)
Definition: backend_cast.h:13
Abstract base class that represents a vkImage and an vkImageView.
A wrapper for AHardwareBuffer https://developer.android.com/ndk/reference/group/a-hardware-buffer.
Vector3 e1
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition: formats_vk.h:185
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)
Definition: formats_gles.h:171
TextureType
Definition: formats.h:265
ISize64 ISize
Definition: size.h:162
vk::StructureChain< vk::SamplerYcbcrConversionCreateInfo > YUVConversionDescriptorVK
Type width
Definition: size.h:28
#define VALIDATION_LOG
Definition: validation.h:91