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