| 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
5 #include "impeller/renderer/backend/vulkan/android/ahb_texture_source_vk.h"
6
7 #include "impeller/renderer/backend/vulkan/allocator_vk.h"
8 #include "impeller/renderer/backend/vulkan/context_vk.h"
9 #include "impeller/renderer/backend/vulkan/texture_source_vk.h"
10 #include "impeller/renderer/backend/vulkan/yuv_conversion_library_vk.h"
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:
242 return PixelFormat::kR8G8B8A8UNormInt;
243 case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
244 return PixelFormat::kR16G16B16A16Float;
245 case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
246 return PixelFormat::kD24UnormS8Uint;
247 case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
248 return PixelFormat::kD32FloatS8UInt;
249 case AHARDWAREBUFFER_FORMAT_S8_UINT:
250 return PixelFormat::kS8UInt;
251 case AHARDWAREBUFFER_FORMAT_R8_UNORM:
252 return PixelFormat::kR8UNormInt;
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 }
270 return PixelFormat::kR8G8B8A8UNormInt;
271 }
272
273 TextureType ToTextureType(const AHardwareBuffer_Desc& ahb_desc) {
274 if (ahb_desc.layers == 1u) {
275 return TextureType::kTexture2D;
276 }
277 if (ahb_desc.layers % 6u == 0 &&
278 (ahb_desc.usage & AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP)) {
279 return TextureType::kTextureCube;
280 }
281 // Our texture types seem to understand external OES textures. Should these be
282 // wired up instead?
283 return TextureType::kTexture2D;
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
308 AHBTextureSourceVK::AHBTextureSourceVK(
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
395 AHBTextureSourceVK::AHBTextureSourceVK(
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|
407 AHBTextureSourceVK::~AHBTextureSourceVK() = default;
408
409 bool AHBTextureSourceVK::IsValid() const {
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|
424 vk::ImageView AHBTextureSourceVK::GetRenderTargetView() const {
425 return image_view_.get();
426 }
427
428 // |TextureSourceVK|
429 bool AHBTextureSourceVK::IsSwapchainImage() const {
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
438 const android::HardwareBuffer* AHBTextureSourceVK::GetBackingStore() const {
439 return backing_store_.get();
440 }
441
442 } // namespace impeller
ahb_texture_source_vk.h
allocator_vk.h
external_format
GLenum external_format
Definition: blit_command_gles.cc:150
impeller::AHBTextureSourceVK
A texture source that wraps an instance of AHardwareBuffer.
Definition: ahb_texture_source_vk.h:32
impeller::AHBTextureSourceVK::IsSwapchainImage
bool IsSwapchainImage() const override
Determines if swapchain image. That is, an image used as the root render target.
Definition: ahb_texture_source_vk.cc:429
impeller::AHBTextureSourceVK::GetImage
vk::Image GetImage() const override
Get the image handle for this texture source.
Definition: ahb_texture_source_vk.cc:414
impeller::AHBTextureSourceVK::IsValid
bool IsValid() const
Definition: ahb_texture_source_vk.cc:409
impeller::AHBTextureSourceVK::GetBackingStore
const android::HardwareBuffer * GetBackingStore() const
Definition: ahb_texture_source_vk.cc:438
impeller::AHBTextureSourceVK::GetYUVConversion
std::shared_ptr< YUVConversionVK > GetYUVConversion() const override
When sampling from textures whose formats are not known to Vulkan, a custom conversion is necessary t...
Definition: ahb_texture_source_vk.cc:434
impeller::AHBTextureSourceVK::AHBTextureSourceVK
AHBTextureSourceVK(const std::shared_ptr< Context > &context, struct AHardwareBuffer *hardware_buffer, const AHardwareBuffer_Desc &hardware_buffer_desc)
Definition: ahb_texture_source_vk.cc:308
impeller::AHBTextureSourceVK::~AHBTextureSourceVK
~AHBTextureSourceVK() override
impeller::AHBTextureSourceVK::GetImageView
vk::ImageView GetImageView() const override
Retrieve the image view used for sampling/blitting/compute with this texture source.
Definition: ahb_texture_source_vk.cc:419
impeller::AHBTextureSourceVK::GetRenderTargetView
vk::ImageView GetRenderTargetView() const override
Retrieve the image view used for render target attachments with this texture source.
Definition: ahb_texture_source_vk.cc:424
impeller::AllocatorVK::FindMemoryTypeIndex
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.
Definition: allocator_vk.cc:180
impeller::BackendCast< ContextVK, Context >::Cast
static ContextVK & Cast(Context &base)
Definition: backend_cast.h:13
impeller::TextureSourceVK
Abstract base class that represents a vkImage and an vkImageView.
Definition: texture_source_vk.h:26
impeller::android::HardwareBuffer
A wrapper for AHardwareBuffer https://developer.android.com/ndk/reference/group/a-hardware-buffer.
Definition: hardware_buffer.h:95
e1
Vector3 e1
Definition: line_contents.cc:107
impeller
Definition: allocation.cc:12
impeller::ToPixelFormat
constexpr PixelFormat ToPixelFormat(vk::Format format)
Definition: formats_vk.h:183
impeller::StorageMode::kDevicePrivate
@ kDevicePrivate
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
impeller::PixelFormat::kD24UnormS8Uint
@ kD24UnormS8Uint
impeller::PixelFormat::kR8UNormInt
@ kR8UNormInt
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::PixelFormat::kR16G16B16A16Float
@ kR16G16B16A16Float
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::ToTextureType
constexpr GLenum ToTextureType(TextureType type)
Definition: formats_gles.h:171
impeller::TextureType
TextureType
Definition: formats.h:262
impeller::TextureType::kTexture2D
@ kTexture2D
impeller::TextureType::kTextureCube
@ kTextureCube
impeller::CompressionType::kLossless
@ kLossless
impeller::ISize
ISize64 ISize
Definition: size.h:162
impeller::YUVConversionDescriptorVK
vk::StructureChain< vk::SamplerYcbcrConversionCreateInfo > YUVConversionDescriptorVK
Definition: yuv_conversion_vk.h:30
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::SampleCount::kCount1
@ kCount1
context_vk.h
impeller::TSize::width
Type width
Definition: size.h:28
texture_source_vk.h
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:91
yuv_conversion_library_vk.h