Commit 7bc3576910 for ffmpeg
commit 7bc357691081c23963c014d08f43d381c977208c
Author: Dong Nguyen <Dong.Nguyen@amd.com>
Date: Mon Oct 5 15:35:14 2026 -0400
avfilter/vf_deinterlace_d3d12: fix command allocator sync before reset
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.
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,23 +506,33 @@ 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
- );
-
+ 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 command allocator: HRESULT 0x%lX\n", hr);
+ av_log(ctx, AV_LOG_ERROR, "Failed to create fence: HRESULT 0x%lX\n", hr);
return AVERROR_EXTERNAL;
}
+ s->fence_value = 1;
+
+ s->fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!s->fence_event) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to create fence event\n");
+ 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,
- s->command_allocator,
+ command_allocator,
NULL,
&IID_ID3D12VideoProcessCommandList,
(void **)&s->command_list
@@ -482,24 +540,15 @@ static int deint_d3d12_configure_processor(DeinterlaceD3D12Context *s,
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);
- 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);
- return AVERROR_EXTERNAL;
- }
-
- s->fence_value = 1;
-
- s->fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!s->fence_event) {
- av_log(ctx, AV_LOG_ERROR, "Failed to create fence event\n");
- return AVERROR_EXTERNAL;
- }
+ 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");
@@ -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;
}