Skip to content

Commit bdc07f3

Browse files
committed
hwcontext_vulkan: add support for mapping multiplane images into CUDA
This patch refactors the CUDA import code to allow for Vulkan images with multiple planes to be mapped. Currently, a driver bug exists which causes NV12 images to be mapped incorrectly when the memory being mapped contains both planes, the issue has been reported to NVIDIA. yuv420p does work correctly, however. This is still an improvement, as the code used to crash when trying to map the memory, unless disable_multiplane=1 was given as an option.
1 parent 49c8f33 commit bdc07f3

File tree

1 file changed

+160
-102
lines changed

1 file changed

+160
-102
lines changed

libavutil/hwcontext_vulkan.c

Lines changed: 160 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -3409,6 +3409,130 @@ static int vulkan_map_from_vaapi(AVHWFramesContext *dst_fc,
34093409
#endif
34103410

34113411
#if CONFIG_CUDA
3412+
static int export_mem_to_cuda(AVHWDeviceContext *ctx,
3413+
AVHWDeviceContext *cuda_cu, CudaFunctions *cu,
3414+
AVVkFrameInternal *dst_int, int idx,
3415+
VkDeviceMemory mem, size_t size)
3416+
{
3417+
VkResult ret;
3418+
VulkanDevicePriv *p = ctx->hwctx;
3419+
AVVulkanDeviceContext *hwctx = &p->p;
3420+
FFVulkanFunctions *vk = &p->vkctx.vkfn;
3421+
3422+
#ifdef _WIN32
3423+
CUDA_EXTERNAL_MEMORY_HANDLE_DESC ext_desc = {
3424+
.type = IsWindows8OrGreater()
3425+
? CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32
3426+
: CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT,
3427+
.size = size,
3428+
};
3429+
VkMemoryGetWin32HandleInfoKHR export_info = {
3430+
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
3431+
.memory = mem,
3432+
.handleType = IsWindows8OrGreater()
3433+
? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
3434+
: VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,
3435+
};
3436+
3437+
ret = vk->GetMemoryWin32HandleKHR(hwctx->act_dev, &export_info,
3438+
&ext_desc.handle.win32.handle);
3439+
if (ret != VK_SUCCESS) {
3440+
av_log(ctx, AV_LOG_ERROR, "Unable to export the image as a Win32 Handle: %s!\n",
3441+
ff_vk_ret2str(ret));
3442+
return AVERROR_EXTERNAL;
3443+
}
3444+
dst_int->ext_mem_handle[idx] = ext_desc.handle.win32.handle;
3445+
#else
3446+
CUDA_EXTERNAL_MEMORY_HANDLE_DESC ext_desc = {
3447+
.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD,
3448+
.size = size,
3449+
};
3450+
VkMemoryGetFdInfoKHR export_info = {
3451+
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
3452+
.memory = mem,
3453+
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
3454+
};
3455+
3456+
ret = vk->GetMemoryFdKHR(hwctx->act_dev, &export_info,
3457+
&ext_desc.handle.fd);
3458+
if (ret != VK_SUCCESS) {
3459+
av_log(ctx, AV_LOG_ERROR, "Unable to export the image as a FD: %s!\n",
3460+
ff_vk_ret2str(ret));
3461+
return AVERROR_EXTERNAL;
3462+
}
3463+
#endif
3464+
3465+
ret = CHECK_CU(cu->cuImportExternalMemory(&dst_int->ext_mem[idx], &ext_desc));
3466+
if (ret < 0) {
3467+
#ifndef _WIN32
3468+
close(ext_desc.handle.fd);
3469+
#endif
3470+
return AVERROR_EXTERNAL;
3471+
}
3472+
3473+
return 0;
3474+
}
3475+
3476+
static int export_sem_to_cuda(AVHWDeviceContext *ctx,
3477+
AVHWDeviceContext *cuda_cu, CudaFunctions *cu,
3478+
AVVkFrameInternal *dst_int, int idx,
3479+
VkSemaphore sem)
3480+
{
3481+
VkResult ret;
3482+
VulkanDevicePriv *p = ctx->hwctx;
3483+
AVVulkanDeviceContext *hwctx = &p->p;
3484+
FFVulkanFunctions *vk = &p->vkctx.vkfn;
3485+
3486+
#ifdef _WIN32
3487+
VkSemaphoreGetWin32HandleInfoKHR sem_export = {
3488+
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR,
3489+
.semaphore = sem,
3490+
.handleType = IsWindows8OrGreater()
3491+
? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT
3492+
: VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,
3493+
};
3494+
CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC ext_sem_desc = {
3495+
.type = 10 /* TODO: CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TIMELINE_SEMAPHORE_WIN32 */,
3496+
};
3497+
#else
3498+
VkSemaphoreGetFdInfoKHR sem_export = {
3499+
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
3500+
.semaphore = sem,
3501+
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
3502+
};
3503+
CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC ext_sem_desc = {
3504+
.type = 9 /* TODO: CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TIMELINE_SEMAPHORE_FD */,
3505+
};
3506+
#endif
3507+
3508+
#ifdef _WIN32
3509+
ret = vk->GetSemaphoreWin32HandleKHR(hwctx->act_dev, &sem_export,
3510+
&ext_sem_desc.handle.win32.handle);
3511+
#else
3512+
ret = vk->GetSemaphoreFdKHR(hwctx->act_dev, &sem_export,
3513+
&ext_sem_desc.handle.fd);
3514+
#endif
3515+
if (ret != VK_SUCCESS) {
3516+
av_log(ctx, AV_LOG_ERROR, "Failed to export semaphore: %s\n",
3517+
ff_vk_ret2str(ret));
3518+
return AVERROR_EXTERNAL;
3519+
}
3520+
#ifdef _WIN32
3521+
dst_int->ext_sem_handle[idx] = ext_sem_desc.handle.win32.handle;
3522+
#endif
3523+
3524+
ret = CHECK_CU(cu->cuImportExternalSemaphore(&dst_int->cu_sem[idx],
3525+
&ext_sem_desc));
3526+
if (ret < 0) {
3527+
#ifndef _WIN32
3528+
close(ext_sem_desc.handle.fd);
3529+
#endif
3530+
return AVERROR_EXTERNAL;
3531+
}
3532+
3533+
return 0;
3534+
}
3535+
34123536
static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
34133537
AVBufferRef *cuda_hwfc,
34143538
const AVFrame *frame)
@@ -3423,6 +3547,7 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
34233547
VulkanDevicePriv *p = ctx->hwctx;
34243548
AVVulkanDeviceContext *hwctx = &p->p;
34253549
FFVulkanFunctions *vk = &p->vkctx.vkfn;
3550+
int nb_images;
34263551

