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