FFmpeg
d3d12va_decode.c
Go to the documentation of this file.
1 /*
2  * Direct3D 12 HW acceleration video decoder
3  *
4  * copyright (c) 2022-2023 Wu Jianhua <toqsxw@outlook.com>
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 <string.h>
24 #include <initguid.h>
25 
26 #include "libavutil/common.h"
27 #include "libavutil/log.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/time.h"
30 #include "libavutil/imgutils.h"
33 #include "avcodec.h"
34 #include "decode.h"
35 #include "d3d12va_decode.h"
36 #include "dxva2_internal.h"
37 
38 typedef struct HelperObjects {
39  ID3D12CommandAllocator *command_allocator;
40  ID3D12Resource *buffer;
41  uint64_t fence_value;
43 
44 typedef struct ReferenceFrame {
45  ID3D12Resource *resource;
46  int used;
47  ID3D12Resource *output_resource;
49 
50 static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12Resource *output_resource)
51 {
53  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
54  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
55  int i = 0;
56  ID3D12Resource *resource = NULL;
57  D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
58  D3D12_RESOURCE_DESC desc;
59  ReferenceFrame *reference_only_map = ctx->reference_only_map;
60  if (reference_only_map == NULL) {
61  av_log(avctx, AV_LOG_ERROR, "Reference frames are not allocated!\n");
62  return NULL;
63  }
64 
65  // find unused resource
66  for (i = 0; i < ctx->max_num_ref; i++) {
67  if (!reference_only_map[i].used && reference_only_map[i].resource != NULL) {
68  reference_only_map[i].used = 1;
69  resource = reference_only_map[i].resource;
70  reference_only_map[i].output_resource = output_resource;
71  return resource;
72  }
73  }
74 
75  // find space to allocate
76  for (i = 0; i < ctx->max_num_ref; i++) {
77  if (reference_only_map[i].resource == NULL)
78  break;
79  }
80 
81  if (i == ctx->max_num_ref) {
82  av_log(avctx, AV_LOG_ERROR, "No space for new Reference frame!\n");
83  return NULL;
84  }
85 
86  // allocate frame
87  output_resource->lpVtbl->GetDesc(output_resource, &desc);
88  desc.Flags = D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
89 
90  if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc,
91  D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&reference_only_map[i].resource))) {
92  av_log(ctx, AV_LOG_ERROR, "Failed to create D3D12 Reference Resource!\n");
93  return NULL;
94  }
95 
96  reference_only_map[i].used = 1;
97  resource = reference_only_map[i].resource;
98  reference_only_map[i].output_resource = output_resource;
99 
100  return resource;
101 }
102 
104 {
106  int i;
107  ReferenceFrame *reference_only_map = ctx->reference_only_map;
108  if (reference_only_map != NULL) {
109  for (i = 0; i < ctx->max_num_ref; i++) {
110  if (reference_only_map[i].resource != NULL) {
111  D3D12_OBJECT_RELEASE(reference_only_map[i].resource);
112  }
113  }
114  av_freep(&ctx->reference_only_map);
115  av_freep(&ctx->ref_only_resources);
116  }
117 }
118 
120 {
122  int i, j;
123  ReferenceFrame *reference_only_map = ctx->reference_only_map;
124  if (reference_only_map == NULL)
125  return;
126  memset(ctx->ref_only_resources, 0, ctx->max_num_ref * sizeof(*(ctx->ref_only_resources)));
127  for (j = 0; j < ctx->max_num_ref; j++) {
128  for (i = 0; i < ctx->max_num_ref; i++) {
129  if (reference_only_map[j].used && reference_only_map[j].output_resource == ctx->ref_resources[i]) {
130  ctx->ref_only_resources[i] = reference_only_map[j].resource;
131  break;
132  }
133  }
134  if (i == ctx->max_num_ref)
135  reference_only_map[j].used = 0;
136  }
137 }
138 
140 {
141  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
142  return av_image_get_buffer_size(frames_ctx->sw_format, avctx->coded_width, avctx->coded_height, 1);
143 }
144 
147  int curr)
148 {
149  AVD3D12VAFrame *f;
150  ID3D12Resource *res;
151  unsigned i;
152 
153  f = (AVD3D12VAFrame *)frame->data[0];
154  if (!f)
155  goto fail;
156 
157  res = f->texture;
158  if (!res)
159  goto fail;
160 
161  for (i = 0; i < ctx->max_num_ref; i++) {
162  if (ctx->ref_resources[i] && res == ctx->ref_resources[i]) {
163  ctx->used_mask |= 1 << i;
164  return i;
165  }
166  }
167 
168  if (curr) {
169  for (i = 0; i < ctx->max_num_ref; i++) {
170  if (!((ctx->used_mask >> i) & 0x1)) {
171  ctx->ref_resources[i] = res;
172  return i;
173  }
174  }
175  }
176 
177 fail:
178  av_log((AVCodecContext *)avctx, AV_LOG_WARNING, "Could not get surface index. Using 0 instead.\n");
179  return 0;
180 }
181 
182 static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator,
183  ID3D12Resource **ppBuffer)
184 {
185  HRESULT hr;
187  HelperObjects obj = { 0 };
188  D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD };
189 
190  D3D12_RESOURCE_DESC desc = {
191  .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
192  .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
193  .Width = ctx->bitstream_size,
194  .Height = 1,
195  .DepthOrArraySize = 1,
196  .MipLevels = 1,
197  .Format = DXGI_FORMAT_UNKNOWN,
198  .SampleDesc = { .Count = 1, .Quality = 0 },
199  .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
200  .Flags = D3D12_RESOURCE_FLAG_NONE,
201  };
202 
203  if (av_fifo_peek(ctx->objects_queue, &obj, 1, 0) >= 0) {
204  uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence);
205  if (completion >= obj.fence_value) {
206  *ppAllocator = obj.command_allocator;
207  *ppBuffer = obj.buffer;
208  av_fifo_read(ctx->objects_queue, &obj, 1);
209  return 0;
210  }
211  }
212 
213  hr = ID3D12Device_CreateCommandAllocator(ctx->device_ctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
214  &IID_ID3D12CommandAllocator, (void **)ppAllocator);
215  if (FAILED(hr)) {
216  av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n");
217  return AVERROR(EINVAL);
218  }
219 
220  hr = ID3D12Device_CreateCommittedResource(ctx->device_ctx->device, &heap_props, D3D12_HEAP_FLAG_NONE,
221  &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
222  &IID_ID3D12Resource, (void **)ppBuffer);
223 
224  if (FAILED(hr)) {
225  av_log(avctx, AV_LOG_ERROR, "Failed to create a new d3d12 buffer!\n");
226  return AVERROR(EINVAL);
227  }
228 
229  return 0;
230 }
231 
232 static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator,
233  ID3D12Resource *pBuffer, uint64_t fence_value)
234 {
236 
237  HelperObjects obj = {
238  .command_allocator = pAllocator,
239  .buffer = pBuffer,
240  .fence_value = fence_value,
241  };
242 
243  if (av_fifo_write(ctx->objects_queue, &obj, 1) < 0) {
244  D3D12_OBJECT_RELEASE(pAllocator);
245  D3D12_OBJECT_RELEASE(pBuffer);
246  return AVERROR(ENOMEM);
247  }
248 
249  return 0;
250 }
251 
253 {
254  uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence);
255  if (completion < psync_ctx->fence_value) {
256  if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event)))
257  return AVERROR(EINVAL);
258 
259  WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE);
260  }
261 
262  return 0;
263 }
264 
265 static void bufref_free_interface(void *opaque, uint8_t *data)
266 {
267  D3D12_OBJECT_RELEASE(opaque);
268 }
269 
270 static AVBufferRef *bufref_wrap_interface(IUnknown *iface)
271 {
272  return av_buffer_create((uint8_t*)iface, 1, bufref_free_interface, iface, 0);
273 }
274 
276 {
278 
279  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
280  return d3d12va_fence_completion(&ctx->sync_ctx);
281 
282 fail:
283  return AVERROR(EINVAL);
284 }
285 
287 {
289  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
290  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
291  AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
292 
293  D3D12_VIDEO_DECODER_HEAP_DESC desc = {
294  .NodeMask = 0,
295  .Configuration = ctx->cfg,
296  .DecodeWidth = frames_ctx->width,
297  .DecodeHeight = frames_ctx->height,
298  .Format = frames_hwctx->format,
299  .FrameRate = { avctx->framerate.num, avctx->framerate.den },
300  .BitRate = avctx->bit_rate,
301  .MaxDecodePictureBufferCount = ctx->max_num_ref,
302  };
303 
304  DX_CHECK(ID3D12VideoDevice_CreateVideoDecoderHeap(device_hwctx->video_device, &desc,
305  &IID_ID3D12VideoDecoderHeap, (void **)&ctx->decoder_heap));
306 
307  return 0;
308 
309 fail:
310  if (ctx->decoder) {
311  av_log(avctx, AV_LOG_ERROR, "D3D12 doesn't support decoding frames with an extent "
312  "[width(%d), height(%d)], on your device!\n", frames_ctx->width, frames_ctx->height);
313  }
314 
315  return AVERROR(EINVAL);
316 }
317 
319 {
320  D3D12_VIDEO_DECODER_DESC desc;
322  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
323  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
324  AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
325 
326  D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT feature = {
327  .NodeIndex = 0,
328  .Configuration = ctx->cfg,
329  .Width = frames_ctx->width,
330  .Height = frames_ctx->height,
331  .DecodeFormat = frames_hwctx->format,
332  .FrameRate = { avctx->framerate.num, avctx->framerate.den },
333  .BitRate = avctx->bit_rate,
334  };
335 
336  DX_CHECK(ID3D12VideoDevice_CheckFeatureSupport(device_hwctx->video_device, D3D12_FEATURE_VIDEO_DECODE_SUPPORT,
337  &feature, sizeof(feature)));
338  if (!(feature.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)) {
339  av_log(avctx, AV_LOG_ERROR, "D3D12 video decode is not supported on this device.\n");
340  return AVERROR(ENOSYS);
341  }
342  if (!(feature.DecodeTier >= D3D12_VIDEO_DECODE_TIER_2)) {
343  av_log(avctx, AV_LOG_ERROR, "D3D12 video decode on this device requires tier %d support, "
344  "but it is not implemented.\n", feature.DecodeTier);
345  return AVERROR_PATCHWELCOME;
346  }
347 
348  ctx->reference_only_map = NULL;
349  ctx->ref_only_resources = NULL;
350  if (feature.ConfigurationFlags & D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_REFERENCE_ONLY_ALLOCATIONS_REQUIRED) {
351  av_log(avctx, AV_LOG_VERBOSE, "Reference-Only Allocations are required for this D3D12 decoder configuration.\n");
352  ctx->reference_only_map = av_calloc(ctx->max_num_ref + 1, sizeof(ReferenceFrame));
353  if (!ctx->reference_only_map)
354  return AVERROR(ENOMEM);
355  ctx->ref_only_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_only_resources));
356  if (!ctx->ref_only_resources)
357  return AVERROR(ENOMEM);
358  }
359 
360  desc = (D3D12_VIDEO_DECODER_DESC) {
361  .NodeMask = 0,
362  .Configuration = ctx->cfg,
363  };
364 
365  DX_CHECK(ID3D12VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc, &IID_ID3D12VideoDecoder,
366  (void **)&ctx->decoder));
367 
368  ctx->decoder_ref = bufref_wrap_interface((IUnknown *)ctx->decoder);
369  if (!ctx->decoder_ref)
370  return AVERROR(ENOMEM);
371 
372  return 0;
373 
374 fail:
375  return AVERROR(EINVAL);
376 }
377 
379 {
380  AVHWFramesContext *frames_ctx = (AVHWFramesContext *)hw_frames_ctx->data;
381 
382  frames_ctx->format = AV_PIX_FMT_D3D12;
384  frames_ctx->width = avctx->width;
385  frames_ctx->height = avctx->height;
386 
387  return 0;
388 }
389 
391 {
392  int ret;
393  AVHWFramesContext *frames_ctx;
395  ID3D12Resource *buffer = NULL;
396  ID3D12CommandAllocator *command_allocator = NULL;
397  D3D12_COMMAND_QUEUE_DESC queue_desc = {
398  .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
399  .Priority = 0,
400  .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
401  .NodeMask = 0,
402  };
403 
404  ctx->pix_fmt = avctx->hwaccel->pix_fmt;
405 
407  if (ret < 0)
408  return ret;
409 
410  frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
411  ctx->device_ctx = (AVD3D12VADeviceContext *)frames_ctx->device_ctx->hwctx;
412 
413  if (frames_ctx->format != ctx->pix_fmt) {
414  av_log(avctx, AV_LOG_ERROR, "Invalid pixfmt for hwaccel!\n");
415  goto fail;
416  }
417 
418  ret = d3d12va_create_decoder(avctx);
419  if (ret < 0)
420  goto fail;
421 
423  if (ret < 0)
424  goto fail;
425 
426  ctx->bitstream_size = ff_d3d12va_get_suitable_max_bitstream_size(avctx);
427 
428  ctx->ref_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_resources));
429  if (!ctx->ref_resources)
430  return AVERROR(ENOMEM);
431 
432  ctx->ref_subresources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_subresources));
433  if (!ctx->ref_subresources)
434  return AVERROR(ENOMEM);
435 
438  if (!ctx->objects_queue)
439  return AVERROR(ENOMEM);
440 
441  DX_CHECK(ID3D12Device_CreateFence(ctx->device_ctx->device, 0, D3D12_FENCE_FLAG_NONE,
442  &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence));
443 
444  ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
445  if (!ctx->sync_ctx.event)
446  goto fail;
447 
448  ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer);
449  if (ret < 0)
450  goto fail;
451 
452  DX_CHECK(ID3D12Device_CreateCommandQueue(ctx->device_ctx->device, &queue_desc,
453  &IID_ID3D12CommandQueue, (void **)&ctx->command_queue));
454 
455  DX_CHECK(ID3D12Device_CreateCommandList(ctx->device_ctx->device, 0, queue_desc.Type,
456  command_allocator, NULL, &IID_ID3D12CommandList, (void **)&ctx->command_list));
457 
458  DX_CHECK(ID3D12VideoDecodeCommandList_Close(ctx->command_list));
459 
460  ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
461 
462  ret = d3d12va_sync_with_gpu(avctx);
463  if (ret < 0)
464  goto fail;
465 
466  d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
467  if (ret < 0)
468  goto fail;
469 
470  return 0;
471 
472 fail:
473  D3D12_OBJECT_RELEASE(command_allocator);
476 
477  return AVERROR(EINVAL);
478 }
479 
481 {
482  int num_allocator = 0;
484  HelperObjects obj;
485 
486  if (ctx->sync_ctx.fence)
487  d3d12va_sync_with_gpu(avctx);
488 
489  av_freep(&ctx->ref_resources);
490  av_freep(&ctx->ref_subresources);
491 
492  D3D12_OBJECT_RELEASE(ctx->command_list);
493  D3D12_OBJECT_RELEASE(ctx->command_queue);
494 
495  if (ctx->objects_queue) {
496  while (av_fifo_read(ctx->objects_queue, &obj, 1) >= 0) {
497  num_allocator++;
500  }
501 
502  av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator);
503  }
505 
506  av_fifo_freep2(&ctx->objects_queue);
507 
508  D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence);
509  if (ctx->sync_ctx.event)
510  CloseHandle(ctx->sync_ctx.event);
511 
512  D3D12_OBJECT_RELEASE(ctx->decoder_heap);
513 
514  av_buffer_unref(&ctx->decoder_ref);
515 
516  return 0;
517 }
518 
519 static inline int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers,
520  ID3D12Resource *current_resource, int state_before, int state_end)
521 {
523  ID3D12Resource **ref_resources = ctx->ref_only_resources ? ctx->ref_only_resources : ctx->ref_resources;
524 
525  int num_barrier = 0;
526  for (int i = 0; i < ctx->max_num_ref; i++) {
527  if (((ctx->used_mask >> i) & 0x1) && ref_resources[i] && ref_resources[i] != current_resource) {
528  barriers[num_barrier].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
529  barriers[num_barrier].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
530  barriers[num_barrier].Transition = (D3D12_RESOURCE_TRANSITION_BARRIER) {
531  .pResource = ref_resources[i],
532  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
533  .StateBefore = state_before,
534  .StateAfter = state_end,
535  };
536  num_barrier++;
537  }
538  }
539 
540  return num_barrier;
541 }
542 
544  const void *pp, unsigned pp_size,
545  const void *qm, unsigned qm_size,
546  int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *))
547 {
548  int ret;
550  ID3D12Resource *buffer = NULL;
551  ID3D12CommandAllocator *command_allocator = NULL;
552  AVD3D12VAFrame *f = (AVD3D12VAFrame*)frame->data[0];
553  ID3D12Resource *output_resource = (ID3D12Resource*)f->texture;
554  ID3D12Resource *ref_resource = NULL;
555 
556  ID3D12VideoDecodeCommandList *cmd_list = ctx->command_list;
557  D3D12_RESOURCE_BARRIER barriers[32] = { 0 };
558 
559  D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS input_args = {
560  .NumFrameArguments = 2,
561  .FrameArguments = {
562  [0] = {
563  .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_PICTURE_PARAMETERS,
564  .Size = pp_size,
565  .pData = (void *)pp,
566  },
567  [1] = {
568  .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_INVERSE_QUANTIZATION_MATRIX,
569  .Size = qm_size,
570  .pData = (void *)qm,
571  },
572  },
573  .pHeap = ctx->decoder_heap,
574  };
575 
576  D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS output_args = {
577  .ConversionArguments = { 0 },
578  .OutputSubresource = 0,
579  .pOutputTexture2D = output_resource,
580  };
581 
582  memset(ctx->ref_subresources, 0, sizeof(UINT) * ctx->max_num_ref);
583  input_args.ReferenceFrames.NumTexture2Ds = ctx->max_num_ref;
584  input_args.ReferenceFrames.pSubresources = ctx->ref_subresources;
585 
586  if (ctx->reference_only_map) {
587  ref_resource = get_reference_only_resource(avctx, output_resource);
588  if (ref_resource == NULL) {
589  av_log(avctx, AV_LOG_ERROR, "Failed to get reference frame!\n");
590  goto fail;
591  }
593 
594  output_args.ConversionArguments.Enable = 1;
595  input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_only_resources;
596  output_args.ConversionArguments.pReferenceTexture2D = ref_resource;
597  output_args.ConversionArguments.ReferenceSubresource = 0;
598  } else {
599  ref_resource = output_resource;
600  input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_resources;
601  }
602 
603  UINT num_barrier = 1;
604  barriers[0] = (D3D12_RESOURCE_BARRIER) {
605  .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
606  .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
607  .Transition = {
608  .pResource = output_resource,
609  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
610  .StateBefore = D3D12_RESOURCE_STATE_COMMON,
611  .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
612  },
613  };
614 
615  if (ctx->reference_only_map) {
616  barriers[1] = (D3D12_RESOURCE_BARRIER) {
617  .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
618  .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
619  .Transition = {
620  .pResource = ref_resource,
621  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
622  .StateBefore = D3D12_RESOURCE_STATE_COMMON,
623  .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
624  },
625  };
626  num_barrier++;
627  }
628 
629  ret = d3d12va_fence_completion(&f->sync_ctx);
630  if (ret < 0)
631  goto fail;
632 
633  if (!qm)
634  input_args.NumFrameArguments = 1;
635 
636  ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer);
637  if (ret < 0)
638  goto fail;
639 
640  ret = update_input_arguments(avctx, &input_args, buffer);
641  if (ret < 0)
642  goto fail;
643 
644  DX_CHECK(ID3D12CommandAllocator_Reset(command_allocator));
645 
646  DX_CHECK(ID3D12VideoDecodeCommandList_Reset(cmd_list, command_allocator));
647 
648  num_barrier += d3d12va_update_reference_frames_state(avctx, &barriers[num_barrier], ref_resource, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ);
649 
650  ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
651 
652  ID3D12VideoDecodeCommandList_DecodeFrame(cmd_list, ctx->decoder, &output_args, &input_args);
653 
654  for (int i = 0; i < num_barrier; i++)
655  FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
656 
657  ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
658 
659  DX_CHECK(ID3D12VideoDecodeCommandList_Close(cmd_list));
660 
661  ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
662 
663  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, f->sync_ctx.fence, ++f->sync_ctx.fence_value));
664 
665  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
666 
667  ret = d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
668  if (ret < 0)
669  return ret;
670 
671  return 0;
672 
673 fail:
674  if (command_allocator)
675  d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
676  return AVERROR(EINVAL);
677 }
AVHWDeviceContext::hwctx
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition: hwcontext.h:86
AVD3D12VADeviceContext::device
ID3D12Device * device
Device used for objects creation and access.
Definition: hwcontext_d3d12va.h:54
AVCodecContext::hwaccel
const struct AVHWAccel * hwaccel
Hardware accelerator in use.
Definition: avcodec.h:1405
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:215
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
HelperObjects
Definition: d3d12va_decode.c:38
ReferenceFrame
Definition: d3d12va_decode.c:44
AVBufferRef::data
uint8_t * data
The data buffer.
Definition: buffer.h:90
AVHWFramesContext::format
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition: hwcontext.h:198
av_fifo_peek
int av_fifo_peek(const AVFifo *f, void *buf, size_t nb_elems, size_t offset)
Read data from a FIFO without modifying FIFO state.
Definition: fifo.c:255
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:410
D3D12VA_VIDEO_DEC_ASYNC_DEPTH
#define D3D12VA_VIDEO_DEC_ASYNC_DEPTH
Definition: d3d12va_decode.h:140
bufref_free_interface
static void bufref_free_interface(void *opaque, uint8_t *data)
Definition: d3d12va_decode.c:265
data
const char data[16]
Definition: mxf.c:149
AV_PIX_FMT_YUV420P10
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:528
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:225
ff_d3d12va_get_surface_index
unsigned ff_d3d12va_get_surface_index(const AVCodecContext *avctx, D3D12VADecodeContext *ctx, const AVFrame *frame, int curr)
Definition: d3d12va_decode.c:145
AVHWFramesContext::width
int width
The allocated dimensions of the frames in this pool.
Definition: hwcontext.h:218
AVCodecContext::framerate
AVRational framerate
Definition: avcodec.h:551
fail
#define fail()
Definition: checkasm.h:194
av_fifo_write
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition: fifo.c:188
ReferenceFrame::resource
ID3D12Resource * resource
Definition: d3d12va_decode.c:45
AVCodecContext::coded_height
int coded_height
Definition: avcodec.h:607
AVRational::num
int num
Numerator.
Definition: rational.h:59
d3d12va_get_valid_helper_objects
static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator, ID3D12Resource **ppBuffer)
Definition: d3d12va_decode.c:182
free_reference_only_resources
static void free_reference_only_resources(AVCodecContext *avctx)
Definition: d3d12va_decode.c:103
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
av_cold
#define av_cold
Definition: attributes.h:90
d3d12va_sync_with_gpu
static int d3d12va_sync_with_gpu(AVCodecContext *avctx)
Definition: d3d12va_decode.c:275
av_fifo_read
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition: fifo.c:240
d3d12va_fence_completion
static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx)
Definition: d3d12va_decode.c:252
AVHWFramesContext::height
int height
Definition: hwcontext.h:218
D3D12_OBJECT_RELEASE
#define D3D12_OBJECT_RELEASE(pInterface)
A release macro used by D3D12 objects highly frequently.
Definition: hwcontext_d3d12va_internal.h:51
ctx
AVFormatContext * ctx
Definition: movenc.c:49
ff_d3d12va_common_frame_params
int ff_d3d12va_common_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
d3d12va common frame params
Definition: d3d12va_decode.c:378
decode.h
AVD3D12VASyncContext
This struct is used to sync d3d12 execution.
Definition: hwcontext_d3d12va.h:84
AVD3D12VASyncContext::fence
ID3D12Fence * fence
D3D12 fence object.
Definition: hwcontext_d3d12va.h:88
ff_decode_get_hw_frames_ctx
int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx, enum AVHWDeviceType dev_type)
Make sure avctx.hw_frames_ctx is set.
Definition: decode.c:1043
dxva2_internal.h
if
if(ret)
Definition: filter_design.txt:179
NULL
#define NULL
Definition: coverity.c:32
AVHWFramesContext::sw_format
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition: hwcontext.h:211
AVERROR_PATCHWELCOME
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition: error.h:64
av_buffer_unref
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
AVCodecContext::bit_rate
int64_t bit_rate
the average bitrate
Definition: avcodec.h:481
ff_d3d12va_common_end_frame
int ff_d3d12va_common_end_frame(AVCodecContext *avctx, AVFrame *frame, const void *pp, unsigned pp_size, const void *qm, unsigned qm_size, int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *))
d3d12va common end frame
Definition: d3d12va_decode.c:543
time.h
AVD3D12VAFramesContext
This struct is allocated as AVHWFramesContext.hwctx.
Definition: hwcontext_d3d12va.h:126
AV_PIX_FMT_D3D12
@ AV_PIX_FMT_D3D12
Hardware surfaces for Direct3D 12.
Definition: pixfmt.h:440
hwcontext_d3d12va.h
D3D12VA_DECODE_CONTEXT
#define D3D12VA_DECODE_CONTEXT(avctx)
Definition: d3d12va_decode.h:141
av_buffer_create
AVBufferRef * av_buffer_create(uint8_t *data, size_t size, void(*free)(void *opaque, uint8_t *data), void *opaque, int flags)
Create an AVBuffer from an existing array.
Definition: buffer.c:55
HelperObjects::buffer
ID3D12Resource * buffer
Definition: d3d12va_decode.c:40
ReferenceFrame::output_resource
ID3D12Resource * output_resource
Definition: d3d12va_decode.c:47
AVD3D12VADeviceContext::video_device
ID3D12VideoDevice * video_device
If unset, this will be set from the device field on init.
Definition: hwcontext_d3d12va.h:62
f
f
Definition: af_crystalizer.c:122
AV_HWDEVICE_TYPE_D3D12VA
@ AV_HWDEVICE_TYPE_D3D12VA
Definition: hwcontext.h:40
D3D12VA_FRAMES_CONTEXT
#define D3D12VA_FRAMES_CONTEXT(avctx)
Definition: d3d12va_decode.h:142
d3d12va_create_decoder_heap
static int d3d12va_create_decoder_heap(AVCodecContext *avctx)
Definition: d3d12va_decode.c:286
HelperObjects::command_allocator
ID3D12CommandAllocator * command_allocator
Definition: d3d12va_decode.c:39
ff_d3d12va_get_suitable_max_bitstream_size
int ff_d3d12va_get_suitable_max_bitstream_size(AVCodecContext *avctx)
Get a suitable maximum bitstream size.
Definition: d3d12va_decode.c:139
av_image_get_buffer_size
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align)
Return the size in bytes of the amount of data required to store an image with the given parameters.
Definition: imgutils.c:466
AVD3D12VAFrame
D3D12VA frame descriptor for pool allocation.
Definition: hwcontext_d3d12va.h:106
AVD3D12VADeviceContext
This struct is allocated as AVHWDeviceContext.hwctx.
Definition: hwcontext_d3d12va.h:43
log.h
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
ff_d3d12va_decode_init
av_cold int ff_d3d12va_decode_init(AVCodecContext *avctx)
init D3D12VADecodeContext
Definition: d3d12va_decode.c:390
bufref_wrap_interface
static AVBufferRef * bufref_wrap_interface(IUnknown *iface)
Definition: d3d12va_decode.c:270
common.h
AVCodecContext::height
int height
Definition: avcodec.h:592
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
avcodec.h
AVD3D12VAFramesContext::format
DXGI_FORMAT format
DXGI_FORMAT format.
Definition: hwcontext_d3d12va.h:131
d3d12va_discard_helper_objects
static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator, ID3D12Resource *pBuffer, uint64_t fence_value)
Definition: d3d12va_decode.c:232
AVHWFramesContext
This struct describes a set or pool of "hardware" frames (i.e.
Definition: hwcontext.h:116
ret
ret
Definition: filter_design.txt:187
AV_PIX_FMT_NV12
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition: pixfmt.h:96
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
AVHWFramesContext::device_ctx
AVHWDeviceContext * device_ctx
The parent AVHWDeviceContext.
Definition: hwcontext.h:135
AVHWFramesContext::hwctx
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition: hwcontext.h:151
av_fifo_alloc2
AVFifo * av_fifo_alloc2(size_t nb_elems, size_t elem_size, unsigned int flags)
Allocate and initialize an AVFifo with a given element size.
Definition: fifo.c:47
AVCodecContext
main external API structure.
Definition: avcodec.h:431
ReferenceFrame::used
int used
Definition: d3d12va_decode.c:46
AVD3D12VASyncContext::event
HANDLE event
A handle to the event object that's raised when the fence reaches a certain value.
Definition: hwcontext_d3d12va.h:94
buffer
the frame and frame reference mechanism is intended to as much as expensive copies of that data while still allowing the filters to produce correct results The data is stored in buffers represented by AVFrame structures Several references can point to the same frame buffer
Definition: filter_design.txt:49
AVRational::den
int den
Denominator.
Definition: rational.h:60
d3d12va_update_reference_frames_state
static int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers, ID3D12Resource *current_resource, int state_before, int state_end)
Definition: d3d12va_decode.c:519
AV_PIX_FMT_P010
#define AV_PIX_FMT_P010
Definition: pixfmt.h:585
AVCodecContext::coded_width
int coded_width
Bitstream width / height, may be different from width/height e.g.
Definition: avcodec.h:607
desc
const char * desc
Definition: libsvtav1.c:79
mem.h
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
prepare_reference_only_resources
static void prepare_reference_only_resources(AVCodecContext *avctx)
Definition: d3d12va_decode.c:119
D3D12VADecodeContext
This structure is used to provide the necessary configurations and data to the FFmpeg Direct3D 12 HWA...
Definition: d3d12va_decode.h:37
ff_d3d12va_decode_uninit
av_cold int ff_d3d12va_decode_uninit(AVCodecContext *avctx)
uninit D3D12VADecodeContext
Definition: d3d12va_decode.c:480
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AVCodecContext::width
int width
picture width / height.
Definition: avcodec.h:592
imgutils.h
DX_CHECK
#define DX_CHECK(hr)
A check macro used by D3D12 functions highly frequently.
Definition: hwcontext_d3d12va_internal.h:40
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
av_fifo_freep2
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition: fifo.c:286
update_input_arguments
static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer)
Definition: d3d12va_av1.c:113
AVCodecContext::sw_pix_fmt
enum AVPixelFormat sw_pix_fmt
Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:638
AVD3D12VASyncContext::fence_value
uint64_t fence_value
The fence value used for sync.
Definition: hwcontext_d3d12va.h:99
hwcontext_d3d12va_internal.h
AVHWAccel::pix_fmt
enum AVPixelFormat pix_fmt
Supported pixel format.
Definition: avcodec.h:1963
d3d12va_decode.h
AV_FIFO_FLAG_AUTO_GROW
#define AV_FIFO_FLAG_AUTO_GROW
Automatically resize the FIFO on writes, so that the data fits.
Definition: fifo.h:63
d3d12va_create_decoder
static int d3d12va_create_decoder(AVCodecContext *avctx)
Definition: d3d12va_decode.c:318
get_reference_only_resource
static ID3D12Resource * get_reference_only_resource(AVCodecContext *avctx, ID3D12Resource *output_resource)
Definition: d3d12va_decode.c:50
HelperObjects::fence_value
uint64_t fence_value
Definition: d3d12va_decode.c:41