34273552
AVHWFramesContext *cuda_fc = (AVHWFramesContext*)cuda_hwfc->data;
34283553
AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx;
@@ -3436,13 +3561,42 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
34363561
dst_int = dst_f->internal;
34373562

34383563
if (!dst_int->cuda_fc_ref) {
3564+
size_t offsets[3] = { 0 };
3565+
34393566
dst_int->cuda_fc_ref = av_buffer_ref(cuda_hwfc);
34403567
if (!dst_int->cuda_fc_ref)
34413568
return AVERROR(ENOMEM);
34423569

3570+
nb_images = ff_vk_count_images(dst_f);
3571+
for (int i = 0; i < nb_images; i++) {
3572+
err = export_mem_to_cuda(ctx, cuda_cu, cu, dst_int, i,
3573+
dst_f->mem[i], dst_f->size[i]);
3574+
if (err < 0)
3575+
goto fail;
3576+
3577+
err = export_sem_to_cuda(ctx, cuda_cu, cu, dst_int, i,
3578+
dst_f->sem[i]);
3579+
if (err < 0)
3580+
goto fail;
3581+
}
3582+
3583+
if (nb_images != planes) {
3584+
for (int i = 0; i < planes; i++) {
3585+
VkImageSubresource subres = {
3586+
.aspectMask = i == 2 ? VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT :
3587+
i == 1 ? VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT :
3588+
VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT
3589+
};
3590+
VkSubresourceLayout layout = { 0 };
3591+
vk->GetImageSubresourceLayout(hwctx->act_dev, dst_f->img[FFMIN(i, nb_images - 1)],
3592+
&subres, &layout);
3593+
offsets[i] = layout.offset;
3594+
}
3595+
}
3596+
34433597
for (int i = 0; i < planes; i++) {
34443598
CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC tex_desc = {
3445-
.offset = 0,
3599+
.offset = offsets[i],
34463600
.arrayDesc = {
34473601
.Depth = 0,
34483602
.Format = cufmt,
@@ -3453,84 +3607,12 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
34533607
};
34543608
int p_w, p_h;
34553609

3456-
#ifdef _WIN32
3457-
CUDA_EXTERNAL_MEMORY_HANDLE_DESC ext_desc = {
3458-
.type = IsWindows8OrGreater()
3459-
? CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32
3460-
: CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT,
3461-
.size = dst_f->size[i],
3462-
};
3463-
VkMemoryGetWin32HandleInfoKHR export_info = {
3464-
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR,
3465-
.memory = dst_f->mem[i],
3466-
.handleType = IsWindows8OrGreater()
3467-
? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
3468-
: VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,
3469-
};
3470-
VkSemaphoreGetWin32HandleInfoKHR sem_export = {
3471-
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR,
3472-
.semaphore = dst_f->sem[i],
3473-
.handleType = IsWindows8OrGreater()
3474-
? VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT
3475-
: VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT,
3476-
};
3477-
CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC ext_sem_desc = {
3478-
.type = 10 /* TODO: CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TIMELINE_SEMAPHORE_WIN32 */,
3479-
};
3480-
3481-
ret = vk->GetMemoryWin32HandleKHR(hwctx->act_dev, &export_info,
3482-
&ext_desc.handle.win32.handle);
3483-
if (ret != VK_SUCCESS) {
3484-
av_log(hwfc, AV_LOG_ERROR, "Unable to export the image as a Win32 Handle: %s!\n",
3485-
ff_vk_ret2str(ret));
3486-
err = AVERROR_EXTERNAL;
3487-
goto fail;
3488-
}
3489-
dst_int->ext_mem_handle[i] = ext_desc.handle.win32.handle;
3490-
#else
3491-
CUDA_EXTERNAL_MEMORY_HANDLE_DESC ext_desc = {
3492-
.type = CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD,
3493-
.size = dst_f->size[i],
3494-
};
3495-
VkMemoryGetFdInfoKHR export_info = {
3496-
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
3497-
.memory = dst_f->mem[i],
3498-
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
3499-
};
3500-
VkSemaphoreGetFdInfoKHR sem_export = {
3501-
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
3502-
.semaphore = dst_f->sem[i],
3503-
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
3504-
};
3505-
CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC ext_sem_desc = {
3506-
.type = 9 /* TODO: CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TIMELINE_SEMAPHORE_FD */,
3507-
};
3508-
3509-
ret = vk->GetMemoryFdKHR(hwctx->act_dev, &export_info,
3510-
&ext_desc.handle.fd);
3511-
if (ret != VK_SUCCESS) {
3512-
av_log(hwfc, AV_LOG_ERROR, "Unable to export the image as a FD: %s!\n",
3513-
ff_vk_ret2str(ret));
3514-
err = AVERROR_EXTERNAL;
3515-
goto fail;
3516-
}
3517-
#endif
3518-
3519-
ret = CHECK_CU(cu->cuImportExternalMemory(&dst_int->ext_mem[i], &ext_desc));
3520-
if (ret < 0) {
3521-
#ifndef _WIN32
3522-
close(ext_desc.handle.fd);
3523-
#endif
3524-
err = AVERROR_EXTERNAL;
3525-
goto fail;
3526-
}
3527-
35283610
get_plane_wh(&p_w, &p_h, hwfc->sw_format, hwfc->width, hwfc->height, i);
35293611
tex_desc.arrayDesc.Width = p_w;
35303612
tex_desc.arrayDesc.Height = p_h;
35313613

35323614
ret = CHECK_CU(cu->cuExternalMemoryGetMappedMipmappedArray(&dst_int->cu_mma[i],
3533-
dst_int->ext_mem[i],
3615+
dst_int->ext_mem[FFMIN(i, nb_images - 1)],
35343616
&tex_desc));
35353617
if (ret < 0) {
35363618
err = AVERROR_EXTERNAL;
@@ -3544,32 +3626,6 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
35443626
goto fail;
35453627
}
35463628

3547-
#ifdef _WIN32
3548-
ret = vk->GetSemaphoreWin32HandleKHR(hwctx->act_dev, &sem_export,
3549-
&ext_sem_desc.handle.win32.handle);
3550-
#else
3551-
ret = vk->GetSemaphoreFdKHR(hwctx->act_dev, &sem_export,
3552-
&ext_sem_desc.handle.fd);
3553-
#endif
3554-
if (ret != VK_SUCCESS) {
3555-
av_log(ctx, AV_LOG_ERROR, "Failed to export semaphore: %s\n",
3556-
ff_vk_ret2str(ret));
3557-
err = AVERROR_EXTERNAL;
3558-
goto fail;
3559-
}
3560-
#ifdef _WIN32
3561-
dst_int->ext_sem_handle[i] = ext_sem_desc.handle.win32.handle;
3562-
#endif
3563-
3564-
ret = CHECK_CU(cu->cuImportExternalSemaphore(&dst_int->cu_sem[i],
3565-
&ext_sem_desc));
3566-
if (ret < 0) {
3567-
#ifndef _WIN32
3568-
close(ext_sem_desc.handle.fd);
3569-
#endif
3570-
err = AVERROR_EXTERNAL;
3571-
goto fail;
3572-
}
35733629
}
35743630
}
35753631

@@ -4313,6 +4369,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
43134369
VulkanFramesPriv *fp = hwfc->hwctx;
43144370
const int planes = av_pix_fmt_count_planes(hwfc->sw_format);
43154371
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format);
4372+
int nb_images;
43164373

