• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/android/GrAHardwareBufferUtils.h"
9 
10 #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
11 
12 #include "include/gpu/ganesh/GrBackendSurface.h"
13 #include "include/gpu/ganesh/GrDirectContext.h"
14 #include "include/gpu/ganesh/GrTypes.h"
15 #include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
16 #include "include/gpu/ganesh/vk/GrVkTypes.h"
17 #include "include/gpu/vk/VulkanTypes.h"
18 #include "include/private/gpu/vk/SkiaVulkan.h"
19 #include "src/gpu/ganesh/GrDirectContextPriv.h"
20 #include "src/gpu/ganesh/vk/GrVkCaps.h"
21 #include "src/gpu/ganesh/vk/GrVkGpu.h"
22 #include "src/gpu/vk/VulkanInterface.h"
23 #include "src/gpu/vk/VulkanUtilsPriv.h"
24 
25 #include <android/hardware_buffer.h>
26 
27 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
28 #include <vndk/hardware_buffer.h>
29 #endif
30 
31 #define VK_CALL(X) gpu->vkInterface()->fFunctions.f##X
32 
33 namespace GrAHardwareBufferUtils {
34 
GetVulkanBackendFormat(GrDirectContext * dContext,AHardwareBuffer * hardwareBuffer,uint32_t bufferFormat,bool requireKnownFormat)35 GrBackendFormat GetVulkanBackendFormat(GrDirectContext* dContext, AHardwareBuffer* hardwareBuffer,
36                                        uint32_t bufferFormat, bool requireKnownFormat) {
37     GrBackendApi backend = dContext->backend();
38     if (backend != GrBackendApi::kVulkan) {
39         return GrBackendFormat();
40     }
41 
42     VkFormat bufferVkFormat = VK_FORMAT_UNDEFINED;
43     switch (bufferFormat) {
44         case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM: {
45             bufferVkFormat = VK_FORMAT_R8G8B8A8_UNORM;
46             break;
47         }
48 #if __ANDROID_API__ >= 34
49         case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM: {
50             bufferVkFormat = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16;
51             break;
52         }
53 #endif
54         case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: {
55             bufferVkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
56             break;
57         }
58         case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM: {
59             bufferVkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
60             break;
61         }
62         case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: {
63             bufferVkFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
64             break;
65         }
66         case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM: {
67             bufferVkFormat = VK_FORMAT_R8G8B8A8_UNORM;
68             break;
69         }
70         case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM: {
71             bufferVkFormat = VK_FORMAT_R8G8B8_UNORM;
72             break;
73         }
74 #if __ANDROID_API__ >= 33
75         case AHARDWAREBUFFER_FORMAT_R8_UNORM: {
76             bufferVkFormat = VK_FORMAT_R8_UNORM;
77             break;
78         }
79 #endif
80 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
81         case AHARDWAREBUFFER_FORMAT_B8G8R8A8_UNORM: {
82             bufferVkFormat = VK_FORMAT_B8G8R8A8_UNORM;
83             break;
84         }
85 #endif
86         default: {
87             if (requireKnownFormat) {
88                 return GrBackendFormat();
89             }
90             break;
91         }
92     }
93 
94     GrVkGpu* gpu = static_cast<GrVkGpu*>(dContext->priv().getGpu());
95     SkASSERT(gpu);
96 
97     if (bufferVkFormat != VK_FORMAT_UNDEFINED) {
98         // Check to make sure the associated VkFormat has the necessary format features. If not,
99         // default to using an external format (set the VkFormat as undefined).
100         // TODO: When creating a GrBackendFormat with a VkFormat that is not VK_FORMAT_UNDEFINED, we
101         // currently assume that the VkFormat's VkFormatFeatureFlags contain
102         // VK_FORMAT_FEATURE_TRANSFER_SRC_BIT and VK_FORMAT_FEATURE_TRANSFER_DST_BIT.
103         if (gpu->vkCaps().isVkFormatTexturable(bufferVkFormat)) {
104             return GrBackendFormats::MakeVk(bufferVkFormat);
105         }
106         bufferVkFormat = VK_FORMAT_UNDEFINED;
107     }
108     // If there is no associated VkFormat (or it does not support the necessary features) and
109     // requireKnownFormat = false, then import using an external format.
110     VkDevice device = gpu->device();
111 
112     if (!gpu->vkCaps().supportsAndroidHWBExternalMemory()) {
113         return GrBackendFormat();
114     }
115 
116     VkAndroidHardwareBufferFormatPropertiesANDROID hwbFormatProps;
117     VkAndroidHardwareBufferPropertiesANDROID hwbProps;
118     if (!GetAHardwareBufferProperties(
119                 &hwbFormatProps, &hwbProps, gpu->vkInterface(), hardwareBuffer, device)) {
120         return GrBackendFormat();
121     }
122 
123     skgpu::VulkanYcbcrConversionInfo ycbcrConversion;
124     GetYcbcrConversionInfoFromFormatProps(&ycbcrConversion, hwbFormatProps);
125 
126     return GrBackendFormats::MakeVk(ycbcrConversion);
127 }
128 
129 class VulkanCleanupHelper {
130 public:
VulkanCleanupHelper(GrVkGpu * gpu,VkImage image,VkDeviceMemory memory)131     VulkanCleanupHelper(GrVkGpu* gpu, VkImage image, VkDeviceMemory memory)
132         : fDevice(gpu->device())
133         , fImage(image)
134         , fMemory(memory)
135         , fDestroyImage(gpu->vkInterface()->fFunctions.fDestroyImage)
136         , fFreeMemory(gpu->vkInterface()->fFunctions.fFreeMemory) {}
~VulkanCleanupHelper()137     ~VulkanCleanupHelper() {
138         fDestroyImage(fDevice, fImage, nullptr);
139         fFreeMemory(fDevice, fMemory, nullptr);
140     }
141 private:
142     VkDevice           fDevice;
143     VkImage            fImage;
144     VkDeviceMemory     fMemory;
145     PFN_vkDestroyImage fDestroyImage;
146     PFN_vkFreeMemory   fFreeMemory;
147 };
148 
delete_vk_image(void * context)149 void delete_vk_image(void* context) {
150     VulkanCleanupHelper* cleanupHelper = static_cast<VulkanCleanupHelper*>(context);
151     delete cleanupHelper;
152 }
153 
update_vk_image(void * context,GrDirectContext * dContext)154 void update_vk_image(void* context, GrDirectContext* dContext) {
155     // no op
156 }
157 
make_vk_backend_texture(GrDirectContext * dContext,AHardwareBuffer * hardwareBuffer,int width,int height,DeleteImageProc * deleteProc,UpdateImageProc * updateProc,TexImageCtx * imageCtx,bool isProtectedContent,const GrBackendFormat & grBackendFormat,bool isRenderable,bool fromAndroidWindow)158 static GrBackendTexture make_vk_backend_texture(
159         GrDirectContext* dContext, AHardwareBuffer* hardwareBuffer,
160         int width, int height,
161         DeleteImageProc* deleteProc,
162         UpdateImageProc* updateProc,
163         TexImageCtx* imageCtx,
164         bool isProtectedContent,
165         const GrBackendFormat& grBackendFormat,
166         bool isRenderable,
167         bool fromAndroidWindow) {
168     SkASSERT(dContext->backend() == GrBackendApi::kVulkan);
169 
170     GrVkGpu* gpu = static_cast<GrVkGpu*>(dContext->priv().getGpu());
171     SkASSERT(gpu);
172     SkASSERT(!isProtectedContent || gpu->protectedContext());
173 
174     VkPhysicalDevice physicalDevice = gpu->physicalDevice();
175     VkDevice device = gpu->device();
176 
177     if (!gpu->vkCaps().supportsAndroidHWBExternalMemory()) {
178         return GrBackendTexture();
179     }
180 
181     VkFormat grBackendVkFormat;
182     if (!GrBackendFormats::AsVkFormat(grBackendFormat, &grBackendVkFormat)) {
183         SkDebugf("AsVkFormat failed (valid: %d, backend: %u)",
184                  grBackendFormat.isValid(),
185                  (unsigned)grBackendFormat.backend());
186         return GrBackendTexture();
187     }
188     bool importAsExternalFormat = grBackendVkFormat == VK_FORMAT_UNDEFINED;
189 
190     VkAndroidHardwareBufferFormatPropertiesANDROID hwbFormatProps;
191     VkAndroidHardwareBufferPropertiesANDROID hwbProps;
192     if (!skgpu::GetAHardwareBufferProperties(
193                 &hwbFormatProps, &hwbProps, gpu->vkInterface(), hardwareBuffer, device)) {
194         return GrBackendTexture();
195     }
196     VkFormat hwbVkFormat = hwbFormatProps.format;
197 
198     // We normally expect the hardware buffer format (hwbVkFormat) to be equivalent to ganesh's
199     // GrBackendFormat VkFormat (grBackendVkFormat). However, even if the hwbVkFormat is a defined
200     // format, we may choose to ignore that and instead import the AHardwareBuffer using an
201     // external format. For example, we would attempt to do this if the VkFormat doesn't support the
202     // necessary features. Thus, it is acceptable for hwbVkFormat to differ from grBackendVkFormat
203     // iff we are importing the AHardwareBuffer using an external format.
204     if (!importAsExternalFormat && hwbVkFormat != grBackendVkFormat) {
205         SkDebugf("Queried format not consistent with expected format; got: %d, expected: %d",
206                  hwbVkFormat,
207                  grBackendVkFormat);
208         return GrBackendTexture();
209     }
210 
211     VkExternalFormatANDROID externalFormat;
212     externalFormat.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID;
213     externalFormat.pNext = nullptr;
214     externalFormat.externalFormat = 0;  // If this is zero it is as if we aren't using this struct.
215 
216     const skgpu::VulkanYcbcrConversionInfo* ycbcrConversion =
217             GrBackendFormats::GetVkYcbcrConversionInfo(grBackendFormat);
218     if (!ycbcrConversion) {
219         return GrBackendTexture();
220     }
221 
222     // TODO: Check the supported tilings vkGetPhysicalDeviceImageFormatProperties2 to see if we have
223     // to use linear. Add better linear support throughout Ganesh.
224     VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
225 
226     if (isRenderable && (importAsExternalFormat || // cannot render to external formats
227                          !gpu->vkCaps().isFormatRenderable(grBackendVkFormat, tiling))) {
228         SkDebugf("Renderable texture requested from an AHardwareBuffer which uses a "
229                  "VkFormat that Skia cannot render to (VkFormat: %d).\n", grBackendVkFormat);
230         return GrBackendTexture();
231     }
232 
233     if (importAsExternalFormat) {
234         if (!ycbcrConversion->isValid()) {
235             SkDebugf("YCbCr conversion must be valid when importing an AHardwareBuffer with an "
236                      "external format");
237             return GrBackendTexture();
238         }
239         SkASSERT(SkToBool(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT & hwbFormatProps.formatFeatures));
240         SkASSERT(hwbFormatProps.externalFormat == ycbcrConversion->fExternalFormat);
241         externalFormat.externalFormat = hwbFormatProps.externalFormat;
242     } else {
243         SkASSERT(!ycbcrConversion->isValid());
244         // Non-external formats are subject to format caps from VkPhysicalDeviceFormatProperties.
245         SkASSERT(gpu->vkCaps().isVkFormatTexturable(grBackendVkFormat));
246         // TODO: We currently assume that the provided VkFormat has transfer features
247         // (VK_FORMAT_FEATURE_TRANSFER_[SRC/DST]_BIT). Instead, we should have a way for Ganesh's
248         // tracking of intenral images to report whether or not they support transfers.
249     }
250 
251     const VkExternalMemoryImageCreateInfo externalMemoryImageInfo{
252             VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,                 // sType
253             &externalFormat,                                                     // pNext
254             VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,  // handleTypes
255     };
256 
257     VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_SAMPLED_BIT;
258     if (!importAsExternalFormat) {
259         usageFlags = usageFlags |
260                      VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
261                      VK_IMAGE_USAGE_TRANSFER_DST_BIT;
262         if (isRenderable) {
263             usageFlags = usageFlags | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
264         }
265     }
266 
267     VkImageCreateFlags flags = isProtectedContent ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
268 
269     const VkImageCreateInfo imageCreateInfo = {
270         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,         // sType
271         &externalMemoryImageInfo,                    // pNext
272         flags,                                       // VkImageCreateFlags
273         VK_IMAGE_TYPE_2D,                            // VkImageType
274         grBackendVkFormat,                           // VkFormat
275         { (uint32_t)width, (uint32_t)height, 1 },    // VkExtent3D
276         1,                                           // mipLevels
277         1,                                           // arrayLayers
278         VK_SAMPLE_COUNT_1_BIT,                       // samples
279         tiling,                                      // VkImageTiling
280         usageFlags,                                  // VkImageUsageFlags
281         VK_SHARING_MODE_EXCLUSIVE,                   // VkSharingMode
282         0,                                           // queueFamilyCount
283         nullptr,                                     // pQueueFamilyIndices
284         VK_IMAGE_LAYOUT_UNDEFINED,                   // initialLayout
285     };
286 
287     VkImage image;
288     VkResult err;
289     err = VK_CALL(CreateImage(device, &imageCreateInfo, nullptr, &image));
290     if (VK_SUCCESS != err) {
291         return GrBackendTexture();
292     }
293 
294     VkPhysicalDeviceMemoryProperties2 phyDevMemProps;
295     phyDevMemProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
296     phyDevMemProps.pNext = nullptr;
297     VK_CALL(GetPhysicalDeviceMemoryProperties2(physicalDevice, &phyDevMemProps));
298 
299     skgpu::VulkanAlloc alloc;
300     if (!skgpu::AllocateAndBindImageMemory(&alloc, image, phyDevMemProps, hwbProps, hardwareBuffer,
301                                            gpu->vkInterface(), device)) {
302         VK_CALL(DestroyImage(device, image, nullptr));
303         return GrBackendTexture();
304     }
305 
306     GrVkImageInfo imageInfo;
307     imageInfo.fImage = image;
308     imageInfo.fAlloc = alloc;
309     imageInfo.fImageTiling = tiling;
310     imageInfo.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
311     imageInfo.fFormat = grBackendVkFormat;
312     imageInfo.fLevelCount = 1;
313     // TODO: This should possibly be VK_QUEUE_FAMILY_FOREIGN_EXT but current Adreno devices do not
314     // support that extension. Or if we know the source of the AHardwareBuffer is not from a
315     // "foreign" device we can leave them as external.
316     imageInfo.fCurrentQueueFamily = VK_QUEUE_FAMILY_EXTERNAL;
317     imageInfo.fProtected = isProtectedContent ? GrProtected::kYes : GrProtected::kNo;
318     imageInfo.fYcbcrConversionInfo = *ycbcrConversion;
319     imageInfo.fSharingMode = imageCreateInfo.sharingMode;
320 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
321     imageInfo.fPartOfSwapchainOrAndroidWindow = fromAndroidWindow;
322 #endif
323 
324     *deleteProc = delete_vk_image;
325     *updateProc = update_vk_image;
326     *imageCtx = new VulkanCleanupHelper(gpu, image, alloc.fMemory);
327 
328     return GrBackendTextures::MakeVk(width, height, imageInfo);
329 }
330 
can_import_protected_content(GrDirectContext * dContext)331 static bool can_import_protected_content(GrDirectContext* dContext) {
332     SkASSERT(GrBackendApi::kVulkan == dContext->backend());
333     return static_cast<GrVkGpu*>(dContext->priv().getGpu())->protectedContext();
334 }
335 
MakeVulkanBackendTexture(GrDirectContext * dContext,AHardwareBuffer * hardwareBuffer,int width,int height,DeleteImageProc * deleteProc,UpdateImageProc * updateProc,TexImageCtx * imageCtx,bool isProtectedContent,const GrBackendFormat & backendFormat,bool isRenderable,bool fromAndroidWindow)336 GrBackendTexture MakeVulkanBackendTexture(GrDirectContext* dContext,
337                                           AHardwareBuffer* hardwareBuffer,
338                                           int width, int height,
339                                           DeleteImageProc* deleteProc,
340                                           UpdateImageProc* updateProc,
341                                           TexImageCtx* imageCtx,
342                                           bool isProtectedContent,
343                                           const GrBackendFormat& backendFormat,
344                                           bool isRenderable,
345                                           bool fromAndroidWindow) {
346     SkASSERT(dContext);
347     if (!dContext || dContext->abandoned()) {
348         return GrBackendTexture();
349     }
350 
351     if (GrBackendApi::kVulkan != dContext->backend()) {
352         return GrBackendTexture();
353     }
354 
355     if (isProtectedContent && !can_import_protected_content(dContext)) {
356         return GrBackendTexture();
357     }
358 
359     return make_vk_backend_texture(dContext, hardwareBuffer, width, height, deleteProc,
360                                    updateProc, imageCtx, isProtectedContent, backendFormat,
361                                    isRenderable, fromAndroidWindow);
362 }
363 
364 }  // namespace GrAHardwareBufferUtils
365 
366 #endif
367