PR #24900 opened by dongnguyenminhanh URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24900 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24900.patch
The fix replaces the single allocator with a pool of allocators tagged with fence values, matching the pattern in d3d12va_encode.c. Each allocator is only reused after its fence value has been reached. This is a follow up action for issue report https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/24483. The current vf_deinterlace_d3d12 has similar issue: ID3D12CommandAllocator_Reset() was called each frame without checking that the GPU had finished using that allocator. If the GPU was still processing the previous frame's commands, resetting the allocator could corrupt or discard in flight work, leading to rendering errors or GPU faults. >From 6450670828d6ef1c38f231cf0cd0ffc4f78c34ce Mon Sep 17 00:00:00 2001 From: Dong Nguyen <[email protected]> Date: Mon, 5 Oct 2026 15:35:14 -0400 Subject: [PATCH] avfilter/vf_deinterlace_d3d12: fix command allocator sync before reset Same race condition as vf_scale_d3d12: ID3D12CommandAllocator_Reset() was called each frame without checking that the GPU finished using that allocator. Use a pool with fence-value tracking instead. --- libavfilter/vf_deinterlace_d3d12.c | 133 +++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 36 deletions(-) diff --git a/libavfilter/vf_deinterlace_d3d12.c b/libavfilter/vf_deinterlace_d3d12.c index 5eb9b227f1..ab0d47f27a 100644 --- a/libavfilter/vf_deinterlace_d3d12.c +++ b/libavfilter/vf_deinterlace_d3d12.c @@ -22,6 +22,7 @@ #define COBJMACROS +#include "libavutil/fifo.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -43,6 +44,11 @@ enum DeinterlaceD3D12Method { DEINT_D3D12_METHOD_CUSTOM = 2, // Driver-defined advanced deinterlacing }; +typedef struct CommandAllocator { + ID3D12CommandAllocator *command_allocator; + UINT64 fence_value; +} CommandAllocator; + typedef struct DeinterlaceD3D12Context { const AVClass *classCtx; @@ -57,7 +63,8 @@ typedef struct DeinterlaceD3D12Context { ID3D12VideoProcessor *video_processor; ID3D12CommandQueue *command_queue; ID3D12VideoProcessCommandList *command_list; - ID3D12CommandAllocator *command_allocator; + + AVFifo *allocator_queue; /* Synchronization */ ID3D12Fence *fence; @@ -101,15 +108,53 @@ typedef struct DeinterlaceD3D12Context { static av_cold int deint_d3d12_init(AVFilterContext *ctx) { DeinterlaceD3D12Context *s = ctx->priv; - s->fence_value = 1; s->processor_configured = 0; return 0; } +static int deint_d3d12_get_valid_command_allocator(DeinterlaceD3D12Context *s, AVFilterContext *ctx, + ID3D12CommandAllocator **ppAllocator) +{ + HRESULT hr; + CommandAllocator allocator; + + if (av_fifo_peek(s->allocator_queue, &allocator, 1, 0) >= 0) { + UINT64 completed = ID3D12Fence_GetCompletedValue(s->fence); + if (completed >= allocator.fence_value) { + *ppAllocator = allocator.command_allocator; + av_fifo_read(s->allocator_queue, &allocator, 1); + return 0; + } + } + + hr = ID3D12Device_CreateCommandAllocator(s->device, D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, + &IID_ID3D12CommandAllocator, (void **)ppAllocator); + if (FAILED(hr)) { + av_log(ctx, AV_LOG_ERROR, "Failed to create command allocator: HRESULT 0x%lX\n", hr); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static int deint_d3d12_discard_command_allocator(DeinterlaceD3D12Context *s, ID3D12CommandAllocator *pAllocator, + UINT64 fence_value) +{ + CommandAllocator allocator = { + .command_allocator = pAllocator, + .fence_value = fence_value, + }; + + av_fifo_write(s->allocator_queue, &allocator, 1); + + return 0; +} + static void release_d3d12_resources(DeinterlaceD3D12Context *s) { UINT64 fence_value; HRESULT hr; + CommandAllocator allocator; /* Wait for all GPU operations to complete before releasing resources */ if (s->command_queue && s->fence && s->fence_event) { @@ -141,9 +186,10 @@ static void release_d3d12_resources(DeinterlaceD3D12Context *s) s->command_list = NULL; } - if (s->command_allocator) { - ID3D12CommandAllocator_Release(s->command_allocator); - s->command_allocator = NULL; + if (s->allocator_queue) { + while (av_fifo_read(s->allocator_queue, &allocator, 1) >= 0) + ID3D12CommandAllocator_Release(allocator.command_allocator); + av_fifo_freep2(&s->allocator_queue); } if (s->video_processor) { @@ -263,10 +309,12 @@ static int deint_d3d12_configure_processor(DeinterlaceD3D12Context *s, AVFrame *in) { HRESULT hr; + int ret; AVHWDeviceContext *hwctx = (AVHWDeviceContext *)s->hw_device_ctx->data; AVD3D12VADeviceContext *d3d12_hwctx = (AVD3D12VADeviceContext *)hwctx->hwctx; D3D12_VIDEO_PROCESS_DEINTERLACE_FLAGS deint_method; D3D12_VIDEO_FIELD_TYPE field_type; + ID3D12CommandAllocator *command_allocator = NULL; s->device = d3d12_hwctx->device; @@ -458,35 +506,6 @@ static int deint_d3d12_configure_processor(DeinterlaceD3D12Context *s, return AVERROR_EXTERNAL; } - hr = ID3D12Device_CreateCommandAllocator( - s->device, - D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, - &IID_ID3D12CommandAllocator, - (void **)&s->command_allocator - ); - - if (FAILED(hr)) { - av_log(ctx, AV_LOG_ERROR, "Failed to create command allocator: HRESULT 0x%lX\n", hr); - return AVERROR_EXTERNAL; - } - - hr = ID3D12Device_CreateCommandList( - s->device, - 0, - D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, - s->command_allocator, - NULL, - &IID_ID3D12VideoProcessCommandList, - (void **)&s->command_list - ); - - if (FAILED(hr)) { - av_log(ctx, AV_LOG_ERROR, "Failed to create command list: HRESULT 0x%lX\n", hr); - return AVERROR_EXTERNAL; - } - - ID3D12VideoProcessCommandList_Close(s->command_list); - hr = ID3D12Device_CreateFence(s->device, 0, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, (void **)&s->fence); if (FAILED(hr)) { av_log(ctx, AV_LOG_ERROR, "Failed to create fence: HRESULT 0x%lX\n", hr); @@ -501,6 +520,36 @@ static int deint_d3d12_configure_processor(DeinterlaceD3D12Context *s, return AVERROR_EXTERNAL; } + s->allocator_queue = av_fifo_alloc2(2, sizeof(CommandAllocator), AV_FIFO_FLAG_AUTO_GROW); + if (!s->allocator_queue) + return AVERROR(ENOMEM); + + ret = deint_d3d12_get_valid_command_allocator(s, ctx, &command_allocator); + if (ret < 0) + return ret; + + hr = ID3D12Device_CreateCommandList( + s->device, + 0, + D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, + command_allocator, + NULL, + &IID_ID3D12VideoProcessCommandList, + (void **)&s->command_list + ); + + if (FAILED(hr)) { + av_log(ctx, AV_LOG_ERROR, "Failed to create command list: HRESULT 0x%lX\n", hr); + ID3D12CommandAllocator_Release(command_allocator); + return AVERROR_EXTERNAL; + } + + ID3D12VideoProcessCommandList_Close(s->command_list); + + ret = deint_d3d12_discard_command_allocator(s, command_allocator, 0); + if (ret < 0) + return ret; + s->processor_configured = 1; av_log(ctx, AV_LOG_VERBOSE, "D3D12 deinterlace processor successfully configured\n"); return 0; @@ -534,6 +583,7 @@ static int deint_d3d12_process_frame(AVFilterContext *ctx, int ret = 0; int i; HRESULT hr; + ID3D12CommandAllocator *command_allocator = NULL; AVD3D12VAFrame *in_d3d12_frame = (AVD3D12VAFrame *)input_frame->data[0]; @@ -638,14 +688,18 @@ static int deint_d3d12_process_frame(AVFilterContext *ctx, } } - hr = ID3D12CommandAllocator_Reset(s->command_allocator); + ret = deint_d3d12_get_valid_command_allocator(s, ctx, &command_allocator); + if (ret < 0) + goto fail; + + hr = ID3D12CommandAllocator_Reset(command_allocator); if (FAILED(hr)) { av_log(ctx, AV_LOG_ERROR, "Failed to reset command allocator: HRESULT 0x%lX\n", hr); ret = AVERROR_EXTERNAL; goto fail; } - hr = ID3D12VideoProcessCommandList_Reset(s->command_list, s->command_allocator); + hr = ID3D12VideoProcessCommandList_Reset(s->command_list, command_allocator); if (FAILED(hr)) { av_log(ctx, AV_LOG_ERROR, "Failed to reset command list: HRESULT 0x%lX\n", hr); ret = AVERROR_EXTERNAL; @@ -742,6 +796,11 @@ static int deint_d3d12_process_frame(AVFilterContext *ctx, out_d3d12_frame->sync_ctx.fence_value = s->fence_value; ID3D12Fence_AddRef(s->fence); + ret = deint_d3d12_discard_command_allocator(s, command_allocator, s->fence_value); + command_allocator = NULL; + if (ret < 0) + goto fail; + s->fence_value++; ret = av_frame_copy_props(out, input_frame); @@ -775,6 +834,8 @@ static int deint_d3d12_process_frame(AVFilterContext *ctx, return ff_filter_frame(outlink, out); fail: + if (command_allocator) + deint_d3d12_discard_command_allocator(s, command_allocator, s->fence_value); av_frame_free(&out); return ret; } -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