43174374
AVHWFramesContext *cuda_fc = (AVHWFramesContext*)dst->hw_frames_ctx->data;
43184375
AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx;
@@ -4323,6 +4380,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
43234380
CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS s_s_par[AV_NUM_DATA_POINTERS] = { 0 };
43244381

43254382
dst_f = (AVVkFrame *)src->data[0];
4383+
nb_images = ff_vk_count_images(dst_f);
43264384

43274385
err = prepare_frame(hwfc, &fp->upload_exec, dst_f, PREP_MODE_EXTERNAL_EXPORT);
43284386
if (err < 0)
@@ -4346,7 +4404,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
43464404
}
43474405

43484406
err = CHECK_CU(cu->cuWaitExternalSemaphoresAsync(dst_int->cu_sem, s_w_par,
4349-
planes, cuda_dev->stream));
4407+
nb_images, cuda_dev->stream));
43504408
if (err < 0)
43514409
goto fail;
43524410

@@ -4373,7 +4431,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
43734431
}
43744432

43754433
err = CHECK_CU(cu->cuSignalExternalSemaphoresAsync(dst_int->cu_sem, s_s_par,
4376-
planes, cuda_dev->stream));
4434+
nb_images, cuda_dev->stream));
43774435
if (err < 0)
43784436
goto fail;
43794437

0 commit comments

Comments
 (0)