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