FFmpeg
Loading...
Searching...
No Matches
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"
32#include "avcodec.h"
33#include "decode.h"
34#include "d3d12va_decode.h"
35#include "dxva2_internal.h"
36
37typedef struct HelperObjects {
38 ID3D12CommandAllocator *command_allocator;
39 ID3D12Resource *buffer;
40 uint64_t fence_value;
42
43typedef struct ReferenceFrame {
44 ID3D12Resource *resource;
45 int used;
46 ID3D12Resource *output_resource;
48
49static ID3D12Resource *get_reference_only_resource(AVCodecContext *avctx, ID3D12Resource *output_resource)
50{
52 AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
53 int i = 0;
54 ID3D12Resource *resource = NULL;
55 D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
56 D3D12_RESOURCE_DESC desc;
57 ReferenceFrame *reference_only_map = ctx->reference_only_map;
58 if (reference_only_map == NULL) {
59 av_log(avctx, AV_LOG_ERROR, "Reference frames are not allocated!\n");
60 return NULL;
61 }
62
63 // Reuse the slot already mapped to this output resource. Output surfaces are
64 // recycled by the frame pool, so without this the same output_resource would
65 // be assigned a new slot every time it is reused, leaking slots until the
66 // pool is exhausted.
67 for (i = 0; i < ctx->max_num_ref + 1; i++) {
68 if (reference_only_map[i].resource != NULL &&
69 reference_only_map[i].output_resource == output_resource) {
70 reference_only_map[i].used = 1;
71 return reference_only_map[i].resource;
72 }
73 }
74
75 // Find an unused resource.
76 for (i = 0; i < ctx->max_num_ref + 1; i++) {
77 if (!reference_only_map[i].used && reference_only_map[i].resource != NULL) {
78 reference_only_map[i].used = 1;
79 resource = reference_only_map[i].resource;
80 reference_only_map[i].output_resource = output_resource;
81 return resource;
82 }
83 }
84
85 // Find space to allocate.
86 for (i = 0; i < ctx->max_num_ref + 1; i++) {
87 if (reference_only_map[i].resource == NULL)
88 break;
89 }
90
91 if (i == ctx->max_num_ref + 1) {
92 av_log(avctx, AV_LOG_ERROR, "No space for new Reference frame!\n");
93 return NULL;
94 }
95
96 // allocate frame
97 output_resource->lpVtbl->GetDesc(output_resource, &desc);
98 desc.Flags = D3D12_RESOURCE_FLAG_VIDEO_DECODE_REFERENCE_ONLY | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE;
99
100 if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc,
101 D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&reference_only_map[i].resource))) {
102 av_log(ctx, AV_LOG_ERROR, "Failed to create D3D12 Reference Resource!\n");
103 return NULL;
104 }
105
106 reference_only_map[i].used = 1;
107 resource = reference_only_map[i].resource;
108 reference_only_map[i].output_resource = output_resource;
109
110 return resource;
111}
112
114{
116 int i;
117 ReferenceFrame *reference_only_map = ctx->reference_only_map;
118 if (reference_only_map != NULL) {
119 for (i = 0; i < ctx->max_num_ref + 1; i++) {
120 if (reference_only_map[i].resource != NULL) {
121 D3D12_OBJECT_RELEASE(reference_only_map[i].resource);
122 }
123 }
124 av_freep(&ctx->reference_only_map);
125 av_freep(&ctx->ref_only_resources);
126 }
127}
128
130{
132 int i, j;
133 ReferenceFrame *reference_only_map = ctx->reference_only_map;
134 if (reference_only_map == NULL)
135 return;
136 memset(ctx->ref_only_resources, 0, ctx->max_num_ref * sizeof(*(ctx->ref_only_resources)));
137 for (j = 0; j < ctx->max_num_ref + 1; j++) {
138 for (i = 0; i < ctx->max_num_ref; i++) {
139 if (reference_only_map[j].used && reference_only_map[j].output_resource == ctx->ref_resources[i]) {
140 ctx->ref_only_resources[i] = reference_only_map[j].resource;
141 break;
142 }
143 }
144 if (i == ctx->max_num_ref)
145 reference_only_map[j].used = 0;
146 }
147}
148
151 int curr)
152{
154 ID3D12Resource *res;
155 unsigned i;
156
157 f = (AVD3D12VAFrame *)frame->data[0];
158 if (!f)
159 goto fail;
160
161 res = f->texture;
162 if (!res)
163 goto fail;
164
165 for (i = 0; i < ctx->max_num_ref; i++) {
166 if (ctx->ref_resources[i] && res == ctx->ref_resources[i]) {
167 ctx->used_mask |= 1 << i;
168 return i;
169 }
170 }
171
172 if (curr) {
173 for (i = 0; i < ctx->max_num_ref; i++) {
174 if (!((ctx->used_mask >> i) & 0x1)) {
175 ctx->ref_resources[i] = res;
176 return i;
177 }
178 }
179 }
180
181fail:
182 av_log((AVCodecContext *)avctx, AV_LOG_WARNING, "Could not get surface index. Using 0 instead.\n");
183 return 0;
184}
185
186static int d3d12va_resize_bitstream_buffer(AVCodecContext *avctx, ID3D12Resource **ppBuffer,
187 UINT64 bitstream_size)
188{
189 HRESULT hr;
191 D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD };
192 ID3D12Resource *buffer = NULL;
193 D3D12_RESOURCE_DESC desc;
194
195 if (!bitstream_size)
196 return 0;
197
198 if (*ppBuffer) {
199 (*ppBuffer)->lpVtbl->GetDesc(*ppBuffer, &desc);
200 if (desc.Width >= bitstream_size)
201 return 0;
202 } else {
203 desc = (D3D12_RESOURCE_DESC) {
204 .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
205 .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
206 .Height = 1,
207 .DepthOrArraySize = 1,
208 .MipLevels = 1,
209 .Format = DXGI_FORMAT_UNKNOWN,
210 .SampleDesc = { .Count = 1, .Quality = 0 },
211 .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
212 .Flags = D3D12_RESOURCE_FLAG_NONE,
213 };
214 }
215 desc.Width = bitstream_size * 1.5;
216
217 hr = ID3D12Device_CreateCommittedResource(ctx->device_ctx->device, &heap_props, D3D12_HEAP_FLAG_NONE,
218 &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
219 &IID_ID3D12Resource, (void **)&buffer);
220 if (FAILED(hr)) {
221 av_log(avctx, AV_LOG_ERROR, "Failed to create a new D3D12 bitstream buffer for uploading!\n");
222 return AVERROR(EINVAL);
223 }
224
225 D3D12_OBJECT_RELEASE(*ppBuffer);
226 *ppBuffer = buffer;
227
228 return 0;
229}
230
231static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator,
232 ID3D12Resource **ppBuffer, UINT64 bitstream_size)
233{
234 HRESULT hr;
236 HelperObjects obj = { 0 };
237
238 if (av_fifo_peek(ctx->objects_queue, &obj, 1, 0) >= 0) {
239 uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence);
240 if (completion >= obj.fence_value) {
241 *ppAllocator = obj.command_allocator;
242 *ppBuffer = obj.buffer;
243 av_fifo_read(ctx->objects_queue, &obj, 1);
244 return d3d12va_resize_bitstream_buffer(avctx, ppBuffer, bitstream_size);
245 }
246 }
247
248 hr = ID3D12Device_CreateCommandAllocator(ctx->device_ctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
249 &IID_ID3D12CommandAllocator, (void **)ppAllocator);
250 if (FAILED(hr)) {
251 av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n");
252 return AVERROR(EINVAL);
253 }
254
255 return d3d12va_resize_bitstream_buffer(avctx, ppBuffer, bitstream_size);
256}
257
258static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator,
259 ID3D12Resource *pBuffer, uint64_t fence_value)
260{
262
263 HelperObjects obj = {
264 .command_allocator = pAllocator,
265 .buffer = pBuffer,
266 .fence_value = fence_value,
267 };
268
269 if (av_fifo_write(ctx->objects_queue, &obj, 1) < 0) {
270 D3D12_OBJECT_RELEASE(pAllocator);
271 D3D12_OBJECT_RELEASE(pBuffer);
272 return AVERROR(ENOMEM);
273 }
274
275 return 0;
276}
277
279{
280 uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence);
281 if (completion < psync_ctx->fence_value) {
282 if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event)))
283 return AVERROR(EINVAL);
284
285 WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE);
286 }
287
288 return 0;
289}
290
291static void bufref_free_interface(void *opaque, uint8_t *data)
292{
293 D3D12_OBJECT_RELEASE(opaque);
294}
295
296static AVBufferRef *bufref_wrap_interface(IUnknown *iface)
297{
298 return av_buffer_create((uint8_t*)iface, 1, bufref_free_interface, iface, 0);
299}
300
302{
304
305 DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
306 return d3d12va_fence_completion(&ctx->sync_ctx);
307
308fail:
309 return AVERROR(EINVAL);
310}
311
313{
315 AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
316 AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
317 AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
318
319 D3D12_VIDEO_DECODER_HEAP_DESC desc = {
320 .NodeMask = 0,
321 .Configuration = ctx->cfg,
322 .DecodeWidth = frames_ctx->width,
323 .DecodeHeight = frames_ctx->height,
324 .Format = frames_hwctx->format,
325 .FrameRate = { avctx->framerate.num, avctx->framerate.den },
326 .BitRate = avctx->bit_rate,
327 .MaxDecodePictureBufferCount = ctx->max_num_ref,
328 };
329
330 DX_CHECK(ID3D12VideoDevice_CreateVideoDecoderHeap(device_hwctx->video_device, &desc,
331 &IID_ID3D12VideoDecoderHeap, (void **)&ctx->decoder_heap));
332
333 return 0;
334
335fail:
336 if (ctx->decoder) {
337 av_log(avctx, AV_LOG_ERROR, "D3D12 doesn't support decoding frames with an extent "
338 "[width(%d), height(%d)], on your device!\n", frames_ctx->width, frames_ctx->height);
339 }
340
341 return AVERROR(EINVAL);
342}
343
345{
346 D3D12_VIDEO_DECODER_DESC desc;
348 AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
349 AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx;
350 AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx;
351
352 D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT feature = {
353 .NodeIndex = 0,
354 .Configuration = ctx->cfg,
355 .Width = frames_ctx->width,
356 .Height = frames_ctx->height,
357 .DecodeFormat = frames_hwctx->format,
358 .FrameRate = { avctx->framerate.num, avctx->framerate.den },
359 .BitRate = avctx->bit_rate,
360 };
361
362 DX_CHECK(ID3D12VideoDevice_CheckFeatureSupport(device_hwctx->video_device, D3D12_FEATURE_VIDEO_DECODE_SUPPORT,
363 &feature, sizeof(feature)));
364 if (!(feature.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)) {
365 av_log(avctx, AV_LOG_ERROR, "D3D12 video decode is not supported on this device.\n");
366 return AVERROR(ENOSYS);
367 }
368 if (!(feature.DecodeTier >= D3D12_VIDEO_DECODE_TIER_2)) {
369 av_log(avctx, AV_LOG_ERROR, "D3D12 video decode on this device requires tier %d support, "
370 "but it is not implemented.\n", feature.DecodeTier);
372 }
373
374 ctx->reference_only_map = NULL;
375 ctx->ref_only_resources = NULL;
376 if (feature.ConfigurationFlags & D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_REFERENCE_ONLY_ALLOCATIONS_REQUIRED) {
377 av_log(avctx, AV_LOG_VERBOSE, "Reference-Only Allocations are required for this D3D12 decoder configuration.\n");
378 ctx->reference_only_map = av_calloc(ctx->max_num_ref + 1, sizeof(ReferenceFrame));
379 if (!ctx->reference_only_map)
380 return AVERROR(ENOMEM);
381 ctx->ref_only_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_only_resources));
382 if (!ctx->ref_only_resources)
383 return AVERROR(ENOMEM);
384 }
385
386 desc = (D3D12_VIDEO_DECODER_DESC) {
387 .NodeMask = 0,
388 .Configuration = ctx->cfg,
389 };
390
391 DX_CHECK(ID3D12VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc, &IID_ID3D12VideoDecoder,
392 (void **)&ctx->decoder));
393
394 ctx->decoder_ref = bufref_wrap_interface((IUnknown *)ctx->decoder);
395 if (!ctx->decoder_ref)
396 return AVERROR(ENOMEM);
397
398 return 0;
399
400fail:
401 return AVERROR(EINVAL);
402}
403
405{
406 AVHWFramesContext *frames_ctx = (AVHWFramesContext *)hw_frames_ctx->data;
407
408 frames_ctx->format = AV_PIX_FMT_D3D12;
409 switch (avctx->sw_pix_fmt) {
410 case AV_PIX_FMT_YUV420P10: frames_ctx->sw_format = AV_PIX_FMT_P010; break;
411 case AV_PIX_FMT_YUV420P12: frames_ctx->sw_format = AV_PIX_FMT_P012; break;
412 default: frames_ctx->sw_format = AV_PIX_FMT_NV12; break;
413 }
414 frames_ctx->width = avctx->coded_width;
415 frames_ctx->height = avctx->coded_height;
416
417 return 0;
418}
419
421{
422 int ret;
423 AVHWFramesContext *frames_ctx;
425 ID3D12Resource *buffer = NULL;
426 ID3D12CommandAllocator *command_allocator = NULL;
427 D3D12_COMMAND_QUEUE_DESC queue_desc = {
428 .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE,
429 .Priority = 0,
430 .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
431 .NodeMask = 0,
432 };
433
434 ctx->pix_fmt = avctx->hwaccel->pix_fmt;
435
437 if (ret < 0)
438 return ret;
439
440 frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx);
441 ctx->device_ctx = (AVD3D12VADeviceContext *)frames_ctx->device_ctx->hwctx;
442
443 if (frames_ctx->format != ctx->pix_fmt) {
444 av_log(avctx, AV_LOG_ERROR, "Invalid pixfmt for hwaccel!\n");
445 goto fail;
446 }
447
448 ret = d3d12va_create_decoder(avctx);
449 if (ret < 0)
450 goto fail;
451
452 ret = d3d12va_create_decoder_heap(avctx);
453 if (ret < 0)
454 goto fail;
455
456 ctx->ref_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_resources));
457 if (!ctx->ref_resources)
458 return AVERROR(ENOMEM);
459
460 ctx->ref_subresources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_subresources));
461 if (!ctx->ref_subresources)
462 return AVERROR(ENOMEM);
463
466 if (!ctx->objects_queue)
467 return AVERROR(ENOMEM);
468
469 DX_CHECK(ID3D12Device_CreateFence(ctx->device_ctx->device, 0, D3D12_FENCE_FLAG_NONE,
470 &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence));
471
472 ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
473 if (!ctx->sync_ctx.event)
474 goto fail;
475
476 ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer, 0);
477 if (ret < 0)
478 goto fail;
479
480 DX_CHECK(ID3D12Device_CreateCommandQueue(ctx->device_ctx->device, &queue_desc,
481 &IID_ID3D12CommandQueue, (void **)&ctx->command_queue));
482
483 DX_CHECK(ID3D12Device_CreateCommandList(ctx->device_ctx->device, 0, queue_desc.Type,
484 command_allocator, NULL, &IID_ID3D12CommandList, (void **)&ctx->command_list));
485
486 DX_CHECK(ID3D12VideoDecodeCommandList_Close(ctx->command_list));
487
488 ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
489
490 ret = d3d12va_sync_with_gpu(avctx);
491 if (ret < 0)
492 goto fail;
493
494 d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
495 if (ret < 0)
496 goto fail;
497
498 return 0;
499
500fail:
501 D3D12_OBJECT_RELEASE(command_allocator);
504
505 return AVERROR(EINVAL);
506}
507
509{
510 int num_allocator = 0;
512 HelperObjects obj;
513
514 if (ctx->sync_ctx.fence)
516
517 av_freep(&ctx->ref_resources);
518 av_freep(&ctx->ref_subresources);
519
520 D3D12_OBJECT_RELEASE(ctx->command_list);
521 D3D12_OBJECT_RELEASE(ctx->command_queue);
522
523 if (ctx->objects_queue) {
524 while (av_fifo_read(ctx->objects_queue, &obj, 1) >= 0) {
525 num_allocator++;
528 }
529
530 av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator);
531 }
533
534 av_fifo_freep2(&ctx->objects_queue);
535
536 D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence);
537 if (ctx->sync_ctx.event)
538 CloseHandle(ctx->sync_ctx.event);
539
540 D3D12_OBJECT_RELEASE(ctx->decoder_heap);
541
542 av_buffer_unref(&ctx->decoder_ref);
543
544 return 0;
545}
546
547static inline int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers,
548 ID3D12Resource *current_resource, int state_before, int state_end)
549{
551 ID3D12Resource **ref_resources = ctx->ref_only_resources ? ctx->ref_only_resources : ctx->ref_resources;
552
553 int num_barrier = 0;
554 for (int i = 0; i < ctx->max_num_ref; i++) {
555 if (((ctx->used_mask >> i) & 0x1) && ref_resources[i] && ref_resources[i] != current_resource) {
556 barriers[num_barrier].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
557 barriers[num_barrier].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
558 barriers[num_barrier].Transition = (D3D12_RESOURCE_TRANSITION_BARRIER) {
559 .pResource = ref_resources[i],
560 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
561 .StateBefore = state_before,
562 .StateAfter = state_end,
563 };
564 num_barrier++;
565 }
566 }
567
568 return num_barrier;
569}
570
572 const void *pp, unsigned pp_size,
573 const void *qm, unsigned qm_size,
574 UINT64 bitstream_size,
575 int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *))
576{
577 int ret;
579 ID3D12Resource *buffer = NULL;
580 ID3D12CommandAllocator *command_allocator = NULL;
581 AVD3D12VAFrame *f = (AVD3D12VAFrame*)frame->data[0];
582 ID3D12Resource *output_resource = (ID3D12Resource*)f->texture;
583 ID3D12Resource *ref_resource = NULL;
584
585 ID3D12VideoDecodeCommandList *cmd_list = ctx->command_list;
586 D3D12_RESOURCE_BARRIER barriers[32] = { 0 };
587
588 D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS input_args = {
589 .NumFrameArguments = 2,
590 .FrameArguments = {
591 [0] = {
592 .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_PICTURE_PARAMETERS,
593 .Size = pp_size,
594 .pData = (void *)pp,
595 },
596 [1] = {
597 .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_INVERSE_QUANTIZATION_MATRIX,
598 .Size = qm_size,
599 .pData = (void *)qm,
600 },
601 },
602 .pHeap = ctx->decoder_heap,
603 };
604
605 D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS output_args = {
606 .ConversionArguments = { 0 },
607 .OutputSubresource = 0,
608 .pOutputTexture2D = output_resource,
609 };
610
611 memset(ctx->ref_subresources, 0, sizeof(UINT) * ctx->max_num_ref);
612 input_args.ReferenceFrames.NumTexture2Ds = ctx->max_num_ref;
613 input_args.ReferenceFrames.pSubresources = ctx->ref_subresources;
614
615 if (ctx->reference_only_map) {
616 ref_resource = get_reference_only_resource(avctx, output_resource);
617 if (ref_resource == NULL) {
618 av_log(avctx, AV_LOG_ERROR, "Failed to get reference frame!\n");
619 goto fail;
620 }
622
623 output_args.ConversionArguments.Enable = 1;
624 input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_only_resources;
625 output_args.ConversionArguments.pReferenceTexture2D = ref_resource;
626 output_args.ConversionArguments.ReferenceSubresource = 0;
627 } else {
628 ref_resource = output_resource;
629 input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_resources;
630 }
631
632 UINT num_barrier = 1;
633 barriers[0] = (D3D12_RESOURCE_BARRIER) {
634 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
635 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
636 .Transition = {
637 .pResource = output_resource,
638 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
639 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
640 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
641 },
642 };
643
644 if (ctx->reference_only_map) {
645 barriers[1] = (D3D12_RESOURCE_BARRIER) {
646 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
647 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
648 .Transition = {
649 .pResource = ref_resource,
650 .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
651 .StateBefore = D3D12_RESOURCE_STATE_COMMON,
652 .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE,
653 },
654 };
655 num_barrier++;
656 }
657
658 ret = d3d12va_fence_completion(&f->sync_ctx);
659 if (ret < 0)
660 goto fail;
661
662 if (!qm)
663 input_args.NumFrameArguments = 1;
664
665 ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer, bitstream_size);
666 if (ret < 0)
667 goto fail;
668
669 ret = update_input_arguments(avctx, &input_args, buffer);
670 if (ret < 0)
671 goto fail;
672
673 DX_CHECK(ID3D12CommandAllocator_Reset(command_allocator));
674
675 DX_CHECK(ID3D12VideoDecodeCommandList_Reset(cmd_list, command_allocator));
676
677 num_barrier += d3d12va_update_reference_frames_state(avctx, &barriers[num_barrier], ref_resource, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ);
678
679 ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
680
681 ID3D12VideoDecodeCommandList_DecodeFrame(cmd_list, ctx->decoder, &output_args, &input_args);
682
683 for (int i = 0; i < num_barrier; i++)
684 FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter);
685
686 ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers);
687
688 DX_CHECK(ID3D12VideoDecodeCommandList_Close(cmd_list));
689
690 ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list);
691
692 DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, f->sync_ctx.fence, ++f->sync_ctx.fence_value));
693
694 DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value));
695
696 ret = d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
697 if (ret < 0)
698 return ret;
699
700 return 0;
701
702fail:
703 if (command_allocator)
704 d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value);
705 return AVERROR(EINVAL);
706}
static AVFormatContext * ctx
Libavcodec external API header.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
common internal and external API header
#define NULL
Definition coverity.c:32
static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer)
static void free_reference_only_resources(AVCodecContext *avctx)
av_cold int ff_d3d12va_decode_init(AVCodecContext *avctx)
init D3D12VADecodeContext
static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx)
static void prepare_reference_only_resources(AVCodecContext *avctx)
static int d3d12va_sync_with_gpu(AVCodecContext *avctx)
int ff_d3d12va_common_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
d3d12va common frame params
static int d3d12va_create_decoder_heap(AVCodecContext *avctx)
static int d3d12va_resize_bitstream_buffer(AVCodecContext *avctx, ID3D12Resource **ppBuffer, UINT64 bitstream_size)
static AVBufferRef * bufref_wrap_interface(IUnknown *iface)
static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator, ID3D12Resource **ppBuffer, UINT64 bitstream_size)
int ff_d3d12va_common_end_frame(AVCodecContext *avctx, AVFrame *frame, const void *pp, unsigned pp_size, const void *qm, unsigned qm_size, UINT64 bitstream_size, int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *))
d3d12va common end frame
static ID3D12Resource * get_reference_only_resource(AVCodecContext *avctx, ID3D12Resource *output_resource)
av_cold int ff_d3d12va_decode_uninit(AVCodecContext *avctx)
uninit D3D12VADecodeContext
static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator, ID3D12Resource *pBuffer, uint64_t fence_value)
static int d3d12va_create_decoder(AVCodecContext *avctx)
unsigned ff_d3d12va_get_surface_index(const AVCodecContext *avctx, D3D12VADecodeContext *ctx, const AVFrame *frame, int curr)
static int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers, ID3D12Resource *current_resource, int state_before, int state_end)
static void bufref_free_interface(void *opaque, uint8_t *data)
#define D3D12VA_VIDEO_DEC_ASYNC_DEPTH
#define D3D12VA_DECODE_CONTEXT(avctx)
#define D3D12VA_FRAMES_CONTEXT(avctx)
int ff_decode_get_hw_frames_ctx(AVCodecContext *avctx, enum AVHWDeviceType dev_type)
Make sure avctx.hw_frames_ctx is set.
Definition decode.c:1069
static AVFrame * frame
#define fail
Definition test.h:479
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_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
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition error.h:64
#define AVERROR(e)
Definition error.h:45
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
void av_fifo_freep2(AVFifo **f)
Free an AVFifo and reset pointer to NULL.
Definition fifo.c:286
#define AV_FIFO_FLAG_AUTO_GROW
Automatically resize the FIFO on writes, so that the data fits.
Definition fifo.h:63
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
int av_fifo_write(AVFifo *f, const void *buf, size_t nb_elems)
Write data into a FIFO.
Definition fifo.c:188
int av_fifo_read(AVFifo *f, void *buf, size_t nb_elems)
Read data from a FIFO.
Definition fifo.c:240
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AV_HWDEVICE_TYPE_D3D12VA
Definition hwcontext.h:40
An API-specific header for AV_HWDEVICE_TYPE_D3D12VA.
#define D3D12_OBJECT_RELEASE(pInterface)
A release macro used by D3D12 objects highly frequently.
#define DX_CHECK(hr)
A check macro used by D3D12 functions highly frequently.
#define av_cold
Definition attributes.h:117
const char * desc
Definition libsvtav1.c:83
#define FFSWAP(type, a, b)
Definition macros.h:52
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define AV_PIX_FMT_YUV420P10
Definition pixfmt.h:545
#define AV_PIX_FMT_P012
Definition pixfmt.h:609
#define AV_PIX_FMT_YUV420P12
Definition pixfmt.h:549
#define AV_PIX_FMT_P010
Definition pixfmt.h:608
@ 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
@ AV_PIX_FMT_D3D12
Hardware surfaces for Direct3D 12.
Definition pixfmt.h:440
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
main external API structure.
Definition avcodec.h:443
enum AVPixelFormat sw_pix_fmt
Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx.
Definition avcodec.h:650
AVRational framerate
Definition avcodec.h:563
const struct AVHWAccel * hwaccel
Hardware accelerator in use.
Definition avcodec.h:1424
int64_t bit_rate
the average bitrate
Definition avcodec.h:493
int coded_width
Bitstream width / height, may be different from width/height e.g.
Definition avcodec.h:619
This struct is allocated as AVHWDeviceContext.hwctx.
ID3D12Device * device
Device used for objects creation and access.
ID3D12VideoDevice * video_device
If unset, this will be set from the device field on init.
D3D12VA frame descriptor for pool allocation.
This struct is allocated as AVHWFramesContext.hwctx.
DXGI_FORMAT format
DXGI_FORMAT format.
This struct is used to sync d3d12 execution.
HANDLE event
A handle to the event object that's raised when the fence reaches a certain value.
ID3D12Fence * fence
D3D12 fence object.
uint64_t fence_value
The fence value used for sync.
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
enum AVPixelFormat pix_fmt
Supported pixel format.
Definition avcodec.h:1986
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition hwcontext.h:88
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition hwcontext.h:200
void * hwctx
The format-specific data, allocated and freed automatically along with this context.
Definition hwcontext.h:153
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition hwcontext.h:213
int width
The allocated dimensions of the frames in this pool.
Definition hwcontext.h:220
AVHWDeviceContext * device_ctx
The parent AVHWDeviceContext.
Definition hwcontext.h:137
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
This structure is used to provide the necessary configurations and data to the FFmpeg Direct3D 12 HWA...
uint64_t fence_value
ID3D12Resource * buffer
ID3D12CommandAllocator * command_allocator
ID3D12Resource * output_resource
ID3D12Resource * resource
#define av_freep(p)
#define av_log(a,...)
static char buffer[20]
Definition seek.c:32