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