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