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