FFmpeg
Loading...
Searching...
No Matches
vf_mestimate_d3d12.c
Go to the documentation of this file.
1/*
2 * D3D12 Hardware-Accelerated Motion Estimation Filter
3 *
4 * Copyright (c) 2025 Advanced Micro Devices, Inc.
5 *
6 * This file is part of FFmpeg.
7 *
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23#include "libavutil/avassert.h"
24#include "libavutil/buffer.h"
25#include "libavutil/hwcontext.h"
28#include "libavutil/internal.h"
29#include "libavutil/opt.h"
31#include "libavutil/mem.h"
32#include "avfilter.h"
33#include "filters.h"
34#include "video.h"
35
36
37typedef struct MEstimateD3D12Context {
38 const AVClass *class;
39
42
45
46 ID3D12Device *device;
47 ID3D12VideoDevice1 *video_device;
48 ID3D12VideoMotionEstimator *motion_estimator;
49 ID3D12VideoMotionVectorHeap *motion_vector_heap;
50 ID3D12VideoEncodeCommandList *command_list;
51 ID3D12CommandQueue *command_queue;
52 ID3D12CommandAllocator *command_allocator;
53
54 // Graphics command list and queue for copy operations
55 ID3D12GraphicsCommandList *copy_command_list;
56 ID3D12CommandAllocator *copy_command_allocator;
57 ID3D12CommandQueue *copy_command_queue;
58
59 // Synchronization
60 ID3D12Fence *fence;
62 uint64_t fence_value;
63
64 // Motion estimation parameters
65 int block_size; // 8 or 16
66 D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE d3d12_block_size;
67 D3D12_VIDEO_MOTION_ESTIMATOR_VECTOR_PRECISION precision;
68
69 // Frame buffer
73
74 // Output textures for resolved motion vectors (GPU-side, DEFAULT heap)
75 ID3D12Resource *resolved_mv_texture_back;
76 ID3D12Resource *resolved_mv_texture_fwd;
77
78 // Readback buffers for CPU access (READBACK heap)
79 ID3D12Resource *readback_buffer_back;
80 ID3D12Resource *readback_buffer_fwd;
82
85
87{
89
90 s->initialized = 0;
91 s->fence_value = 0;
92
93 // Validate block size - only 8 and 16 are valid
94 if (s->block_size != 8 && s->block_size != 16) {
95 av_log(ctx, AV_LOG_ERROR, "Invalid block_size %d. Only 8 and 16 are supported.\n", s->block_size);
96 return AVERROR(EINVAL);
97 }
98
99 // Set D3D12 block size based on user option
100 if (s->block_size == 8)
101 s->d3d12_block_size = D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_8X8;
102 else
103 s->d3d12_block_size = D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_16X16;
104
105 // Use quarter-pel precision
106 s->precision = D3D12_VIDEO_MOTION_ESTIMATOR_VECTOR_PRECISION_QUARTER_PEL;
107
108 return 0;
109}
110
112{
113 MEstimateD3D12Context *s = ctx->priv;
114 HRESULT hr;
115 D3D12_COMMAND_QUEUE_DESC queue_desc = {
116 .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
117 .Priority = 0,
118 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
119 .NodeMask = 0,
120 };
121
122 // Create fence for synchronization
123 hr = ID3D12Device_CreateFence(s->device, 0, D3D12_FENCE_FLAG_NONE,
124 &IID_ID3D12Fence, (void **)&s->fence);
125 if (FAILED(hr)) {
126 av_log(ctx, AV_LOG_ERROR, "Failed to create fence\n");
127 return AVERROR(EINVAL);
128 }
129
130 s->fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
131 if (!s->fence_event) {
132 av_log(ctx, AV_LOG_ERROR, "Failed to create fence event\n");
133 return AVERROR(EINVAL);
134 }
135
136 // Create command queue
137 hr = ID3D12Device_CreateCommandQueue(s->device, &queue_desc,
138 &IID_ID3D12CommandQueue, (void **)&s->command_queue);
139 if (FAILED(hr)) {
140 av_log(ctx, AV_LOG_ERROR, "Failed to create command queue\n");
141 return AVERROR(EINVAL);
142 }
143
144 // Create command allocator
145 hr = ID3D12Device_CreateCommandAllocator(s->device, D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
146 &IID_ID3D12CommandAllocator, (void **)&s->command_allocator);
147 if (FAILED(hr)) {
148 av_log(ctx, AV_LOG_ERROR, "Failed to create command allocator\n");
149 return AVERROR(EINVAL);
150 }
151
152 // Create command list
153 hr = ID3D12Device_CreateCommandList(s->device, 0, D3D12_COMMAND_LIST_TYPE_VIDEO_ENCODE,
154 s->command_allocator, NULL, &IID_ID3D12VideoEncodeCommandList,
155 (void **)&s->command_list);
156 if (FAILED(hr)) {
157 av_log(ctx, AV_LOG_ERROR, "Failed to create command list\n");
158 return AVERROR(EINVAL);
159 }
160
161 hr = ID3D12VideoEncodeCommandList_Close(s->command_list);
162 if (FAILED(hr)) {
163 av_log(ctx, AV_LOG_ERROR, "Failed to close command list\n");
164 return AVERROR(EINVAL);
165 }
166
167 return 0;
168}
169
171{
172 MEstimateD3D12Context *s = ctx->priv;
173 HRESULT hr;
174 D3D12_FEATURE_DATA_VIDEO_MOTION_ESTIMATOR feature_data = {0};
175 D3D12_VIDEO_MOTION_ESTIMATOR_DESC me_desc = {0};
176 D3D12_VIDEO_MOTION_VECTOR_HEAP_DESC heap_desc = {0};
177
178 // Check if motion estimation is supported
179 // Set the input parameters for what we want to query
180 feature_data.NodeIndex = 0;
181 feature_data.InputFormat = s->frames_ctx->format;
182 feature_data.BlockSizeFlags = 0; // Will be filled by CheckFeatureSupport with supported flags
183 feature_data.PrecisionFlags = 0; // Will be filled by CheckFeatureSupport with supported flags
184 feature_data.SizeRange.MaxWidth = width;
185 feature_data.SizeRange.MaxHeight = height;
186 feature_data.SizeRange.MinWidth = width;
187 feature_data.SizeRange.MinHeight = height;
188
189 hr = ID3D12VideoDevice1_CheckFeatureSupport(s->video_device,
190 D3D12_FEATURE_VIDEO_MOTION_ESTIMATOR,
191 &feature_data, sizeof(feature_data));
192 if (FAILED(hr)) {
193 av_log(ctx, AV_LOG_ERROR, "Failed to check motion estimator support (hr=0x%lx)\n", (long)hr);
194 return AVERROR(EINVAL);
195 }
196
197 // Verify the requested features are actually supported (check returned flags)
198 D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_FLAGS requested_block_flag =
199 (s->d3d12_block_size == D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_8X8) ?
200 D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_FLAG_8X8 :
201 D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE_FLAG_16X16;
202
203 if (!(feature_data.BlockSizeFlags & requested_block_flag)) {
204 av_log(ctx, AV_LOG_ERROR, "Requested block size (%dx%d) not supported by device (supported flags: 0x%x)\n",
205 s->block_size, s->block_size, feature_data.BlockSizeFlags);
206 return AVERROR(ENOSYS);
207 }
208
209 if (!(feature_data.PrecisionFlags & D3D12_VIDEO_MOTION_ESTIMATOR_VECTOR_PRECISION_FLAG_QUARTER_PEL)) {
210 av_log(ctx, AV_LOG_ERROR, "Quarter-pel precision not supported by device (supported flags: 0x%x)\n",
211 feature_data.PrecisionFlags);
212 return AVERROR(ENOSYS);
213 }
214
215 av_log(ctx, AV_LOG_VERBOSE, "Motion estimator support confirmed: block_size=%dx%d, precision=quarter-pel\n",
216 s->block_size, s->block_size);
217
218 // Create motion estimator
219 me_desc.NodeMask = 0;
220 me_desc.InputFormat = s->frames_ctx->format;
221 me_desc.BlockSize = s->d3d12_block_size;
222 me_desc.Precision = s->precision;
223 me_desc.SizeRange = feature_data.SizeRange;
224
225 hr = ID3D12VideoDevice1_CreateVideoMotionEstimator(s->video_device, &me_desc, NULL,
226 &IID_ID3D12VideoMotionEstimator,
227 (void **)&s->motion_estimator);
228 if (FAILED(hr)) {
229 av_log(ctx, AV_LOG_ERROR, "Failed to create motion estimator\n");
230 return AVERROR(EINVAL);
231 }
232
233 // Create motion vector heap
234 heap_desc.NodeMask = 0;
235 heap_desc.InputFormat = s->frames_ctx->format;
236 heap_desc.BlockSize = s->d3d12_block_size;
237 heap_desc.Precision = s->precision;
238 heap_desc.SizeRange = feature_data.SizeRange;
239
240 hr = ID3D12VideoDevice1_CreateVideoMotionVectorHeap(s->video_device, &heap_desc, NULL,
241 &IID_ID3D12VideoMotionVectorHeap,
242 (void **)&s->motion_vector_heap);
243 if (FAILED(hr)) {
244 av_log(ctx, AV_LOG_ERROR, "Failed to create motion vector heap\n");
245 return AVERROR(EINVAL);
246 }
247
248 // Create resolved motion vector textures in DEFAULT heap (GPU writable)
249 // ResolveMotionVectorHeap outputs to TEXTURE2D with DXGI_FORMAT_R16G16_SINT
250 int mb_width = (width + s->block_size - 1) / s->block_size;
251 int mb_height = (height + s->block_size - 1) / s->block_size;
252
253 D3D12_HEAP_PROPERTIES heap_props_default = {.Type = D3D12_HEAP_TYPE_DEFAULT};
254 D3D12_RESOURCE_DESC texture_desc = {
255 .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
256 .Alignment = 0,
257 .Width = mb_width,
258 .Height = mb_height,
259 .DepthOrArraySize = 1,
260 .MipLevels = 1,
261 .Format = DXGI_FORMAT_R16G16_SINT, // Motion vector format: signed 16-bit X,Y
262 .SampleDesc = {.Count = 1, .Quality = 0},
263 .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
264 .Flags = D3D12_RESOURCE_FLAG_NONE,
265 };
266
267 hr = ID3D12Device_CreateCommittedResource(s->device, &heap_props_default, D3D12_HEAP_FLAG_NONE,
268 &texture_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
269 &IID_ID3D12Resource, (void **)&s->resolved_mv_texture_back);
270 if (FAILED(hr)) {
271 av_log(ctx, AV_LOG_ERROR, "Failed to create backward motion vector texture (hr=0x%lx)\n", (long)hr);
272 return AVERROR(EINVAL);
273 }
274
275 hr = ID3D12Device_CreateCommittedResource(s->device, &heap_props_default, D3D12_HEAP_FLAG_NONE,
276 &texture_desc, D3D12_RESOURCE_STATE_COMMON, NULL,
277 &IID_ID3D12Resource, (void **)&s->resolved_mv_texture_fwd);
278 if (FAILED(hr)) {
279 av_log(ctx, AV_LOG_ERROR, "Failed to create forward motion vector texture (hr=0x%lx)\n", (long)hr);
280 return AVERROR(EINVAL);
281 }
282
283 // Create READBACK buffers for CPU access
284 // Need to calculate proper size accounting for D3D12 row pitch alignment
285 // Get the footprint to determine the actual required buffer size
286 D3D12_PLACED_SUBRESOURCE_FOOTPRINT temp_layout;
287 UINT64 temp_total_size;
288
289 ID3D12Device_GetCopyableFootprints(s->device, &texture_desc, 0, 1, 0,
290 &temp_layout, NULL, NULL, &temp_total_size);
291
292 s->readback_buffer_size = temp_total_size;
293
294 av_log(ctx, AV_LOG_DEBUG, "Readback buffer size: %llu bytes (texture: %dx%d, pitch: %u)\n",
295 (unsigned long long)s->readback_buffer_size, mb_width, mb_height, temp_layout.Footprint.RowPitch);
296
297 D3D12_HEAP_PROPERTIES heap_props_readback = {.Type = D3D12_HEAP_TYPE_READBACK};
298 D3D12_RESOURCE_DESC buffer_desc = {
299 .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
300 .Alignment = 0,
301 .Width = s->readback_buffer_size,
302 .Height = 1,
303 .DepthOrArraySize = 1,
304 .MipLevels = 1,
305 .Format = DXGI_FORMAT_UNKNOWN,
306 .SampleDesc = {.Count = 1, .Quality = 0},
307 .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
308 .Flags = D3D12_RESOURCE_FLAG_NONE,
309 };
310
311 hr = ID3D12Device_CreateCommittedResource(s->device, &heap_props_readback, D3D12_HEAP_FLAG_NONE,
312 &buffer_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
313 &IID_ID3D12Resource, (void **)&s->readback_buffer_back);
314 if (FAILED(hr)) {
315 av_log(ctx, AV_LOG_ERROR, "Failed to create backward readback buffer (hr=0x%lx)\n", (long)hr);
316 return AVERROR(EINVAL);
317 }
318
319 hr = ID3D12Device_CreateCommittedResource(s->device, &heap_props_readback, D3D12_HEAP_FLAG_NONE,
320 &buffer_desc, D3D12_RESOURCE_STATE_COPY_DEST, NULL,
321 &IID_ID3D12Resource, (void **)&s->readback_buffer_fwd);
322 if (FAILED(hr)) {
323 av_log(ctx, AV_LOG_ERROR, "Failed to create forward readback buffer (hr=0x%lx)\n", (long)hr);
324 return AVERROR(EINVAL);
325 }
326
327 // Create graphics command queue, allocator and list for copy operations
328 D3D12_COMMAND_QUEUE_DESC copy_queue_desc = {
329 .Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
330 .Priority = 0,
331 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
332 .NodeMask = 0,
333 };
334
335 hr = ID3D12Device_CreateCommandQueue(s->device, &copy_queue_desc,
336 &IID_ID3D12CommandQueue, (void **)&s->copy_command_queue);
337 if (FAILED(hr)) {
338 av_log(ctx, AV_LOG_ERROR, "Failed to create copy command queue\n");
339 return AVERROR(EINVAL);
340 }
341
342 hr = ID3D12Device_CreateCommandAllocator(s->device, D3D12_COMMAND_LIST_TYPE_DIRECT,
343 &IID_ID3D12CommandAllocator, (void **)&s->copy_command_allocator);
344 if (FAILED(hr)) {
345 av_log(ctx, AV_LOG_ERROR, "Failed to create copy command allocator\n");
346 return AVERROR(EINVAL);
347 }
348
349 hr = ID3D12Device_CreateCommandList(s->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT,
350 s->copy_command_allocator, NULL, &IID_ID3D12GraphicsCommandList,
351 (void **)&s->copy_command_list);
352 if (FAILED(hr)) {
353 av_log(ctx, AV_LOG_ERROR, "Failed to create copy command list\n");
354 return AVERROR(EINVAL);
355 }
356
357 hr = ID3D12GraphicsCommandList_Close(s->copy_command_list);
358 if (FAILED(hr)) {
359 av_log(ctx, AV_LOG_ERROR, "Failed to close copy command list\n");
360 return AVERROR(EINVAL);
361 }
362
363 return 0;
364}
365
367{
368 AVFilterContext *ctx = outlink->src;
369 AVFilterLink *inlink = ctx->inputs[0];
370 FilterLink *inl = ff_filter_link(inlink);
371 FilterLink *outl = ff_filter_link(outlink);
372 MEstimateD3D12Context *s = ctx->priv;
373 AVHWFramesContext *hw_frames_ctx;
374 HRESULT hr;
375 int err;
376
377 if (!inl->hw_frames_ctx) {
378 av_log(ctx, AV_LOG_ERROR, "D3D12 hardware frames context required\n");
379 return AVERROR(EINVAL);
380 }
381
382 hw_frames_ctx = (AVHWFramesContext *)inl->hw_frames_ctx->data;
383 if (hw_frames_ctx->format != AV_PIX_FMT_D3D12) {
384 av_log(ctx, AV_LOG_ERROR, "Input must be D3D12 frames\n");
385 return AVERROR(EINVAL);
386 }
387
388 s->hw_frames_ref = av_buffer_ref(inl->hw_frames_ctx);
389 if (!s->hw_frames_ref)
390 return AVERROR(ENOMEM);
391
392 s->frames_ctx = hw_frames_ctx->hwctx;
393 s->hw_device_ref = av_buffer_ref(hw_frames_ctx->device_ref);
394 if (!s->hw_device_ref)
395 return AVERROR(ENOMEM);
396
397 s->device_ctx = ((AVHWDeviceContext *)s->hw_device_ref->data)->hwctx;
398 s->device = s->device_ctx->device;
399
400 // Propagate hardware frames context to output
402 if (!outl->hw_frames_ctx)
403 return AVERROR(ENOMEM);
404
405 // Query for ID3D12VideoDevice1 interface from the base video device
406 hr = ID3D12VideoDevice_QueryInterface(s->device_ctx->video_device, &IID_ID3D12VideoDevice1,
407 (void **)&s->video_device);
408 if (FAILED(hr)) {
409 av_log(ctx, AV_LOG_ERROR, "ID3D12VideoDevice1 interface not supported\n");
410 return AVERROR(ENOSYS);
411 }
412
414 if (err < 0)
415 return err;
416
417 err = mestimate_d3d12_create_motion_estimator(ctx, inlink->w, inlink->h);
418 if (err < 0)
419 return err;
420
421 s->initialized = 1;
422
423 return 0;
424}
425
427{
428 uint64_t completion = ID3D12Fence_GetCompletedValue(s->fence);
429
430 if (completion < s->fence_value) {
431 if (FAILED(ID3D12Fence_SetEventOnCompletion(s->fence, s->fence_value, s->fence_event)))
432 return AVERROR(EINVAL);
433 WaitForSingleObjectEx(s->fence_event, INFINITE, FALSE);
434 }
435
436 return 0;
437}
438
439static inline void d3d12_barrier_transition(D3D12_RESOURCE_BARRIER *barrier,
440 ID3D12Resource *resource,
441 D3D12_RESOURCE_STATES state_before,
442 D3D12_RESOURCE_STATES state_after)
443{
444 barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
445 barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
446 barrier->Transition.pResource = resource;
447 barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
448 barrier->Transition.StateBefore = state_before;
449 barrier->Transition.StateAfter = state_after;
450}
451
452static void add_mv_data(AVMotionVector *mv, int mb_size,
453 int x, int y, int x_mv, int y_mv, int dir)
454{
455 mv->w = mb_size;
456 mv->h = mb_size;
457 mv->dst_x = x + (mb_size >> 1);
458 mv->dst_y = y + (mb_size >> 1);
459 mv->src_x = x_mv + (mb_size >> 1);
460 mv->src_y = y_mv + (mb_size >> 1);
461 mv->source = dir ? 1 : -1;
462 mv->flags = 0;
463 mv->motion_x = x_mv - x;
464 mv->motion_y = y_mv - y;
465 mv->motion_scale = 1;
466}
467
469{
470 MEstimateD3D12Context *s = ctx->priv;
471 uint8_t *mapped_data = NULL;
472 HRESULT hr;
473 int err = 0;
474 AVFrameSideData *sd;
475 AVMotionVector *mvs;
476 int mb_x, mb_y, mv_idx;
477 int mb_width, mb_height;
478 int16_t *d3d12_mvs;
479 ID3D12Resource *buffer = (direction == 0) ? s->readback_buffer_back : s->readback_buffer_fwd;
480
481 // Map the readback buffer
482 hr = ID3D12Resource_Map(buffer, 0, NULL, (void **)&mapped_data);
483 if (FAILED(hr)) {
484 av_log(ctx, AV_LOG_ERROR, "Failed to map readback buffer (dir=%d, hr=0x%lx)\n", direction, (long)hr);
485 return AVERROR(EINVAL);
486 }
487
488 // Get the motion vector side data
490 if (!sd) {
491 av_log(ctx, AV_LOG_ERROR, "No motion vector side data found\n");
492 ID3D12Resource_Unmap(buffer, 0, NULL);
493 return AVERROR(EINVAL);
494 }
495
496 mvs = (AVMotionVector *)sd->data;
497 mb_width = (out->width + s->block_size - 1) / s->block_size;
498 mb_height = (out->height + s->block_size - 1) / s->block_size;
499
500 // Calculate offset for this direction (0 = backward, 1 = forward)
501 mv_idx = direction * mb_width * mb_height;
502
503 // Parse D3D12 motion vector format
504 // According to Microsoft documentation:
505 // - Format: DXGI_FORMAT_R16G16_SINT (2D texture)
506 // - Data: Signed 16-bit integers
507 // - Units: Quarter-PEL (quarter pixel precision)
508 // - Layout: X component in R channel, Y component in G channel
509 // - Storage: 2D array matching block layout
510 //
511 // Each motion vector is stored as two int16_t values (X, Y) in quarter-pel units
512 // The buffer is organized as a 2D array: [mb_height][mb_width][2]
513
514 d3d12_mvs = (int16_t *)mapped_data;
515
516 for (mb_y = 0; mb_y < mb_height; mb_y++) {
517 for (mb_x = 0; mb_x < mb_width; mb_x++) {
518 const int x_mb = mb_x * s->block_size;
519 const int y_mb = mb_y * s->block_size;
520 const int mv_offset = (mb_y * mb_width + mb_x) * 2;
521
522 // Read motion vector components in quarter-pel units
523 // R component (index 0) = X motion
524 // G component (index 1) = Y motion
525 int16_t mv_x_qpel = d3d12_mvs[mv_offset + 0];
526 int16_t mv_y_qpel = d3d12_mvs[mv_offset + 1];
527
528 // Convert from quarter-pel to full pixel coordinates
529 // Quarter-pel means the value is 4x the actual pixel displacement
530 // So divide by 4 to get pixel displacement
531 int src_x = x_mb + (mv_x_qpel / 4);
532 int src_y = y_mb + (mv_y_qpel / 4);
533
534 // Store the motion vector data
535 // This will set dst (current position) and src (where it came from)
536 add_mv_data(&mvs[mv_idx++], s->block_size, x_mb, y_mb, src_x, src_y, direction);
537
538 av_log(ctx, AV_LOG_TRACE, "Block[%d,%d] dir=%d: MV=(%d,%d) qpel -> (%d,%d) pixels\n",
539 mb_x, mb_y, direction, mv_x_qpel, mv_y_qpel,
540 mv_x_qpel / 4, mv_y_qpel / 4);
541 }
542 }
543
544 ID3D12Resource_Unmap(buffer, 0, NULL);
545
546 av_log(ctx, AV_LOG_DEBUG, "Parsed %d motion vectors for direction %d\n",
547 mb_width * mb_height, direction);
548
549 return err;
550}
551
553{
554 AVFilterContext *ctx = inlink->dst;
555 MEstimateD3D12Context *s = ctx->priv;
556 AVFrame *out;
557 AVFrameSideData *sd;
558 AVD3D12VAFrame *cur_hwframe, *prev_hwframe, *next_hwframe = NULL;
559 HRESULT hr;
560 int err;
561 int mb_width, mb_height, mb_count;
562
563 if (!s->initialized) {
564 err = mestimate_d3d12_config_props(ctx->outputs[0]);
565 if (err < 0) {
567 return err;
568 }
569 }
570
571 // Manage frame buffer
572 av_frame_free(&s->prev_frame);
573 s->prev_frame = s->cur_frame;
574 s->cur_frame = s->next_frame;
575 s->next_frame = frame;
576
577 if (!s->cur_frame) {
578 s->cur_frame = av_frame_clone(frame);
579 if (!s->cur_frame)
580 return AVERROR(ENOMEM);
581 }
582
583 if (!s->prev_frame)
584 return 0;
585
586 // Clone current frame for output
587 out = av_frame_clone(s->cur_frame);
588 if (!out)
589 return AVERROR(ENOMEM);
590
591 mb_width = (frame->width + s->block_size - 1) / s->block_size;
592 mb_height = (frame->height + s->block_size - 1) / s->block_size;
593 mb_count = mb_width * mb_height;
594
595 // Allocate side data for motion vectors (2 directions)
597 2 * mb_count * sizeof(AVMotionVector));
598 if (!sd) {
600 return AVERROR(ENOMEM);
601 }
602
603 // Get hardware frame pointers
604 cur_hwframe = (AVD3D12VAFrame *)s->cur_frame->data[0];
605 prev_hwframe = (AVD3D12VAFrame *)s->prev_frame->data[0];
606 if (s->next_frame)
607 next_hwframe = (AVD3D12VAFrame *)s->next_frame->data[0];
608
609 // Reset command allocator and list ONCE for both estimations
610 hr = ID3D12CommandAllocator_Reset(s->command_allocator);
611 if (FAILED(hr)) {
612 av_log(ctx, AV_LOG_ERROR, "Failed to reset command allocator\n");
614 return AVERROR(EINVAL);
615 }
616
617 hr = ID3D12VideoEncodeCommandList_Reset(s->command_list, s->command_allocator);
618 if (FAILED(hr)) {
619 av_log(ctx, AV_LOG_ERROR, "Failed to reset command list\n");
621 return AVERROR(EINVAL);
622 }
623
624 // Transition current and previous frames to VIDEO_ENCODE_READ
625 D3D12_RESOURCE_BARRIER barriers[3];
626 int barrier_count = 2;
627
628 d3d12_barrier_transition(&barriers[0], cur_hwframe->texture,
629 D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
630 d3d12_barrier_transition(&barriers[1], prev_hwframe->texture,
631 D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
632
633 if (next_hwframe) {
634 d3d12_barrier_transition(&barriers[2], next_hwframe->texture,
635 D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_ENCODE_READ);
636 barrier_count = 3;
637 }
638
639 ID3D12VideoEncodeCommandList_ResourceBarrier(s->command_list, barrier_count, barriers);
640
641 // Backward motion estimation (cur -> prev)
642 D3D12_VIDEO_MOTION_ESTIMATOR_INPUT input_back = {
643 .pInputTexture2D = cur_hwframe->texture,
644 .InputSubresourceIndex = 0,
645 .pReferenceTexture2D = prev_hwframe->texture,
646 .ReferenceSubresourceIndex = 0,
647 .pHintMotionVectorHeap = NULL,
648 };
649
650 D3D12_VIDEO_MOTION_ESTIMATOR_OUTPUT output = {
651 .pMotionVectorHeap = s->motion_vector_heap,
652 };
653
654 ID3D12VideoEncodeCommandList_EstimateMotion(s->command_list, s->motion_estimator,
655 &output, &input_back);
656
657 D3D12_RESOLVE_VIDEO_MOTION_VECTOR_HEAP_INPUT resolve_input = {
658 .pMotionVectorHeap = s->motion_vector_heap,
659 .PixelWidth = s->cur_frame->width,
660 .PixelHeight = s->cur_frame->height,
661 };
662
663 D3D12_RESOLVE_VIDEO_MOTION_VECTOR_HEAP_OUTPUT resolve_output_back = {
664 .pMotionVectorTexture2D = s->resolved_mv_texture_back,
665 .MotionVectorCoordinate = {.X = 0, .Y = 0, .Z = 0, .SubresourceIndex = 0},
666 };
667
668 ID3D12VideoEncodeCommandList_ResolveMotionVectorHeap(s->command_list,
669 &resolve_output_back, &resolve_input);
670
671 // Copy resolved texture to readback buffer for CPU access
672 // CopyTextureRegion is not available on video encode command list
673 // We'll need to read directly from the resolved texture after GPU sync
674
675 // Forward motion estimation (cur -> next) if next frame exists
676 if (next_hwframe) {
677 D3D12_VIDEO_MOTION_ESTIMATOR_INPUT input_fwd = {
678 .pInputTexture2D = cur_hwframe->texture,
679 .InputSubresourceIndex = 0,
680 .pReferenceTexture2D = next_hwframe->texture,
681 .ReferenceSubresourceIndex = 0,
682 .pHintMotionVectorHeap = NULL,
683 };
684
685 ID3D12VideoEncodeCommandList_EstimateMotion(s->command_list, s->motion_estimator,
686 &output, &input_fwd);
687
688 D3D12_RESOLVE_VIDEO_MOTION_VECTOR_HEAP_OUTPUT resolve_output_fwd = {
689 .pMotionVectorTexture2D = s->resolved_mv_texture_fwd,
690 .MotionVectorCoordinate = {.X = 0, .Y = 0, .Z = 0, .SubresourceIndex = 0},
691 };
692
693 ID3D12VideoEncodeCommandList_ResolveMotionVectorHeap(s->command_list,
694 &resolve_output_fwd, &resolve_input);
695
696 // Copy will be done after command list execution
697 }
698
699 // Transition resources back to COMMON (reuse barriers by swapping states)
700 for (int i = 0; i < barrier_count; i++)
701 FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
702
703 ID3D12VideoEncodeCommandList_ResourceBarrier(s->command_list, barrier_count, barriers);
704
705 // Close command list ONCE
706 hr = ID3D12VideoEncodeCommandList_Close(s->command_list);
707 if (FAILED(hr)) {
708 av_log(ctx, AV_LOG_ERROR, "Failed to close command list (hr=0x%lx)\n", (long)hr);
710 return AVERROR(EINVAL);
711 }
712
713 // Wait for input frame sync
714 hr = ID3D12CommandQueue_Wait(s->command_queue, cur_hwframe->sync_ctx.fence,
715 cur_hwframe->sync_ctx.fence_value);
716 if (FAILED(hr)) {
717 av_log(ctx, AV_LOG_ERROR, "Failed to wait for current frame\n");
719 return AVERROR(EINVAL);
720 }
721
722 hr = ID3D12CommandQueue_Wait(s->command_queue, prev_hwframe->sync_ctx.fence,
723 prev_hwframe->sync_ctx.fence_value);
724 if (FAILED(hr)) {
725 av_log(ctx, AV_LOG_ERROR, "Failed to wait for previous frame\n");
727 return AVERROR(EINVAL);
728 }
729
730 if (next_hwframe) {
731 hr = ID3D12CommandQueue_Wait(s->command_queue, next_hwframe->sync_ctx.fence,
732 next_hwframe->sync_ctx.fence_value);
733 if (FAILED(hr)) {
734 av_log(ctx, AV_LOG_ERROR, "Failed to wait for next frame\n");
736 return AVERROR(EINVAL);
737 }
738 }
739
740 // Execute command list ONCE
741 ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list);
742
743 // Signal completion
744 hr = ID3D12CommandQueue_Signal(s->command_queue, s->fence, ++s->fence_value);
745 if (FAILED(hr)) {
746 av_log(ctx, AV_LOG_ERROR, "Failed to signal fence\n");
748 return AVERROR(EINVAL);
749 }
750
751 // Wait for GPU to complete
753 if (err < 0) {
755 return err;
756 }
757
758 // Now copy the resolved textures to readback buffers using graphics command list
759 hr = ID3D12CommandAllocator_Reset(s->copy_command_allocator);
760 if (FAILED(hr)) {
761 av_log(ctx, AV_LOG_ERROR, "Failed to reset copy command allocator\n");
763 return AVERROR(EINVAL);
764 }
765
766 hr = ID3D12GraphicsCommandList_Reset(s->copy_command_list, s->copy_command_allocator, NULL);
767 if (FAILED(hr)) {
768 av_log(ctx, AV_LOG_ERROR, "Failed to reset copy command list\n");
770 return AVERROR(EINVAL);
771 }
772
773 // Transition resolved textures to COPY_SOURCE state
774 D3D12_RESOURCE_BARRIER copy_barriers[2];
775 int copy_barrier_count = 1;
776
777 d3d12_barrier_transition(&copy_barriers[0], s->resolved_mv_texture_back,
778 D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
779
780 if (s->next_frame) {
781 d3d12_barrier_transition(&copy_barriers[1], s->resolved_mv_texture_fwd,
782 D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
783 copy_barrier_count = 2;
784 }
785
786 ID3D12GraphicsCommandList_ResourceBarrier(s->copy_command_list, copy_barrier_count, copy_barriers);
787
788 // Get texture layout for backward copy
789 D3D12_RESOURCE_DESC texture_desc_back;
790 D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout_back;
791 UINT64 row_size_back, total_size_back;
792 UINT num_rows_back;
793
794 // Get the resource description for backward texture
795 s->resolved_mv_texture_back->lpVtbl->GetDesc(s->resolved_mv_texture_back, &texture_desc_back);
796
797 av_log(ctx, AV_LOG_DEBUG, "Back texture desc: Width=%llu, Height=%u, Format=%d\n",
798 (unsigned long long)texture_desc_back.Width, texture_desc_back.Height, texture_desc_back.Format);
799
800 // Get the copyable footprints for the backward texture
801 ID3D12Device_GetCopyableFootprints(s->device, &texture_desc_back, 0, 1, 0,
802 &layout_back, &num_rows_back, &row_size_back, &total_size_back);
803
804 av_log(ctx, AV_LOG_DEBUG, "Back layout: Offset=%llu, Width=%u, Height=%u, Depth=%u, RowPitch=%u\n",
805 (unsigned long long)layout_back.Offset, layout_back.Footprint.Width, layout_back.Footprint.Height,
806 layout_back.Footprint.Depth, layout_back.Footprint.RowPitch);
807
808 // Copy backward motion vectors
809 D3D12_TEXTURE_COPY_LOCATION src_back = {
810 .pResource = s->resolved_mv_texture_back,
811 .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
812 .SubresourceIndex = 0
813 };
814
815 D3D12_TEXTURE_COPY_LOCATION dst_back = {
816 .pResource = s->readback_buffer_back,
817 .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
818 .PlacedFootprint = {
819 .Offset = 0,
820 .Footprint = layout_back.Footprint
821 }
822 };
823
824 av_log(ctx, AV_LOG_DEBUG, "Copying backward MVs...\n");
825 ID3D12GraphicsCommandList_CopyTextureRegion(s->copy_command_list, &dst_back, 0, 0, 0, &src_back, NULL);
826
827 // Copy forward motion vectors if available
828 if (s->next_frame) {
829 // Get texture layout for forward copy
830 D3D12_RESOURCE_DESC texture_desc_fwd;
831 D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout_fwd;
832 UINT64 row_size_fwd, total_size_fwd;
833 UINT num_rows_fwd;
834
835 // Get the resource description for forward texture
836 s->resolved_mv_texture_fwd->lpVtbl->GetDesc(s->resolved_mv_texture_fwd, &texture_desc_fwd);
837
838 av_log(ctx, AV_LOG_DEBUG, "Fwd texture desc: Width=%llu, Height=%u, Format=%d\n",
839 (unsigned long long)texture_desc_fwd.Width, texture_desc_fwd.Height, texture_desc_fwd.Format);
840
841 // Get the copyable footprints for the forward texture
842 ID3D12Device_GetCopyableFootprints(s->device, &texture_desc_fwd, 0, 1, 0,
843 &layout_fwd, &num_rows_fwd, &row_size_fwd, &total_size_fwd);
844
845 av_log(ctx, AV_LOG_DEBUG, "Fwd layout: Offset=%llu, Width=%u, Height=%u, Depth=%u, RowPitch=%u\n",
846 (unsigned long long)layout_fwd.Offset, layout_fwd.Footprint.Width, layout_fwd.Footprint.Height,
847 layout_fwd.Footprint.Depth, layout_fwd.Footprint.RowPitch);
848
849 D3D12_TEXTURE_COPY_LOCATION src_fwd = {
850 .pResource = s->resolved_mv_texture_fwd,
851 .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
852 .SubresourceIndex = 0
853 };
854
855 D3D12_TEXTURE_COPY_LOCATION dst_fwd = {
856 .pResource = s->readback_buffer_fwd,
857 .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
858 .PlacedFootprint = {
859 .Offset = 0,
860 .Footprint = layout_fwd.Footprint
861 }
862 };
863
864 av_log(ctx, AV_LOG_DEBUG, "Copying forward MVs...\n");
865 ID3D12GraphicsCommandList_CopyTextureRegion(s->copy_command_list, &dst_fwd, 0, 0, 0, &src_fwd, NULL);
866 }
867
868 // Transition back to COMMON state (reuse barriers by swapping states)
869 for (int i = 0; i < copy_barrier_count; i++)
870 FFSWAP(D3D12_RESOURCE_STATES, copy_barriers[i].Transition.StateBefore, copy_barriers[i].Transition.StateAfter);
871
872 ID3D12GraphicsCommandList_ResourceBarrier(s->copy_command_list, copy_barrier_count, copy_barriers);
873
874 hr = ID3D12GraphicsCommandList_Close(s->copy_command_list);
875 if (FAILED(hr)) {
876 av_log(ctx, AV_LOG_ERROR, "Failed to close copy command list (hr=0x%lx)\n", (long)hr);
878 return AVERROR(EINVAL);
879 }
880
881 // Execute copy command list on the copy queue
882 ID3D12CommandQueue_ExecuteCommandLists(s->copy_command_queue, 1, (ID3D12CommandList **)&s->copy_command_list);
883
884 // Signal and wait for copy completion
885 hr = ID3D12CommandQueue_Signal(s->copy_command_queue, s->fence, ++s->fence_value);
886 if (FAILED(hr)) {
887 av_log(ctx, AV_LOG_ERROR, "Failed to signal fence for copy\n");
889 return AVERROR(EINVAL);
890 }
891
893 if (err < 0) {
895 return err;
896 }
897
898 // Read motion vectors for both directions
900 if (err < 0) {
902 return err;
903 }
904
905 if (s->next_frame) {
907 if (err < 0) {
909 return err;
910 }
911 }
912
913 return ff_filter_frame(ctx->outputs[0], out);
914}
915
917{
918 MEstimateD3D12Context *s = ctx->priv;
919
920 av_frame_free(&s->prev_frame);
921 av_frame_free(&s->cur_frame);
922 av_frame_free(&s->next_frame);
923
924 D3D12_OBJECT_RELEASE(s->copy_command_list);
925 D3D12_OBJECT_RELEASE(s->copy_command_allocator);
926 D3D12_OBJECT_RELEASE(s->copy_command_queue);
927 D3D12_OBJECT_RELEASE(s->readback_buffer_back);
928 D3D12_OBJECT_RELEASE(s->readback_buffer_fwd);
929 D3D12_OBJECT_RELEASE(s->resolved_mv_texture_back);
930 D3D12_OBJECT_RELEASE(s->resolved_mv_texture_fwd);
931 D3D12_OBJECT_RELEASE(s->motion_vector_heap);
932 D3D12_OBJECT_RELEASE(s->motion_estimator);
933 D3D12_OBJECT_RELEASE(s->command_list);
934 D3D12_OBJECT_RELEASE(s->command_allocator);
935 D3D12_OBJECT_RELEASE(s->command_queue);
936 D3D12_OBJECT_RELEASE(s->fence);
937
938 if (s->fence_event)
939 CloseHandle(s->fence_event);
940
941 av_buffer_unref(&s->hw_frames_ref);
942 av_buffer_unref(&s->hw_device_ref);
943}
944
946 {
947 .name = "default",
948 .type = AVMEDIA_TYPE_VIDEO,
949 .filter_frame = mestimate_d3d12_filter_frame,
950 },
951};
952
954 {
955 .name = "default",
956 .type = AVMEDIA_TYPE_VIDEO,
957 .config_props = mestimate_d3d12_config_props,
958 },
959};
960
961#define OFFSET(x) offsetof(MEstimateD3D12Context, x)
962#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
963
965 { "mb_size", "macroblock size, only 8 and 16 are supported", OFFSET(block_size), AV_OPT_TYPE_INT, {.i64 = 16}, 8, 16, FLAGS },
966 { NULL }
967};
968
969AVFILTER_DEFINE_CLASS(mestimate_d3d12);
970
972 .p.name = "mestimate_d3d12",
973 .p.description = NULL_IF_CONFIG_SMALL("Generate motion vectors using D3D12 hardware acceleration."),
974 .p.priv_class = &mestimate_d3d12_class,
976 .priv_size = sizeof(MEstimateD3D12Context),
979 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
983};
const FFFilter ff_vf_mestimate_d3d12
simple assert() macros that are a bit more flexible than ISO C assert().
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
refcounted data buffer API
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
static AVFrame * frame
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
#define AVFILTER_FLAG_HWDEVICE
The filter can create hardware frames using AVFilterContext.hw_device_ctx.
Definition avfilter.h:187
#define AVFILTER_FLAG_METADATA_ONLY
The filter is a "metadata" filter - it does not modify the frame data in any way.
Definition avfilter.h:182
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:103
#define AVERROR(e)
Definition error.h:45
AVFrameSideData * av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type)
Definition frame.c:659
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
AVFrameSideData * av_frame_new_side_data(AVFrame *frame, enum AVFrameSideDataType type, size_t size)
Add a new side data to a frame.
Definition frame.c:647
AVFrame * av_frame_clone(const AVFrame *src)
Create a new frame that references the same data as src.
Definition frame.c:483
@ AV_FRAME_DATA_MOTION_VECTORS
Motion vectors exported by some codecs (on demand through the export_mvs flag set in the libavcodec A...
Definition frame.h:97
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition log.h:236
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
An API-specific header for AV_HWDEVICE_TYPE_D3D12VA.
#define D3D12_OBJECT_RELEASE(pInterface)
A release macro used by D3D12 objects highly frequently.
static av_cold void uninit(AVBitStreamFilterContext *ctx)
static const int8_t mv[256][2]
Definition 4xm.c:81
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
#define av_cold
Definition attributes.h:117
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define FFSWAP(type, a, b)
Definition macros.h:52
Memory handling functions.
AVOptions.
@ AV_PIX_FMT_D3D12
Hardware surfaces for Direct3D 12.
Definition pixfmt.h:440
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
Describe the class of an AVClass context structure.
Definition log.h:76
This struct is allocated as AVHWDeviceContext.hwctx.
D3D12VA frame descriptor for pool allocation.
ID3D12Resource * texture
The texture in which the frame is located.
AVD3D12VASyncContext sync_ctx
The sync context for the texture.
This struct is allocated as AVHWFramesContext.hwctx.
ID3D12Fence * fence
D3D12 fence object.
uint64_t fence_value
The fence value used for sync.
An instance of a filter.
Definition avfilter.h:273
A filter pad used for either input or output.
Definition filters.h:40
Structure to hold side data for an AVFrame.
Definition frame.h:327
uint8_t * data
Definition frame.h:329
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition hwcontext.h:63
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition hwcontext.h:200
AVBufferRef * device_ref
A reference to the parent AVHWDeviceContext.
Definition hwcontext.h:129
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition hwcontext.h:153
AVOption.
Definition opt.h:428
D3D12_VIDEO_MOTION_ESTIMATOR_SEARCH_BLOCK_SIZE d3d12_block_size
ID3D12Resource * resolved_mv_texture_back
ID3D12VideoMotionVectorHeap * motion_vector_heap
ID3D12VideoEncodeCommandList * command_list
ID3D12CommandAllocator * copy_command_allocator
AVD3D12VADeviceContext * device_ctx
ID3D12GraphicsCommandList * copy_command_list
ID3D12Resource * readback_buffer_fwd
D3D12_VIDEO_MOTION_ESTIMATOR_VECTOR_PRECISION precision
ID3D12CommandQueue * command_queue
ID3D12Resource * resolved_mv_texture_fwd
AVD3D12VAFramesContext * frames_ctx
ID3D12CommandQueue * copy_command_queue
ID3D12VideoDevice1 * video_device
ID3D12Resource * readback_buffer_back
ID3D12VideoMotionEstimator * motion_estimator
ID3D12CommandAllocator * command_allocator
#define av_log(a,...)
static FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
static char buffer[20]
Definition seek.c:32
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
static const AVFilterPad mestimate_d3d12_outputs[]
static int mestimate_d3d12_create_motion_estimator(AVFilterContext *ctx, int width, int height)
static const AVFilterPad mestimate_d3d12_inputs[]
static const AVOption mestimate_d3d12_options[]
static void add_mv_data(AVMotionVector *mv, int mb_size, int x, int y, int x_mv, int y_mv, int dir)
static void d3d12_barrier_transition(D3D12_RESOURCE_BARRIER *barrier, ID3D12Resource *resource, D3D12_RESOURCE_STATES state_before, D3D12_RESOURCE_STATES state_after)
static av_cold void mestimate_d3d12_uninit(AVFilterContext *ctx)
static int mestimate_d3d12_config_props(AVFilterLink *outlink)
static int mestimate_d3d12_read_motion_vectors(AVFilterContext *ctx, AVFrame *out, int direction)
static int mestimate_d3d12_init(AVFilterContext *ctx)
#define OFFSET(x)
static int mestimate_d3d12_filter_frame(AVFilterLink *inlink, AVFrame *frame)
static int mestimate_d3d12_sync_gpu(MEstimateD3D12Context *s)
static int mestimate_d3d12_create_objects(AVFilterContext *ctx)