diff --git a/libavfilter/vf_hwupload.c b/libavfilter/vf_hwupload.c index 6aafac4d4d..3142aabb17 100644 --- a/libavfilter/vf_hwupload.c +++ b/libavfilter/vf_hwupload.c @@ -222,6 +222,15 @@ static av_cold void hwupload_uninit(AVFilterContext *avctx) { HWUploadContext *ctx = avctx->priv; + /** + * Ensure that all outstanding asynchronous uploads are properly completed. + * This is to avoid leaking the context, because nobody else will clean up + * after the hwfc while there are still pending asynchronous transfers + * referencing it. + */ + if (ctx->hwframes_ref) + av_hwframe_transfer_wait_all(ctx->hwframes_ref, 0); + av_buffer_unref(&ctx->hwframes_ref); av_buffer_unref(&ctx->hwdevice_ref); } diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c index f06d49c45c..05c77ac7be 100644 --- a/libavutil/hwcontext.c +++ b/libavutil/hwcontext.c @@ -492,6 +492,15 @@ int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags) return 0; } +int av_hwframe_transfer_wait_all(AVBufferRef *hwframe_ref, int flags) +{ + FFHWFramesContext *ctx = (FFHWFramesContext*) hwframe_ref->data; + if (!ctx->hw_type->frames_sync) + return 0; + + return ctx->hw_type->frames_sync(&ctx->p); +} + int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags) { FFHWFramesContext *ctxi = (FFHWFramesContext*)hwframe_ref->data; diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h index 96042ba197..7a0d826e61 100644 --- a/libavutil/hwcontext.h +++ b/libavutil/hwcontext.h @@ -401,6 +401,16 @@ int av_hwframe_get_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame, int flags); */ int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags); +/** + * Explicitly wait for all preceding (possibly asynchronous) transfers to be + * completed. No-op for synchronous hardware device types. + * + * @param hwframe_ctx a reference to an AVHWFramesContext + * @param flags currently unused, should be set to zero + * @return 0 on success, a negative AVERROR error code on failure. + */ +int av_hwframe_transfer_wait_all(AVBufferRef *hwframe_ctx, int flags); + enum AVHWFrameTransferDirection { /** * Transfer the data from the queried hw frame. diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h index db23579c9e..e1b8c12424 100644 --- a/libavutil/hwcontext_internal.h +++ b/libavutil/hwcontext_internal.h @@ -69,6 +69,7 @@ typedef struct HWContextType { int (*frames_init)(AVHWFramesContext *ctx); void (*frames_uninit)(AVHWFramesContext *ctx); + int (*frames_sync)(AVHWFramesContext *ctx); int (*frames_get_buffer)(AVHWFramesContext *ctx, AVFrame *frame); int (*transfer_get_formats)(AVHWFramesContext *ctx, diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c index fec22a4f46..e1b3a6f346 100644 --- a/libavutil/hwcontext_vulkan.c +++ b/libavutil/hwcontext_vulkan.c @@ -2716,6 +2716,18 @@ static void vulkan_frames_uninit(AVHWFramesContext *hwfc) av_buffer_pool_uninit(&fp->tmp); } +static int vulkan_frames_sync(AVHWFramesContext *hwfc) +{ + VulkanDevicePriv *p = hwfc->device_ctx->hwctx; + VulkanFramesPriv *fp = hwfc->hwctx; + + ff_vk_exec_pool_wait(&p->vkctx, &fp->compute_exec); + ff_vk_exec_pool_wait(&p->vkctx, &fp->upload_exec); + ff_vk_exec_pool_wait(&p->vkctx, &fp->download_exec); + + return 0; +} + static int vulkan_frames_init(AVHWFramesContext *hwfc) { int err; @@ -4282,6 +4294,8 @@ static int vulkan_transfer_frame(AVHWFramesContext *hwfc, ff_vk_exec_wait(&p->vkctx, exec); if (!host_mapped) err = copy_buffer_data(hwfc, bufs[0], swf, region, planes, 0); + } else if (upload) { + ff_vk_exec_wait(&p->vkctx, exec); } end: @@ -4478,6 +4492,7 @@ const HWContextType ff_hwcontext_type_vulkan = { .frames_init = vulkan_frames_init, .frames_get_buffer = vulkan_get_buffer, .frames_uninit = vulkan_frames_uninit, + .frames_sync = vulkan_frames_sync, .transfer_get_formats = vulkan_transfer_get_formats, .transfer_data_to = vulkan_transfer_data_to, diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c index 0571ae64e1..4586d0edee 100644 --- a/libavutil/vulkan.c +++ b/libavutil/vulkan.c @@ -230,6 +230,16 @@ AVVulkanDeviceQueueFamily *ff_vk_qf_find(FFVulkanContext *s, return NULL; } +void ff_vk_exec_pool_wait(FFVulkanContext *s, FFVkExecPool *pool) +{ + FFVulkanFunctions *vk = &s->vkfn; + for (int i = 0; i < pool->pool_size; i++) { + FFVkExecContext *e = &pool->contexts[i]; + if (e->had_submission) + ff_vk_exec_wait(s, e); + } +} + void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool) { FFVulkanFunctions *vk = &s->vkfn; diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h index 8690c13b3d..c7a194ad61 100644 --- a/libavutil/vulkan.h +++ b/libavutil/vulkan.h @@ -408,6 +408,11 @@ int ff_vk_exec_pool_init(FFVulkanContext *s, AVVulkanDeviceQueueFamily *qf, const void *query_create_pnext); void ff_vk_exec_pool_free(FFVulkanContext *s, FFVkExecPool *pool); +/** + * Wait until all prior submissions have finished. + */ +void ff_vk_exec_pool_wait(FFVulkanContext *s, FFVkExecPool *pool); + /** * Retrieve an execution pool. Threadsafe. */