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  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
54  int i = 0;
55  ID3D12Resource *resource = NULL;
56  D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
57  D3D12_RESOURCE_DESC desc;
58  ReferenceFrame *reference_only_map = ctx->reference_only_map;
59  if (reference_only_map == NULL) {
60  av_log(avctx, AV_LOG_ERROR, "Reference frames are not allocated!\n");
61  return NULL;
62  }
63 
64  // find unused resource
65  for (i = 0; i < ctx->max_num_ref; i++) {
66  if (!reference_only_map[i].used && reference_only_map[i].resource != NULL) {
67  reference_only_map[i].used = 1;
68  resource = reference_only_map[i].resource;
69  reference_only_map[i].output_resource = output_resource;
70  return resource;
71  }
72  }
73 
74  // find space to allocate
75  for (i = 0; i < ctx->max_num_ref; i++) {
76  if (reference_only_map[i].resource == NULL)
77  break;
78  }
79 
80  if (i == ctx->max_num_ref) {
81  av_log(avctx, AV_LOG_ERROR, "No space for new Reference frame!\n");
82  return NULL;
83  }
84 
85  // allocate frame
86  output_resource->lpVtbl->GetDesc(output_resource, &desc);
87  desc.Flags = D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
88 
89  if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc,
90  D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&reference_only_map[i].resource))) {
91  av_log(ctx, AV_LOG_ERROR, "Failed to create D3D12 Reference Resource!\n");
92  return NULL;
93  }
94 
95  reference_only_map[i].used = 1;
96  resource = reference_only_map[i].resource;
97  reference_only_map[i].output_resource = output_resource;
98 
99  return resource;
100 }
101 
103 {
105  int i;
106  ReferenceFrame *reference_only_map = ctx->reference_only_map;
107  if (reference_only_map != NULL) {
108  for (i = 0; i < ctx->max_num_ref; i++) {
109  if (reference_only_map[i].resource != NULL) {
110  D3D12_OBJECT_RELEASE(reference_only_map[i].resource);
111  }
112  }
113  av_freep(&ctx->reference_only_map);
114  av_freep(&ctx->ref_only_resources);
115  }
116 }
117 
119 {
121  int i, j;
122  ReferenceFrame *reference_only_map = ctx->reference_only_map;
123  if (reference_only_map == NULL)
124  return;
125  memset(ctx->ref_only_resources, 0, ctx->max_num_ref * sizeof(*(ctx->ref_only_resources)));
126  for (j = 0; j < ctx->max_num_ref; j++) {
127  for (i = 0; i < ctx->max_num_ref; i++) {
128  if (reference_only_map[j].used && reference_only_map[j].output_resource == ctx->ref_resources[i]) {
129  ctx->ref_only_resources[i] = reference_only_map[j].resource;
130  break;
131  }
132  }
133  if (i == ctx->max_num_ref)
134  reference_only_map[j].used = 0;
135  }
136 }
137 
139 {
140  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
141  return av_image_get_buffer_size(frames_ctx->sw_format, avctx->coded_width, avctx->coded_height, 1);
142 }
143 
146  int curr)
147 {
148  AVD3D12VAFrame *f;
149  ID3D12Resource *res;
150  unsigned i;
151 
152  f = (AVD3D12VAFrame *)frame->data[0];
153  if (!f)
154  goto fail;
155 
156  res = f->texture;
157  if (!res)
158  goto fail;
159 
160  for (i = 0; i < ctx->max_num_ref; i++) {
161  if (ctx->ref_resources[i] && res == ctx->ref_resources[i]) {
162  ctx->used_mask |= 1 << i;
163  return i;
164  }
165  }
166 
167  if (curr) {
168  for (i = 0; i < ctx->max_num_ref; i++) {
169  if (!((ctx->used_mask >> i) & 0x1)) {
170  ctx->ref_resources[i] = res;
171  return i;
172  }
173  }
174  }
175 
176 fail:
177  av_log((AVCodecContext *)avctx, AV_LOG_WARNING, "Could not get surface index. Using 0 instead.\n");
178  return 0;
179 }
180 
181 static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator,
182  ID3D12Resource **ppBuffer)
183 {
184  HRESULT hr;
186  HelperObjects obj = { 0 };
187  D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD };
188 
189  D3D12_RESOURCE_DESC desc = {
190  .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
191  .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
192  .Width = ctx->bitstream_size,
193  .Height = 1,
194  .DepthOrArraySize = 1,
195  .MipLevels = 1,
196  .Format = DXGI_FORMAT_UNKNOWN,
197  .SampleDesc = { .Count = 1, .Quality = 0 },
198  .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
199  .Flags = D3D12_RESOURCE_FLAG_NONE,
200  };
201 
202  if (av_fifo_peek(ctx->objects_queue, &obj, 1, 0) >= 0) {
203  uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence);
204  if (completion >= obj.fence_value) {
205  *ppAllocator = obj.command_allocator;
206  *ppBuffer = obj.buffer;
207  av_fifo_read(ctx->objects_queue, &obj, 1);
208  return 0;
209  }
210  }
211 
212  hr = ID3D12Device_CreateCommandAllocator(ctx->device_ctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
213  &IID_ID3D12CommandAllocator, (void **)ppAllocator);
214  if (FAILED(hr)) {
215  av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n");
216  return AVERROR(EINVAL);
217  }
218 
219  hr = ID3D12Device_CreateCommittedResource(ctx->device_ctx->device, &heap_props, D3D12_HEAP_FLAG_NONE,
220  &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
221  &IID_ID3D12Resource, (void **)ppBuffer);
222 
223  if (FAILED(hr)) {
224  av_log(avctx, AV_LOG_ERROR, "Failed to create a new d3d12 buffer!\n");
225  return AVERROR(EINVAL);
226  }
227 
228  return 0;
229 }
230 
231 static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator,
232  ID3D12Resource *pBuffer, uint64_t fence_value)
233 {
235 
236  HelperObjects obj = {
237  .command_allocator = pAllocator,
238  .buffer = pBuffer,
239  .fence_value = fence_value,
240  };
241 
242  if (av_fifo_write(ctx->objects_queue, &obj, 1) < 0) {
243  D3D12_OBJECT_RELEASE(pAllocator);
244  D3D12_OBJECT_RELEASE(pBuffer);
245  return AVERROR(ENOMEM);
246  }
247 
248  return 0;
249 }
250 
252 {
253  uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence);
254  if (completion < psync_ctx->fence_value) {
255  if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event)))
256  return AVERROR(EINVAL);
257 
258  WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE);
259  }
260 
261  return 0;
262 }
263 
264 static void bufref_free_interface(void *opaque, uint8_t *data)
265 {
266  D3D12_OBJECT_RELEASE(opaque);
267 }
268 
269 static AVBufferRef *bufref_wrap_interface(IUnknown *iface)
270 {
271  return av_buffer_create((uint8_t*)iface, 1, bufref_free_interface, iface, 0);
272 }
273 
275 {
277 
278  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
279  return d3d12va_fence_completion(&ctx->sync_ctx);
280 
281 fail:
282  return AVERROR(EINVAL);
283 }
284 
286 {
288  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
289  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
290  AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
291 
292  D3D12_VIDEO_DECODER_HEAP_DESC desc = {
293  .NodeMask = 0,
294  .Configuration = ctx->cfg,
295  .DecodeWidth = frames_ctx->width,
296  .DecodeHeight = frames_ctx->height,
297  .Format = frames_hwctx->format,
298  .FrameRate = { avctx->framerate.num, avctx->framerate.den },
299  .BitRate = avctx->bit_rate,
300  .MaxDecodePictureBufferCount = ctx->max_num_ref,
301  };
302 
303  DX_CHECK(ID3D12VideoDevice_CreateVideoDecoderHeap(device_hwctx->video_device, &desc,
304  &IID_ID3D12VideoDecoderHeap, (void **)&ctx->decoder_heap));
305 
306  return 0;
307 
308 fail:
309  if (ctx->decoder) {
310  av_log(avctx, AV_LOG_ERROR, "D3D12 doesn't support decoding frames with an extent "
311  "[width(%d), height(%d)], on your device!\n", frames_ctx->width, frames_ctx->height);
312  }
313 
314  return AVERROR(EINVAL);
315 }
316 
318 {
319  D3D12_VIDEO_DECODER_DESC desc;
321  AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
322  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
323  AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
324 
325  D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT feature = {
326  .NodeIndex = 0,
327  .Configuration = ctx->cfg,
328  .Width = frames_ctx->width,
329  .Height = frames_ctx->height,
330  .DecodeFormat = frames_hwctx->format,
331  .FrameRate = { avctx->framerate.num, avctx->framerate.den },
332  .BitRate = avctx->bit_rate,
333  };
334 
335  DX_CHECK(ID3D12VideoDevice_CheckFeatureSupport(device_hwctx->video_device, D3D12_FEATURE_VIDEO_DECODE_SUPPORT,
336  &feature, sizeof(feature)));
337  if (!(feature.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)) {
338  av_log(avctx, AV_LOG_ERROR, "D3D12 video decode is not supported on this device.\n");
339  return AVERROR(ENOSYS);
340  }
341  if (!(feature.DecodeTier >= D3D12_VIDEO_DECODE_TIER_2)) {
342  av_log(avctx, AV_LOG_ERROR, "D3D12 video decode on this device requires tier %d support, "
343  "but it is not implemented.\n", feature.DecodeTier);
344  return AVERROR_PATCHWELCOME;
345  }
346 
347  ctx->reference_only_map = NULL;
348  ctx->ref_only_resources = NULL;
349  if (feature.ConfigurationFlags & D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_REFERENCE_ONLY_ALLOCATIONS_REQUIRED) {
350  av_log(avctx, AV_LOG_VERBOSE, "Reference-Only Allocations are required for this D3D12 decoder configuration.\n");
351  ctx->reference_only_map = av_calloc(ctx->max_num_ref + 1, sizeof(ReferenceFrame));
352  if (!ctx->reference_only_map)
353  return AVERROR(ENOMEM);
354  ctx->ref_only_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_only_resources));
355  if (!ctx->ref_only_resources)
356  return AVERROR(ENOMEM);
357  }
358 
359  desc = (D3D12_VIDEO_DECODER_DESC) {
360  .NodeMask = 0,
361  .Configuration = ctx->cfg,
362  };
363 
364  DX_CHECK(ID3D12VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc, &IID_ID3D12VideoDecoder,
365  (void **)&ctx->decoder));
366 
367  ctx->decoder_ref = bufref_wrap_interface((IUnknown *)ctx->decoder);
368  if (!ctx->decoder_ref)
369  return AVERROR(ENOMEM);
370 
371  return 0;
372 
373 fail:
374  return AVERROR(EINVAL);
375 }
376 
378 {
379  AVHWFramesContext *frames_ctx = (AVHWFramesContext *)hw_frames_ctx->data;
380 
381  frames_ctx->format = AV_PIX_FMT_D3D12;
383  frames_ctx->width = avctx->width;
384  frames_ctx->height = avctx->height;
385 
386  return 0;
387 }
388 
390 {
391  int ret;
392  AVHWFramesContext *frames_ctx;
394  ID3D12Resource *buffer = NULL;
395  ID3D12CommandAllocator *command_allocator = NULL;
396  D3D12_COMMAND_QUEUE_DESC queue_desc = {
397  .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
398  .Priority = 0,
399  .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
400  .NodeMask = 0,
401  };
402 
403  ctx->pix_fmt = avctx->hwaccel->pix_fmt;
404 
406  if (ret < 0)
407  return ret;
408 
409  frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
410  ctx->device_ctx = (AVD3D12VADeviceContext *)frames_ctx->device_ctx->hwctx;
411 
412  if (frames_ctx->format != ctx->pix_fmt) {
413  av_log(avctx, AV_LOG_ERROR, "Invalid pixfmt for hwaccel!\n");
414  goto fail;
415  }
416 
417  ret = d3d12va_create_decoder(avctx);
418  if (ret < 0)
419  goto fail;
420 
422  if (ret < 0)
423  goto fail;
424 
425  ctx->bitstream_size = ff_d3d12va_get_suitable_max_bitstream_size(avctx);
426 
427  ctx->ref_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_resources));
428  if (!ctx->ref_resources)
429  return AVERROR(ENOMEM);
430 
431  ctx->ref_subresources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_subresources));
432  if (!ctx->ref_subresources)
433  return AVERROR(ENOMEM);
434 
437  if (!ctx->objects_queue)
438  return AVERROR(ENOMEM);
439 
440  DX_CHECK(ID3D12Device_CreateFence(ctx->device_ctx->device, 0, D3D12_FENCE_FLAG_NONE,
441  &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence));
442 
443  ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
444  if (!ctx->sync_ctx.event)
445  goto fail;
446 
447  ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer);
448  if (ret < 0)
449  goto fail;
450 
451  DX_CHECK(ID3D12Device_CreateCommandQueue(ctx->device_ctx->device, &queue_desc,
452  &IID_ID3D12CommandQueue, (void **)&ctx->command_queue));
453 
454  DX_CHECK(ID3D12Device_CreateCommandList(ctx->device_ctx->device, 0, queue_desc.Type,
455  command_allocator, NULL, &IID_ID3D12CommandList, (void **)&ctx->command_list));
456 
457  DX_CHECK(ID3D12VideoDecodeCommandList_Close(ctx->command_list));
458 
459  ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
460 
461  ret = d3d12va_sync_with_gpu(avctx);
462  if (ret < 0)
463  goto fail;
464 
465  d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
466  if (ret < 0)
467  goto fail;
468 
469  return 0;
470 
471 fail:
472  D3D12_OBJECT_RELEASE(command_allocator);
475 
476  return AVERROR(EINVAL);
477 }
478 
480 {
481  int num_allocator = 0;
483  HelperObjects obj;
484 
485  if (ctx->sync_ctx.fence)
486  d3d12va_sync_with_gpu(avctx);
487 
488  av_freep(&ctx->ref_resources);
489  av_freep(&ctx->ref_subresources);
490 
491  D3D12_OBJECT_RELEASE(ctx->command_list);
492  D3D12_OBJECT_RELEASE(ctx->command_queue);
493 
494  if (ctx->objects_queue) {
495  while (av_fifo_read(ctx->objects_queue, &obj, 1) >= 0) {
496  num_allocator++;
499  }
500 
501  av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator);
502  }
504 
505  av_fifo_freep2(&ctx->objects_queue);
506 
507  D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence);
508  if (ctx->sync_ctx.event)
509  CloseHandle(ctx->sync_ctx.event);
510 
511  D3D12_OBJECT_RELEASE(ctx->decoder_heap);
512 
513  av_buffer_unref(&ctx->decoder_ref);
514 
515  return 0;
516 }
517 
518 static inline int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers,
519  ID3D12Resource *current_resource, int state_before, int state_end)
520 {
522  ID3D12Resource **ref_resources = ctx->ref_only_resources ? ctx->ref_only_resources : ctx->ref_resources;
523 
524  int num_barrier = 0;
525  for (int i = 0; i < ctx->max_num_ref; i++) {
526  if (((ctx->used_mask >> i) & 0x1) && ref_resources[i] && ref_resources[i] != current_resource) {
527  barriers[num_barrier].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
528  barriers[num_barrier].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
529  barriers[num_barrier].Transition = (D3D12_RESOURCE_TRANSITION_BARRIER) {
530  .pResource = ref_resources[i],
531  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
532  .StateBefore = state_before,
533  .StateAfter = state_end,
534  };
535  num_barrier++;
536  }
537  }
538 
539  return num_barrier;
540 }
541 
543  const void *pp, unsigned pp_size,
544  const void *qm, unsigned qm_size,
545  int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *))
546 {
547  int ret;
549  ID3D12Resource *buffer = NULL;
550  ID3D12CommandAllocator *command_allocator = NULL;
551  AVD3D12VAFrame *f = (AVD3D12VAFrame*)frame->data[0];
552  ID3D12Resource *output_resource = (ID3D12Resource*)f->texture;
553  ID3D12Resource *ref_resource = NULL;
554 
555  ID3D12VideoDecodeCommandList *cmd_list = ctx->command_list;
556  D3D12_RESOURCE_BARRIER barriers[32] = { 0 };
557 
558  D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS input_args = {
559  .NumFrameArguments = 2,
560  .FrameArguments = {
561  [0] = {
562  .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_PICTURE_PARAMETERS,
563  .Size = pp_size,
564  .pData = (void *)pp,
565  },
566  [1] = {
567  .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_INVERSE_QUANTIZATION_MATRIX,
568  .Size = qm_size,
569  .pData = (void *)qm,
570  },
571  },
572  .pHeap = ctx->decoder_heap,
573  };
574 
575  D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS output_args = {
576  .ConversionArguments = { 0 },
577  .OutputSubresource = 0,
578  .pOutputTexture2D = output_resource,
579  };
580 
581  memset(ctx->ref_subresources, 0, sizeof(UINT) * ctx->max_num_ref);
582  input_args.ReferenceFrames.NumTexture2Ds = ctx->max_num_ref;
583  input_args.ReferenceFrames.pSubresources = ctx->ref_subresources;
584 
585  if (ctx->reference_only_map) {
586  ref_resource = get_reference_only_resource(avctx, output_resource);
587  if (ref_resource == NULL) {
588  av_log(avctx, AV_LOG_ERROR, "Failed to get reference frame!\n");
589  goto fail;
590  }
592 
593  output_args.ConversionArguments.Enable = 1;
594  input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_only_resources;
595  output_args.ConversionArguments.pReferenceTexture2D = ref_resource;
596  output_args.ConversionArguments.ReferenceSubresource = 0;
597  } else {
598  ref_resource = output_resource;
599  input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_resources;
600  }
601 
602  UINT num_barrier = 1;
603  barriers[0] = (D3D12_RESOURCE_BARRIER) {
604  .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
605  .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
606  .Transition = {
607  .pResource = output_resource,
608  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
609  .StateBefore = D3D12_RESOURCE_STATE_COMMON,
610  .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
611  },
612  };
613 
614  if (ctx->reference_only_map) {
615  barriers[1] = (D3D12_RESOURCE_BARRIER) {
616  .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
617  .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
618  .Transition = {
619  .pResource = ref_resource,
620  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
621  .StateBefore = D3D12_RESOURCE_STATE_COMMON,
622  .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
623  },
624  };
625  num_barrier++;
626  }
627 
628  ret = d3d12va_fence_completion(&f->sync_ctx);
629  if (ret < 0)
630  goto fail;
631 
632  if (!qm)
633  input_args.NumFrameArguments = 1;
634 
635  ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer);
636  if (ret < 0)
637  goto fail;
638 
639  ret = update_input_arguments(avctx, &input_args, buffer);
640  if (ret < 0)
641  goto fail;
642 
643  DX_CHECK(ID3D12CommandAllocator_Reset(command_allocator));
644 
645  DX_CHECK(ID3D12VideoDecodeCommandList_Reset(cmd_list, command_allocator));
646 
647  num_barrier += d3d12va_update_reference_frames_state(avctx, &barriers[num_barrier], ref_resource, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ);
648 
649  ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
650 
651  ID3D12VideoDecodeCommandList_DecodeFrame(cmd_list, ctx->decoder, &output_args, &input_args);
652 
653  for (int i = 0; i < num_barrier; i++)
654  FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
655 
656  ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
657 
658  DX_CHECK(ID3D12VideoDecodeCommandList_Close(cmd_list));
659 
660  ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
661 
662  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, f->sync_ctx.fence, ++f->sync_ctx.fence_value));
663 
664  DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
665 
666  ret = d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
667  if (ret < 0)
668  return ret;
669 
670  return 0;
671 
672 fail:
673  if (command_allocator)
674  d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
675  return AVERROR(EINVAL);
676 }
AVHWDeviceContext::hwctx
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition: hwcontext.h:88
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:216
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:200
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:421
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:264
data
const char data[16]
Definition: mxf.c:149
AV_PIX_FMT_YUV420P10
#define AV_PIX_FMT_YUV420P10
Definition: pixfmt.h:539
AV_LOG_VERBOSE
#define AV_LOG_VERBOSE
Detailed information.
Definition: log.h:226
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:144
AVHWFramesContext::width
int width
The allocated dimensions of the frames in this pool.
Definition: hwcontext.h:220
AVCodecContext::framerate
AVRational framerate
Definition: avcodec.h:551
fail
#define fail()
Definition: checkasm.h:199
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:181
free_reference_only_resources
static void free_reference_only_resources(AVCodecContext *avctx)
Definition: d3d12va_decode.c:102
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
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:274
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:251
AVHWFramesContext::height
int height
Definition: hwcontext.h:220
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:377
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:1040
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:213
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:542
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:285
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:138
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:389
bufref_wrap_interface
static AVBufferRef * bufref_wrap_interface(IUnknown *iface)
Definition: d3d12va_decode.c:269
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:231
AVHWFramesContext
This struct describes a set or pool of "hardware" frames (i.e.
Definition: hwcontext.h:118
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:265
AVHWFramesContext::device_ctx
AVHWDeviceContext * device_ctx
The parent AVHWDeviceContext.
Definition: hwcontext.h:137
AVHWFramesContext::hwctx
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition: hwcontext.h:153
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:518
AV_PIX_FMT_P010
#define AV_PIX_FMT_P010
Definition: pixfmt.h:602
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:118
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:479
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:317
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