FFmpeg
hwcontext_d3d12va.c
Go to the documentation of this file.
1 /*
2  * Direct3D 12 HW acceleration.
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 "config.h"
24 #include "common.h"
25 #include "hwcontext.h"
26 #include "hwcontext_internal.h"
28 #include "hwcontext_d3d12va.h"
29 #include "imgutils.h"
30 #include "mem.h"
31 #include "pixdesc.h"
32 #include "pixfmt.h"
33 #include "thread.h"
34 #include "compat/w32dlfcn.h"
35 #include <dxgi1_3.h>
36 
37 typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT Flags, REFIID riid, void **ppFactory);
38 
39 typedef struct D3D12VAFramesContext {
40  /**
41  * The public AVD3D12VAFramesContext. See hwcontext_d3d12va.h for it.
42  */
44 
45  ID3D12Resource *staging_download_buffer;
46  ID3D12Resource *staging_upload_buffer;
47  ID3D12CommandQueue *command_queue;
48  ID3D12CommandAllocator *command_allocator;
49  ID3D12GraphicsCommandList *command_list;
53 
54 typedef struct D3D12VADevicePriv {
55  /**
56  * The public AVD3D12VADeviceContext. See hwcontext_d3d12va.h for it.
57  */
59  HANDLE d3d12lib;
60  HANDLE dxgilib;
62  PFN_D3D12_CREATE_DEVICE create_device;
63  PFN_D3D12_GET_DEBUG_INTERFACE get_debug_interface;
65 
66 static const struct {
67  DXGI_FORMAT d3d_format;
69 } supported_formats[] = {
70  { DXGI_FORMAT_NV12, AV_PIX_FMT_NV12 },
71  { DXGI_FORMAT_P010, AV_PIX_FMT_P010 },
72 };
73 
74 static void d3d12va_default_lock(void *ctx)
75 {
76  WaitForSingleObjectEx(ctx, INFINITE, FALSE);
77 }
78 
79 static void d3d12va_default_unlock(void *ctx)
80 {
81  ReleaseMutex(ctx);
82 }
83 
85 {
86  uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence);
87  if (completion < psync_ctx->fence_value) {
88  if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event)))
89  return AVERROR(EINVAL);
90 
91  WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE);
92  }
93 
94  return 0;
95 }
96 
97 static inline int d3d12va_wait_queue_idle(AVD3D12VASyncContext *psync_ctx, ID3D12CommandQueue *command_queue)
98 {
99  DX_CHECK(ID3D12CommandQueue_Signal(command_queue, psync_ctx->fence, ++psync_ctx->fence_value));
100  return d3d12va_fence_completion(psync_ctx);
101 
102 fail:
103  return AVERROR(EINVAL);
104 }
105 
106 static int d3d12va_create_staging_buffer_resource(AVHWFramesContext *ctx, D3D12_RESOURCE_STATES states,
107  ID3D12Resource **ppResource, int download)
108 {
109  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
110  D3D12VAFramesContext *s = ctx->hwctx;
111  D3D12_HEAP_PROPERTIES props = { .Type = download ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_UPLOAD };
112  D3D12_RESOURCE_DESC desc = {
113  .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
114  .Alignment = 0,
115  .Width = s->luma_component_size + (s->luma_component_size >> 1),
116  .Height = 1,
117  .DepthOrArraySize = 1,
118  .MipLevels = 1,
119  .Format = DXGI_FORMAT_UNKNOWN,
120  .SampleDesc = { .Count = 1, .Quality = 0 },
121  .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
122  .Flags = D3D12_RESOURCE_FLAG_NONE,
123  };
124 
125  if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc,
126  states, NULL, &IID_ID3D12Resource, (void **)ppResource))) {
127  av_log(ctx, AV_LOG_ERROR, "Could not create the staging buffer resource\n");
128  return AVERROR_UNKNOWN;
129  }
130 
131  return 0;
132 }
133 
135 {
136  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
137  D3D12VAFramesContext *s = ctx->hwctx;
138  AVD3D12VAFramesContext *frames_hwctx = &s->p;
139 
140  D3D12_COMMAND_QUEUE_DESC queue_desc = {
141  .Type = D3D12_COMMAND_LIST_TYPE_COPY,
142  .Priority = 0,
143  .NodeMask = 0,
144  };
145 
146  s->luma_component_size = FFALIGN(ctx->width * (frames_hwctx->format == DXGI_FORMAT_P010 ? 2 : 1),
147  D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) * ctx->height;
148 
149  DX_CHECK(ID3D12Device_CreateFence(device_hwctx->device, 0, D3D12_FENCE_FLAG_NONE,
150  &IID_ID3D12Fence, (void **)&s->sync_ctx.fence));
151 
152  s->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
153  if (!s->sync_ctx.event)
154  goto fail;
155 
156  DX_CHECK(ID3D12Device_CreateCommandQueue(device_hwctx->device, &queue_desc,
157  &IID_ID3D12CommandQueue, (void **)&s->command_queue));
158 
159  DX_CHECK(ID3D12Device_CreateCommandAllocator(device_hwctx->device, queue_desc.Type,
160  &IID_ID3D12CommandAllocator, (void **)&s->command_allocator));
161 
162  DX_CHECK(ID3D12Device_CreateCommandList(device_hwctx->device, 0, queue_desc.Type,
163  s->command_allocator, NULL, &IID_ID3D12GraphicsCommandList, (void **)&s->command_list));
164 
165  DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list));
166 
167  ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list);
168 
169  return d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue);
170 
171 fail:
172  return AVERROR(EINVAL);
173 }
174 
176 {
177  D3D12VAFramesContext *s = ctx->hwctx;
178 
179  D3D12_OBJECT_RELEASE(s->sync_ctx.fence);
180  if (s->sync_ctx.event)
181  CloseHandle(s->sync_ctx.event);
182 
183  D3D12_OBJECT_RELEASE(s->staging_download_buffer);
184  D3D12_OBJECT_RELEASE(s->staging_upload_buffer);
185  D3D12_OBJECT_RELEASE(s->command_allocator);
186  D3D12_OBJECT_RELEASE(s->command_list);
187  D3D12_OBJECT_RELEASE(s->command_queue);
188 }
189 
190 static int d3d12va_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints)
191 {
192  HRESULT hr;
193  int nb_sw_formats = 0;
194  AVD3D12VADeviceContext *device_hwctx = ctx->hwctx;
195 
197  sizeof(*constraints->valid_sw_formats));
198  if (!constraints->valid_sw_formats)
199  return AVERROR(ENOMEM);
200 
201  for (int i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
202  D3D12_FEATURE_DATA_FORMAT_SUPPORT format_support = { supported_formats[i].d3d_format };
203  hr = ID3D12Device_CheckFeatureSupport(device_hwctx->device, D3D12_FEATURE_FORMAT_SUPPORT, &format_support, sizeof(format_support));
204  if (SUCCEEDED(hr) && (format_support.Support1 & D3D12_FORMAT_SUPPORT1_TEXTURE2D))
205  constraints->valid_sw_formats[nb_sw_formats++] = supported_formats[i].pix_fmt;
206  }
207  constraints->valid_sw_formats[nb_sw_formats] = AV_PIX_FMT_NONE;
208 
209  constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
210  if (!constraints->valid_hw_formats)
211  return AVERROR(ENOMEM);
212 
213  constraints->valid_hw_formats[0] = AV_PIX_FMT_D3D12;
214  constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
215 
216  return 0;
217 }
218 
219 static void free_texture(void *opaque, uint8_t *data)
220 {
222 
223  D3D12_OBJECT_RELEASE(frame->texture);
224  D3D12_OBJECT_RELEASE(frame->sync_ctx.fence);
225  if (frame->sync_ctx.event)
226  CloseHandle(frame->sync_ctx.event);
227 
228  av_freep(&data);
229 }
230 
231 static AVBufferRef *d3d12va_pool_alloc(void *opaque, size_t size)
232 {
234  AVD3D12VAFramesContext *hwctx = ctx->hwctx;
235  AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
236 
237  AVBufferRef *buf;
239  D3D12_HEAP_PROPERTIES props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
240  D3D12_RESOURCE_DESC desc = {
241  .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D,
242  .Alignment = 0,
243  .Width = ctx->width,
244  .Height = ctx->height,
245  .DepthOrArraySize = 1,
246  .MipLevels = 1,
247  .Format = hwctx->format,
248  .SampleDesc = {.Count = 1, .Quality = 0 },
249  .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
250  .Flags = hwctx->flags,
251  };
252 
253  frame = av_mallocz(sizeof(AVD3D12VAFrame));
254  if (!frame)
255  return NULL;
256 
257  if (FAILED(ID3D12Device_CreateCommittedResource(device_hwctx->device, &props, D3D12_HEAP_FLAG_NONE, &desc,
258  D3D12_RESOURCE_STATE_COMMON, NULL, &IID_ID3D12Resource, (void **)&frame->texture))) {
259  av_log(ctx, AV_LOG_ERROR, "Could not create the texture\n");
260  goto fail;
261  }
262 
263  DX_CHECK(ID3D12Device_CreateFence(device_hwctx->device, 0, D3D12_FENCE_FLAG_NONE,
264  &IID_ID3D12Fence, (void **)&frame->sync_ctx.fence));
265 
266  frame->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL);
267  if (!frame->sync_ctx.event)
268  goto fail;
269 
270  buf = av_buffer_create((uint8_t *)frame, sizeof(frame), free_texture, NULL, 0);
271  if (!buf)
272  goto fail;
273 
274  return buf;
275 
276 fail:
277  free_texture(NULL, (uint8_t *)frame);
278  return NULL;
279 }
280 
282 {
283  AVD3D12VAFramesContext *hwctx = ctx->hwctx;
284  int i;
285 
286  for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
287  if (ctx->sw_format == supported_formats[i].pix_fmt) {
288  if (hwctx->format != DXGI_FORMAT_UNKNOWN &&
289  hwctx->format != supported_formats[i].d3d_format)
290  av_log(ctx, AV_LOG_WARNING, "Incompatible DXGI format provided by user, will be overided\n");
291  hwctx->format = supported_formats[i].d3d_format;
292  break;
293  }
294  }
296  av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n",
297  av_get_pix_fmt_name(ctx->sw_format));
298  return AVERROR(EINVAL);
299  }
300 
303 
304  if (!ffhwframesctx(ctx)->pool_internal)
305  return AVERROR(ENOMEM);
306 
307  return 0;
308 }
309 
311 {
312  int ret;
313 
314  frame->buf[0] = av_buffer_pool_get(ctx->pool);
315  if (!frame->buf[0])
316  return AVERROR(ENOMEM);
317 
318  ret = av_image_fill_arrays(frame->data, frame->linesize, NULL,
319  ctx->sw_format, ctx->width, ctx->height,
320  D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
321  if (ret < 0)
322  return ret;
323 
324  frame->data[0] = frame->buf[0]->data;
325  frame->format = AV_PIX_FMT_D3D12;
326  frame->width = ctx->width;
327  frame->height = ctx->height;
328 
329  return 0;
330 }
331 
334  enum AVPixelFormat **formats)
335 {
336  enum AVPixelFormat *fmts;
337 
338  fmts = av_malloc_array(2, sizeof(*fmts));
339  if (!fmts)
340  return AVERROR(ENOMEM);
341 
342  fmts[0] = ctx->sw_format;
343  fmts[1] = AV_PIX_FMT_NONE;
344 
345  *formats = fmts;
346 
347  return 0;
348 }
349 
351  const AVFrame *src)
352 {
353  AVD3D12VADeviceContext *hwctx = ctx->device_ctx->hwctx;
354  D3D12VAFramesContext *s = ctx->hwctx;
355  AVD3D12VAFramesContext *frames_hwctx = &s->p;
356 
357  int ret;
358  int download = src->format == AV_PIX_FMT_D3D12;
359  const AVFrame *frame = download ? src : dst;
360  const AVFrame *other = download ? dst : src;
361 
362  AVD3D12VAFrame *f = (AVD3D12VAFrame *)frame->data[0];
363  ID3D12Resource *texture = (ID3D12Resource *)f->texture;
364 
365  uint8_t *mapped_data;
366  uint8_t *data[4];
367  int linesizes[4];
368 
369  D3D12_TEXTURE_COPY_LOCATION staging_y_location = { 0 };
370  D3D12_TEXTURE_COPY_LOCATION staging_uv_location = { 0 };
371 
372  D3D12_TEXTURE_COPY_LOCATION texture_y_location = {
373  .pResource = texture,
374  .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
375  .SubresourceIndex = 0,
376  };
377 
378  D3D12_TEXTURE_COPY_LOCATION texture_uv_location = {
379  .pResource = texture,
380  .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
381  .SubresourceIndex = 1,
382  };
383 
384  D3D12_RESOURCE_BARRIER barrier = {
385  .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
386  .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
387  .Transition = {
388  .pResource = texture,
389  .StateBefore = D3D12_RESOURCE_STATE_COMMON,
390  .StateAfter = download ? D3D12_RESOURCE_STATE_COPY_SOURCE : D3D12_RESOURCE_STATE_COPY_DEST,
391  .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
392  },
393  };
394 
395  if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format)
396  return AVERROR(EINVAL);
397 
398  hwctx->lock(hwctx->lock_ctx);
399 
400  if (!s->command_queue) {
402  if (ret < 0)
403  goto fail;
404  }
405 
406  for (int i = 0; i < 4; i++)
407  linesizes[i] = FFALIGN(frame->width * (frames_hwctx->format == DXGI_FORMAT_P010 ? 2 : 1),
408  D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
409 
410  staging_y_location = (D3D12_TEXTURE_COPY_LOCATION) {
411  .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
412  .PlacedFootprint = {
413  .Offset = 0,
414  .Footprint = {
415  .Format = frames_hwctx->format == DXGI_FORMAT_P010 ?
416  DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM,
417  .Width = ctx->width,
418  .Height = ctx->height,
419  .Depth = 1,
420  .RowPitch = linesizes[0],
421  },
422  },
423  };
424 
425  staging_uv_location = (D3D12_TEXTURE_COPY_LOCATION) {
426  .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
427  .PlacedFootprint = {
428  .Offset = s->luma_component_size,
429  .Footprint = {
430  .Format = frames_hwctx->format == DXGI_FORMAT_P010 ?
431  DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM,
432  .Width = ctx->width >> 1,
433  .Height = ctx->height >> 1,
434  .Depth = 1,
435  .RowPitch = linesizes[0],
436  },
437  },
438  };
439 
440  DX_CHECK(ID3D12CommandAllocator_Reset(s->command_allocator));
441 
442  DX_CHECK(ID3D12GraphicsCommandList_Reset(s->command_list, s->command_allocator, NULL));
443 
444  if (download) {
445  if (!s->staging_download_buffer) {
446  ret = d3d12va_create_staging_buffer_resource(ctx, D3D12_RESOURCE_STATE_COPY_DEST,
447  &s->staging_download_buffer, 1);
448  if (ret < 0) {
449  goto fail;
450  }
451  }
452 
453  staging_y_location.pResource = staging_uv_location.pResource = s->staging_download_buffer;
454 
455  ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
456 
457  ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
458  &staging_y_location, 0, 0, 0,
459  &texture_y_location, NULL);
460 
461  ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
462  &staging_uv_location, 0, 0, 0,
463  &texture_uv_location, NULL);
464 
465  barrier.Transition.StateBefore = barrier.Transition.StateAfter;
466  barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
467  ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
468 
469  DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list));
470 
471  DX_CHECK(ID3D12CommandQueue_Wait(s->command_queue, f->sync_ctx.fence, f->sync_ctx.fence_value));
472 
473  ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list);
474 
475  ret = d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue);
476  if (ret < 0)
477  goto fail;
478 
479  DX_CHECK(ID3D12Resource_Map(s->staging_download_buffer, 0, NULL, (void **)&mapped_data));
480  av_image_fill_pointers(data, ctx->sw_format, ctx->height, mapped_data, linesizes);
481 
482  av_image_copy2(dst->data, dst->linesize, data, linesizes,
483  ctx->sw_format, ctx->width, ctx->height);
484 
485  ID3D12Resource_Unmap(s->staging_download_buffer, 0, NULL);
486  } else {
487  if (!s->staging_upload_buffer) {
488  ret = d3d12va_create_staging_buffer_resource(ctx, D3D12_RESOURCE_STATE_GENERIC_READ,
489  &s->staging_upload_buffer, 0);
490  if (ret < 0) {
491  goto fail;
492  }
493  }
494 
495  staging_y_location.pResource = staging_uv_location.pResource = s->staging_upload_buffer;
496 
497  DX_CHECK(ID3D12Resource_Map(s->staging_upload_buffer, 0, NULL, (void **)&mapped_data));
498  av_image_fill_pointers(data, ctx->sw_format, ctx->height, mapped_data, linesizes);
499 
500  av_image_copy2(data, linesizes, src->data, src->linesize,
501  ctx->sw_format, ctx->width, ctx->height);
502 
503  ID3D12Resource_Unmap(s->staging_upload_buffer, 0, NULL);
504 
505  ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
506 
507  ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
508  &texture_y_location, 0, 0, 0,
509  &staging_y_location, NULL);
510 
511  ID3D12GraphicsCommandList_CopyTextureRegion(s->command_list,
512  &texture_uv_location, 0, 0, 0,
513  &staging_uv_location, NULL);
514 
515  barrier.Transition.StateBefore = barrier.Transition.StateAfter;
516  barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
517  ID3D12GraphicsCommandList_ResourceBarrier(s->command_list, 1, &barrier);
518 
519  DX_CHECK(ID3D12GraphicsCommandList_Close(s->command_list));
520 
521  ID3D12CommandQueue_ExecuteCommandLists(s->command_queue, 1, (ID3D12CommandList **)&s->command_list);
522 
523  ret = d3d12va_wait_queue_idle(&s->sync_ctx, s->command_queue);
524  if (ret < 0)
525  goto fail;
526  }
527 
528  hwctx->unlock(hwctx->lock_ctx);
529 
530  return 0;
531 
532 fail:
533  hwctx->unlock(hwctx->lock_ctx);
534  return AVERROR(EINVAL);
535 }
536 
538 {
539  D3D12VADevicePriv *priv = hwdev->hwctx;
540 
541 #if !HAVE_UWP
542  priv->d3d12lib = dlopen("d3d12.dll", 0);
543  priv->dxgilib = dlopen("dxgi.dll", 0);
544 
545  if (!priv->d3d12lib || !priv->dxgilib)
546  goto fail;
547 
548  priv->create_device = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(priv->d3d12lib, "D3D12CreateDevice");
549  if (!priv->create_device)
550  goto fail;
551 
552  priv->create_dxgi_factory2 = (PFN_CREATE_DXGI_FACTORY2)GetProcAddress(priv->dxgilib, "CreateDXGIFactory2");
553  if (!priv->create_dxgi_factory2)
554  goto fail;
555 
556  priv->get_debug_interface = (PFN_D3D12_GET_DEBUG_INTERFACE)GetProcAddress(priv->d3d12lib, "D3D12GetDebugInterface");
557 #else
558  priv->create_device = (PFN_D3D12_CREATE_DEVICE) D3D12CreateDevice;
559  priv->create_dxgi_factory2 = (PFN_CREATE_DXGI_FACTORY2) CreateDXGIFactory2;
560  priv->get_debug_interface = (PFN_D3D12_GET_DEBUG_INTERFACE) D3D12GetDebugInterface;
561 #endif
562  return 0;
563 
564 fail:
565  av_log(hwdev, AV_LOG_ERROR, "Failed to load D3D12 library or its functions\n");
566  return AVERROR_UNKNOWN;
567 }
568 
570 {
571  D3D12VADevicePriv *priv = hwdev->hwctx;
572  AVD3D12VADeviceContext *ctx = &priv->p;
573 
574  D3D12_OBJECT_RELEASE(ctx->device);
575 
576  if (priv->d3d12lib)
577  dlclose(priv->d3d12lib);
578 
579  if (priv->dxgilib)
580  dlclose(priv->dxgilib);
581 }
582 
584 {
585  AVD3D12VADeviceContext *ctx = hwdev->hwctx;
586 
587  if (!ctx->lock) {
588  ctx->lock_ctx = CreateMutex(NULL, 0, NULL);
589  if (ctx->lock_ctx == INVALID_HANDLE_VALUE) {
590  av_log(NULL, AV_LOG_ERROR, "Failed to create a mutex\n");
591  return AVERROR(EINVAL);
592  }
593  ctx->lock = d3d12va_default_lock;
594  ctx->unlock = d3d12va_default_unlock;
595  }
596 
597  if (!ctx->video_device)
598  DX_CHECK(ID3D12Device_QueryInterface(ctx->device, &IID_ID3D12VideoDevice, (void **)&ctx->video_device));
599 
600  return 0;
601 
602 fail:
603  return AVERROR(EINVAL);
604 }
605 
607 {
608  AVD3D12VADeviceContext *device_hwctx = hwdev->hwctx;
609 
610  D3D12_OBJECT_RELEASE(device_hwctx->video_device);
611 
612  if (device_hwctx->lock == d3d12va_default_lock) {
613  CloseHandle(device_hwctx->lock_ctx);
614  device_hwctx->lock_ctx = INVALID_HANDLE_VALUE;
615  device_hwctx->lock = NULL;
616  }
617 }
618 
619 static int d3d12va_device_create(AVHWDeviceContext *hwdev, const char *device,
620  AVDictionary *opts, int flags)
621 {
622  D3D12VADevicePriv *priv = hwdev->hwctx;
623  AVD3D12VADeviceContext *ctx = &priv->p;
624 
625  HRESULT hr;
626  UINT create_flags = 0;
627  IDXGIAdapter *pAdapter = NULL;
628 
629  int ret;
630  int is_debug = !!av_dict_get(opts, "debug", NULL, 0);
631 
632  hwdev->free = d3d12va_device_free;
633 
634  ret = d3d12va_load_functions(hwdev);
635  if (ret < 0)
636  return ret;
637 
638  if (is_debug) {
639  ID3D12Debug *pDebug;
640  if (priv->get_debug_interface && SUCCEEDED(priv->get_debug_interface(&IID_ID3D12Debug, (void **)&pDebug))) {
641  create_flags |= DXGI_CREATE_FACTORY_DEBUG;
642  ID3D12Debug_EnableDebugLayer(pDebug);
643  D3D12_OBJECT_RELEASE(pDebug);
644  av_log(hwdev, AV_LOG_INFO, "D3D12 debug layer is enabled!\n");
645  }
646  }
647 
648  if (!ctx->device) {
649  IDXGIFactory2 *pDXGIFactory = NULL;
650 
651  hr = priv->create_dxgi_factory2(create_flags, &IID_IDXGIFactory2, (void **)&pDXGIFactory);
652  if (SUCCEEDED(hr)) {
653  int adapter = device ? atoi(device) : 0;
654  if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter)))
655  pAdapter = NULL;
656  IDXGIFactory2_Release(pDXGIFactory);
657  }
658 
659  if (pAdapter) {
660  DXGI_ADAPTER_DESC desc;
661  hr = IDXGIAdapter2_GetDesc(pAdapter, &desc);
662  if (!FAILED(hr)) {
663  av_log(ctx, AV_LOG_INFO, "Using device %04x:%04x (%ls).\n",
664  desc.VendorId, desc.DeviceId, desc.Description);
665  }
666  }
667 
668  hr = priv->create_device((IUnknown *)pAdapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&ctx->device);
669  D3D12_OBJECT_RELEASE(pAdapter);
670  if (FAILED(hr)) {
671  av_log(ctx, AV_LOG_ERROR, "Failed to create Direct 3D 12 device (%lx)\n", (long)hr);
672  return AVERROR_UNKNOWN;
673  }
674  }
675 
676  return 0;
677 }
678 
681  .name = "D3D12VA",
682 
683  .device_hwctx_size = sizeof(D3D12VADevicePriv),
684  .frames_hwctx_size = sizeof(D3D12VAFramesContext),
685 
686  .device_create = d3d12va_device_create,
687  .device_init = d3d12va_device_init,
688  .device_uninit = d3d12va_device_uninit,
689  .frames_get_constraints = d3d12va_frames_get_constraints,
690  .frames_init = d3d12va_frames_init,
691  .frames_uninit = d3d12va_frames_uninit,
692  .frames_get_buffer = d3d12va_get_buffer,
693  .transfer_get_formats = d3d12va_transfer_get_formats,
694  .transfer_data_to = d3d12va_transfer_data,
695  .transfer_data_from = d3d12va_transfer_data,
696 
697  .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_D3D12, AV_PIX_FMT_NONE },
698 };
formats
formats
Definition: signature.h:47
AVHWDeviceContext::hwctx
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition: hwcontext.h:85
FFHWFramesContext::pool_internal
AVBufferPool * pool_internal
Definition: hwcontext_internal.h:101
AVD3D12VADeviceContext::device
ID3D12Device * device
Device used for objects creation and access.
Definition: hwcontext_d3d12va.h:54
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:215
d3d12va_transfer_data
static int d3d12va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src)
Definition: hwcontext_d3d12va.c:350
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
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
AVD3D12VADeviceContext::lock_ctx
void * lock_ctx
Definition: hwcontext_d3d12va.h:77
thread.h
d3d12va_frames_get_constraints
static int d3d12va_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints)
Definition: hwcontext_d3d12va.c:190
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:389
pixdesc.h
D3D12VADevicePriv
Definition: hwcontext_d3d12va.c:54
data
const char data[16]
Definition: mxf.c:149
d3d12va_device_create
static int d3d12va_device_create(AVHWDeviceContext *hwdev, const char *device, AVDictionary *opts, int flags)
Definition: hwcontext_d3d12va.c:619
D3D12VAFramesContext::sync_ctx
AVD3D12VASyncContext sync_ctx
Definition: hwcontext_d3d12va.c:50
AVDictionary
Definition: dict.c:34
AVHWFramesConstraints::valid_hw_formats
enum AVPixelFormat * valid_hw_formats
A list of possible values for format in the hw_frames_ctx, terminated by AV_PIX_FMT_NONE.
Definition: hwcontext.h:446
AVERROR_UNKNOWN
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition: error.h:73
D3D12VAFramesContext::staging_download_buffer
ID3D12Resource * staging_download_buffer
Definition: hwcontext_d3d12va.c:45
d3d_format
DXGI_FORMAT d3d_format
Definition: hwcontext_d3d12va.c:67
AVHWFramesConstraints
This struct describes the constraints on hardware frames attached to a given device with a hardware-s...
Definition: hwcontext.h:441
D3D12VADevicePriv::d3d12lib
HANDLE d3d12lib
Definition: hwcontext_d3d12va.c:59
AVHWDeviceContext::free
void(* free)(struct AVHWDeviceContext *ctx)
This field may be set by the caller before calling av_hwdevice_ctx_init().
Definition: hwcontext.h:97
fail
#define fail()
Definition: checkasm.h:200
av_buffer_pool_init2
AVBufferPool * av_buffer_pool_init2(size_t size, void *opaque, AVBufferRef *(*alloc)(void *opaque, size_t size), void(*pool_free)(void *opaque))
Allocate and initialize a buffer pool with a more complex allocator.
Definition: buffer.c:259
D3D12VADevicePriv::get_debug_interface
PFN_D3D12_GET_DEBUG_INTERFACE get_debug_interface
Definition: hwcontext_d3d12va.c:63
av_image_fill_pointers
int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int height, uint8_t *ptr, const int linesizes[4])
Fill plane data pointers for an image with pixel format pix_fmt and height height.
Definition: imgutils.c:145
d3d12va_default_unlock
static void d3d12va_default_unlock(void *ctx)
Definition: hwcontext_d3d12va.c:79
AVHWDeviceContext
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition: hwcontext.h:60
HWContextType::type
enum AVHWDeviceType type
Definition: hwcontext_internal.h:30
ffhwframesctx
static FFHWFramesContext * ffhwframesctx(AVHWFramesContext *ctx)
Definition: hwcontext_internal.h:115
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:209
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
AVHWFramesConstraints::valid_sw_formats
enum AVPixelFormat * valid_sw_formats
A list of possible values for sw_format in the hw_frames_ctx, terminated by AV_PIX_FMT_NONE.
Definition: hwcontext.h:453
av_dict_get
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition: dict.c:62
av_buffer_pool_get
AVBufferRef * av_buffer_pool_get(AVBufferPool *pool)
Allocate a new AVBuffer, reusing an old buffer from the pool when available.
Definition: buffer.c:390
d3d12va_device_init
static int d3d12va_device_init(AVHWDeviceContext *hwdev)
Definition: hwcontext_d3d12va.c:583
s
#define s(width, name)
Definition: cbs_vp9.c:198
D3D12_OBJECT_RELEASE
#define D3D12_OBJECT_RELEASE(pInterface)
A release macro used by D3D12 objects highly frequently.
Definition: hwcontext_d3d12va_internal.h:51
d3d12va_pool_alloc
static AVBufferRef * d3d12va_pool_alloc(void *opaque, size_t size)
Definition: hwcontext_d3d12va.c:231
AVD3D12VADeviceContext::unlock
void(* unlock)(void *lock_ctx)
Definition: hwcontext_d3d12va.h:76
ctx
AVFormatContext * ctx
Definition: movenc.c:49
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
opts
AVDictionary * opts
Definition: movenc.c:51
D3D12VAFramesContext::staging_upload_buffer
ID3D12Resource * staging_upload_buffer
Definition: hwcontext_d3d12va.c:46
NULL
#define NULL
Definition: coverity.c:32
d3d12va_device_free
static void d3d12va_device_free(AVHWDeviceContext *hwdev)
Definition: hwcontext_d3d12va.c:569
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
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
AVD3D12VAFramesContext::flags
D3D12_RESOURCE_FLAGS flags
Options for working with resources.
Definition: hwcontext_d3d12va.h:139
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
av_image_fill_arrays
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align)
Setup the data pointers and linesizes based on the specified image parameters and the provided array.
Definition: imgutils.c:446
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:83
d3d12va_fence_completion
static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx)
Definition: hwcontext_d3d12va.c:84
size
int size
Definition: twinvq_data.h:10344
free_texture
static void free_texture(void *opaque, uint8_t *data)
Definition: hwcontext_d3d12va.c:219
D3D12VAFramesContext::p
AVD3D12VAFramesContext p
The public AVD3D12VAFramesContext.
Definition: hwcontext_d3d12va.c:43
AVFrame::format
int format
format of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames,...
Definition: frame.h:476
D3D12VADevicePriv::p
AVD3D12VADeviceContext p
The public AVD3D12VADeviceContext.
Definition: hwcontext_d3d12va.c:58
d3d12va_frames_init
static int d3d12va_frames_init(AVHWFramesContext *ctx)
Definition: hwcontext_d3d12va.c:281
AVD3D12VAFrame
D3D12VA frame descriptor for pool allocation.
Definition: hwcontext_d3d12va.h:106
supported_formats
static const struct @442 supported_formats[]
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:220
D3D12VADevicePriv::create_dxgi_factory2
PFN_CREATE_DXGI_FACTORY2 create_dxgi_factory2
Definition: hwcontext_d3d12va.c:61
d3d12va_load_functions
static int d3d12va_load_functions(AVHWDeviceContext *hwdev)
Definition: hwcontext_d3d12va.c:537
D3D12VAFramesContext::command_allocator
ID3D12CommandAllocator * command_allocator
Definition: hwcontext_d3d12va.c:48
AVD3D12VADeviceContext
This struct is allocated as AVHWDeviceContext.hwctx.
Definition: hwcontext_d3d12va.h:43
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:31
common.h
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:256
d3d12va_frames_uninit
static void d3d12va_frames_uninit(AVHWFramesContext *ctx)
Definition: hwcontext_d3d12va.c:175
AVHWFrameTransferDirection
AVHWFrameTransferDirection
Definition: hwcontext.h:403
d3d12va_default_lock
static void d3d12va_default_lock(void *ctx)
Definition: hwcontext_d3d12va.c:74
AVD3D12VAFramesContext::format
DXGI_FORMAT format
DXGI_FORMAT format.
Definition: hwcontext_d3d12va.h:131
AVHWFramesContext
This struct describes a set or pool of "hardware" frames (i.e.
Definition: hwcontext.h:115
D3D12VADevicePriv::create_device
PFN_D3D12_CREATE_DEVICE create_device
Definition: hwcontext_d3d12va.c:62
ret
ret
Definition: filter_design.txt:187
pixfmt.h
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
frame
these buffered frames must be flushed immediately if a new input produces new the filter must not call request_frame to get more It must just process the frame or queue it The task of requesting more frames is left to the filter s request_frame method or the application If a filter has several the filter must be ready for frames arriving randomly on any input any filter with several inputs will most likely require some kind of queuing mechanism It is perfectly acceptable to have a limited queue and to drop frames when the inputs are too unbalanced request_frame For filters that do not use the this method is called when a frame is wanted on an output For a it should directly call filter_frame on the corresponding output For a if there are queued frames already one of these frames should be pushed If the filter should request a frame on one of its repeatedly until at least one frame has been pushed Return or at least make progress towards producing a frame
Definition: filter_design.txt:264
D3D12VADevicePriv::dxgilib
HANDLE dxgilib
Definition: hwcontext_d3d12va.c:60
d3d12va_wait_queue_idle
static int d3d12va_wait_queue_idle(AVD3D12VASyncContext *psync_ctx, ID3D12CommandQueue *command_queue)
Definition: hwcontext_d3d12va.c:97
d3d12va_get_buffer
static int d3d12va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
Definition: hwcontext_d3d12va.c:310
ff_hwcontext_type_d3d12va
const HWContextType ff_hwcontext_type_d3d12va
Definition: hwcontext_d3d12va.c:679
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
D3D12VAFramesContext
Definition: hwcontext_d3d12va.c:39
D3D12VAFramesContext::command_list
ID3D12GraphicsCommandList * command_list
Definition: hwcontext_d3d12va.c:49
av_image_copy2
static void av_image_copy2(uint8_t *const dst_data[4], const int dst_linesizes[4], uint8_t *const src_data[4], const int src_linesizes[4], enum AVPixelFormat pix_fmt, int width, int height)
Wrapper around av_image_copy() to workaround the limitation that the conversion from uint8_t * const ...
Definition: imgutils.h:184
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
d3d12va_create_staging_buffer_resource
static int d3d12va_create_staging_buffer_resource(AVHWFramesContext *ctx, D3D12_RESOURCE_STATES states, ID3D12Resource **ppResource, int download)
Definition: hwcontext_d3d12va.c:106
pix_fmt
enum AVPixelFormat pix_fmt
Definition: hwcontext_d3d12va.c:68
AVD3D12VADeviceContext::lock
void(* lock)(void *lock_ctx)
Callbacks for locking.
Definition: hwcontext_d3d12va.h:75
AV_PIX_FMT_P010
#define AV_PIX_FMT_P010
Definition: pixfmt.h:552
D3D12VAFramesContext::luma_component_size
UINT luma_component_size
Definition: hwcontext_d3d12va.c:51
desc
const char * desc
Definition: libsvtav1.c:79
mem.h
AVBufferRef
A reference to a data buffer.
Definition: buffer.h:82
hwcontext_internal.h
FFALIGN
#define FFALIGN(x, a)
Definition: macros.h:78
PFN_CREATE_DXGI_FACTORY2
HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY2)(UINT Flags, REFIID riid, void **ppFactory)
Definition: hwcontext_d3d12va.c:37
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:34
imgutils.h
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:482
hwcontext.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
HWContextType
Definition: hwcontext_internal.h:29
AVD3D12VASyncContext::fence_value
uint64_t fence_value
The fence value used for sync.
Definition: hwcontext_d3d12va.h:99
D3D12VAFramesContext::command_queue
ID3D12CommandQueue * command_queue
Definition: hwcontext_d3d12va.c:47
hwcontext_d3d12va_internal.h
d3d12va_create_helper_objects
static int d3d12va_create_helper_objects(AVHWFramesContext *ctx)
Definition: hwcontext_d3d12va.c:134
src
#define src
Definition: vp8dsp.c:248
d3d12va_transfer_get_formats
static int d3d12va_transfer_get_formats(AVHWFramesContext *ctx, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats)
Definition: hwcontext_d3d12va.c:332
w32dlfcn.h
d3d12va_device_uninit
static void d3d12va_device_uninit(AVHWDeviceContext *hwdev)
Definition: hwcontext_d3d12va.c:606
av_get_pix_fmt_name
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition: pixdesc.c:3090