2 * This file is part of Libav.
4 * Libav is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * Libav is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with Libav; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 #include <dxgidebug.h>
35 #include "hwcontext.h"
36 #include "hwcontext_d3d11va.h"
37 #include "hwcontext_internal.h"
43 typedef HRESULT(WINAPI
*PFN_CREATE_DXGI_FACTORY
)(REFIID riid
, void **ppFactory
);
45 static AVOnce functions_loaded
= AV_ONCE_INIT
;
47 static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory
;
48 static PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice
;
50 static av_cold
void load_functions(void)
53 // We let these "leak" - this is fine, as unloading has no great benefit, and
54 // Windows will mark a DLL as loaded forever if its internal refcount overflows
55 // from too many LoadLibrary calls.
56 HANDLE d3dlib
, dxgilib
;
58 d3dlib
= LoadLibrary("d3d11.dll");
59 dxgilib
= LoadLibrary("dxgi.dll");
60 if (!d3dlib
|| !dxgilib
)
63 mD3D11CreateDevice
= (PFN_D3D11_CREATE_DEVICE
) GetProcAddress(d3dlib
, "D3D11CreateDevice");
64 mCreateDXGIFactory
= (PFN_CREATE_DXGI_FACTORY
) GetProcAddress(dxgilib
, "CreateDXGIFactory");
66 // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't available,
67 // only CreateDXGIFactory1
68 mD3D11CreateDevice
= (PFN_D3D11_CREATE_DEVICE
) D3D11CreateDevice
;
69 mCreateDXGIFactory
= (PFN_CREATE_DXGI_FACTORY
) CreateDXGIFactory1
;
73 typedef struct D3D11VAFramesContext
{
78 ID3D11Texture2D
*staging_texture
;
79 } D3D11VAFramesContext
;
82 DXGI_FORMAT d3d_format
;
83 enum AVPixelFormat pix_fmt
;
84 } supported_formats
[] = {
85 { DXGI_FORMAT_NV12
, AV_PIX_FMT_NV12
},
86 { DXGI_FORMAT_P010
, AV_PIX_FMT_P010
},
87 // Special opaque formats. The pix_fmt is merely a place holder, as the
88 // opaque format cannot be accessed directly.
89 { DXGI_FORMAT_420_OPAQUE
, AV_PIX_FMT_YUV420P
},
92 static void d3d11va_default_lock(void *ctx
)
94 WaitForSingleObjectEx(ctx
, INFINITE
, FALSE
);
97 static void d3d11va_default_unlock(void *ctx
)
102 static void d3d11va_frames_uninit(AVHWFramesContext
*ctx
)
104 AVD3D11VAFramesContext
*frames_hwctx
= ctx
->hwctx
;
105 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
107 if (frames_hwctx
->texture
)
108 ID3D11Texture2D_Release(frames_hwctx
->texture
);
109 frames_hwctx
->texture
= NULL
;
111 if (s
->staging_texture
)
112 ID3D11Texture2D_Release(s
->staging_texture
);
113 s
->staging_texture
= NULL
;
116 static void free_texture(void *opaque
, uint8_t *data
)
118 ID3D11Texture2D_Release((ID3D11Texture2D
*)opaque
);
121 static AVBufferRef
*wrap_texture_buf(ID3D11Texture2D
*tex
, int index
)
124 AVD3D11FrameDescriptor
*desc
= av_mallocz(sizeof(*desc
));
126 ID3D11Texture2D_Release(tex
);
133 buf
= av_buffer_create((uint8_t *)desc
, sizeof(desc
), free_texture
, tex
, 0);
135 ID3D11Texture2D_Release(tex
);
143 static AVBufferRef
*d3d11va_alloc_single(AVHWFramesContext
*ctx
)
145 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
146 AVD3D11VAFramesContext
*hwctx
= ctx
->hwctx
;
147 AVD3D11VADeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
149 ID3D11Texture2D
*tex
;
150 D3D11_TEXTURE2D_DESC texDesc
= {
152 .Height
= ctx
->height
,
155 .SampleDesc
= { .Count
= 1 },
157 .Usage
= D3D11_USAGE_DEFAULT
,
158 .BindFlags
= hwctx
->BindFlags
,
159 .MiscFlags
= hwctx
->MiscFlags
,
162 hr
= ID3D11Device_CreateTexture2D(device_hwctx
->device
, &texDesc
, NULL
, &tex
);
164 av_log(ctx
, AV_LOG_ERROR
, "Could not create the texture (%lx)\n", (long)hr
);
168 return wrap_texture_buf(tex
, 0);
171 static AVBufferRef
*d3d11va_pool_alloc(void *opaque
, int size
)
173 AVHWFramesContext
*ctx
= (AVHWFramesContext
*)opaque
;
174 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
175 AVD3D11VAFramesContext
*hwctx
= ctx
->hwctx
;
176 D3D11_TEXTURE2D_DESC texDesc
;
179 return d3d11va_alloc_single(ctx
);
181 ID3D11Texture2D_GetDesc(hwctx
->texture
, &texDesc
);
183 if (s
->nb_surfaces_used
>= texDesc
.ArraySize
) {
184 av_log(ctx
, AV_LOG_ERROR
, "Static surface pool size exceeded.\n");
188 ID3D11Texture2D_AddRef(hwctx
->texture
);
189 return wrap_texture_buf(hwctx
->texture
, s
->nb_surfaces_used
++);
192 static int d3d11va_frames_init(AVHWFramesContext
*ctx
)
194 AVD3D11VAFramesContext
*hwctx
= ctx
->hwctx
;
195 AVD3D11VADeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
196 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
200 D3D11_TEXTURE2D_DESC texDesc
;
202 for (i
= 0; i
< FF_ARRAY_ELEMS(supported_formats
); i
++) {
203 if (ctx
->sw_format
== supported_formats
[i
].pix_fmt
) {
204 s
->format
= supported_formats
[i
].d3d_format
;
208 if (i
== FF_ARRAY_ELEMS(supported_formats
)) {
209 av_log(ctx
, AV_LOG_ERROR
, "Unsupported pixel format: %s\n",
210 av_get_pix_fmt_name(ctx
->sw_format
));
211 return AVERROR(EINVAL
);
214 texDesc
= (D3D11_TEXTURE2D_DESC
){
216 .Height
= ctx
->height
,
219 .SampleDesc
= { .Count
= 1 },
220 .ArraySize
= ctx
->initial_pool_size
,
221 .Usage
= D3D11_USAGE_DEFAULT
,
222 .BindFlags
= hwctx
->BindFlags
,
223 .MiscFlags
= hwctx
->MiscFlags
,
226 if (hwctx
->texture
) {
227 D3D11_TEXTURE2D_DESC texDesc2
;
228 ID3D11Texture2D_GetDesc(hwctx
->texture
, &texDesc2
);
230 if (texDesc
.Width
!= texDesc2
.Width
||
231 texDesc
.Height
!= texDesc2
.Height
||
232 texDesc
.Format
!= texDesc2
.Format
) {
233 av_log(ctx
, AV_LOG_ERROR
, "User-provided texture has mismatching parameters\n");
234 return AVERROR(EINVAL
);
236 } else if (texDesc
.ArraySize
> 0) {
237 hr
= ID3D11Device_CreateTexture2D(device_hwctx
->device
, &texDesc
, NULL
, &hwctx
->texture
);
239 av_log(ctx
, AV_LOG_ERROR
, "Could not create the texture (%lx)\n", (long)hr
);
240 return AVERROR_UNKNOWN
;
244 ctx
->internal
->pool_internal
= av_buffer_pool_init2(sizeof(AVD3D11FrameDescriptor
),
245 ctx
, d3d11va_pool_alloc
, NULL
);
246 if (!ctx
->internal
->pool_internal
)
247 return AVERROR(ENOMEM
);
252 static int d3d11va_get_buffer(AVHWFramesContext
*ctx
, AVFrame
*frame
)
254 AVD3D11FrameDescriptor
*desc
;
256 frame
->buf
[0] = av_buffer_pool_get(ctx
->pool
);
258 return AVERROR(ENOMEM
);
260 desc
= (AVD3D11FrameDescriptor
*)frame
->buf
[0]->data
;
262 frame
->data
[0] = (uint8_t *)desc
->texture
;
263 frame
->data
[1] = (uint8_t *)desc
->index
;
264 frame
->format
= AV_PIX_FMT_D3D11
;
265 frame
->width
= ctx
->width
;
266 frame
->height
= ctx
->height
;
271 static int d3d11va_transfer_get_formats(AVHWFramesContext
*ctx
,
272 enum AVHWFrameTransferDirection dir
,
273 enum AVPixelFormat
**formats
)
275 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
276 enum AVPixelFormat
*fmts
;
278 fmts
= av_malloc_array(2, sizeof(*fmts
));
280 return AVERROR(ENOMEM
);
282 fmts
[0] = ctx
->sw_format
;
283 fmts
[1] = AV_PIX_FMT_NONE
;
285 // Don't signal support for opaque formats. Actual access would fail.
286 if (s
->format
== DXGI_FORMAT_420_OPAQUE
)
287 fmts
[0] = AV_PIX_FMT_NONE
;
294 static int d3d11va_create_staging_texture(AVHWFramesContext
*ctx
)
296 AVD3D11VADeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
297 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
299 D3D11_TEXTURE2D_DESC texDesc
= {
301 .Height
= ctx
->height
,
304 .SampleDesc
= { .Count
= 1 },
306 .Usage
= D3D11_USAGE_STAGING
,
307 .CPUAccessFlags
= D3D11_CPU_ACCESS_READ
| D3D11_CPU_ACCESS_WRITE
,
310 hr
= ID3D11Device_CreateTexture2D(device_hwctx
->device
, &texDesc
, NULL
, &s
->staging_texture
);
312 av_log(ctx
, AV_LOG_ERROR
, "Could not create the staging texture (%lx)\n", (long)hr
);
313 return AVERROR_UNKNOWN
;
319 static void fill_texture_ptrs(uint8_t *data
[4], int linesize
[4],
320 AVHWFramesContext
*ctx
,
321 D3D11_TEXTURE2D_DESC
*desc
,
322 D3D11_MAPPED_SUBRESOURCE
*map
)
326 for (i
= 0; i
< 4; i
++)
327 linesize
[i
] = map
->RowPitch
;
329 av_image_fill_pointers(data
, ctx
->sw_format
, desc
->Height
,
330 (uint8_t*)map
->pData
, linesize
);
333 static int d3d11va_transfer_data(AVHWFramesContext
*ctx
, AVFrame
*dst
,
336 AVD3D11VADeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
337 D3D11VAFramesContext
*s
= ctx
->internal
->priv
;
338 int download
= src
->format
== AV_PIX_FMT_D3D11
;
339 const AVFrame
*frame
= download
? src
: dst
;
340 const AVFrame
*other
= download
? dst
: src
;
341 // (The interface types are compatible.)
342 ID3D11Resource
*texture
= (ID3D11Resource
*)(ID3D11Texture2D
*)frame
->data
[0];
343 int index
= (intptr_t)frame
->data
[1];
344 ID3D11Resource
*staging
;
345 int w
= FFMIN(dst
->width
, src
->width
);
346 int h
= FFMIN(dst
->height
, src
->height
);
347 uint8_t *map_data
[4];
349 D3D11_TEXTURE2D_DESC desc
;
350 D3D11_MAPPED_SUBRESOURCE map
;
353 if (frame
->hw_frames_ctx
->data
!= (uint8_t *)ctx
|| other
->format
!= ctx
->sw_format
)
354 return AVERROR(EINVAL
);
356 device_hwctx
->lock(device_hwctx
->lock_ctx
);
358 if (!s
->staging_texture
) {
359 int res
= d3d11va_create_staging_texture(ctx
);
364 staging
= (ID3D11Resource
*)s
->staging_texture
;
366 ID3D11Texture2D_GetDesc(s
->staging_texture
, &desc
);
369 ID3D11DeviceContext_CopySubresourceRegion(device_hwctx
->device_context
,
371 texture
, index
, NULL
);
373 hr
= ID3D11DeviceContext_Map(device_hwctx
->device_context
,
374 staging
, 0, D3D11_MAP_READ
, 0, &map
);
378 fill_texture_ptrs(map_data
, map_linesize
, ctx
, &desc
, &map
);
380 av_image_copy(dst
->data
, dst
->linesize
, map_data
, map_linesize
,
381 ctx
->sw_format
, w
, h
);
383 ID3D11DeviceContext_Unmap(device_hwctx
->device_context
, staging
, 0);
385 hr
= ID3D11DeviceContext_Map(device_hwctx
->device_context
,
386 staging
, 0, D3D11_MAP_WRITE
, 0, &map
);
390 fill_texture_ptrs(map_data
, map_linesize
, ctx
, &desc
, &map
);
392 av_image_copy(map_data
, map_linesize
, src
->data
, src
->linesize
,
393 ctx
->sw_format
, w
, h
);
395 ID3D11DeviceContext_Unmap(device_hwctx
->device_context
, staging
, 0);
397 ID3D11DeviceContext_CopySubresourceRegion(device_hwctx
->device_context
,
398 texture
, index
, 0, 0, 0,
402 device_hwctx
->unlock(device_hwctx
->lock_ctx
);
406 av_log(ctx
, AV_LOG_ERROR
, "Unable to lock D3D11VA surface (%lx)\n", (long)hr
);
407 device_hwctx
->unlock(device_hwctx
->lock_ctx
);
408 return AVERROR_UNKNOWN
;
411 static int d3d11va_device_init(AVHWDeviceContext
*hwdev
)
413 AVD3D11VADeviceContext
*device_hwctx
= hwdev
->hwctx
;
416 if (!device_hwctx
->lock
) {
417 device_hwctx
->lock_ctx
= CreateMutex(NULL
, 0, NULL
);
418 if (device_hwctx
->lock_ctx
== INVALID_HANDLE_VALUE
) {
419 av_log(NULL
, AV_LOG_ERROR
, "Failed to create a mutex\n");
420 return AVERROR(EINVAL
);
422 device_hwctx
->lock
= d3d11va_default_lock
;
423 device_hwctx
->unlock
= d3d11va_default_unlock
;
426 if (!device_hwctx
->device_context
) {
427 ID3D11Device_GetImmediateContext(device_hwctx
->device
, &device_hwctx
->device_context
);
428 if (!device_hwctx
->device_context
)
429 return AVERROR_UNKNOWN
;
432 if (!device_hwctx
->video_device
) {
433 hr
= ID3D11DeviceContext_QueryInterface(device_hwctx
->device
, &IID_ID3D11VideoDevice
,
434 (void **)&device_hwctx
->video_device
);
436 return AVERROR_UNKNOWN
;
439 if (!device_hwctx
->video_context
) {
440 hr
= ID3D11DeviceContext_QueryInterface(device_hwctx
->device_context
, &IID_ID3D11VideoContext
,
441 (void **)&device_hwctx
->video_context
);
443 return AVERROR_UNKNOWN
;
449 static void d3d11va_device_uninit(AVHWDeviceContext
*hwdev
)
451 AVD3D11VADeviceContext
*device_hwctx
= hwdev
->hwctx
;
453 if (device_hwctx
->device
)
454 ID3D11Device_Release(device_hwctx
->device
);
456 if (device_hwctx
->device_context
)
457 ID3D11DeviceContext_Release(device_hwctx
->device_context
);
459 if (device_hwctx
->video_device
)
460 ID3D11VideoDevice_Release(device_hwctx
->video_device
);
462 if (device_hwctx
->video_context
)
463 ID3D11VideoContext_Release(device_hwctx
->video_context
);
465 if (device_hwctx
->lock
== d3d11va_default_lock
)
466 CloseHandle(device_hwctx
->lock_ctx
);
469 static int d3d11va_device_create(AVHWDeviceContext
*ctx
, const char *device
,
470 AVDictionary
*opts
, int flags
)
472 AVD3D11VADeviceContext
*device_hwctx
= ctx
->hwctx
;
475 IDXGIAdapter
*pAdapter
= NULL
;
476 ID3D10Multithread
*pMultithread
;
477 UINT creationFlags
= D3D11_CREATE_DEVICE_VIDEO_SUPPORT
;
478 int is_debug
= !!av_dict_get(opts
, "debug", NULL
, 0);
481 // (On UWP we can't check this.)
483 if (!LoadLibrary("d3d11_1sdklayers.dll"))
488 creationFlags
|= D3D11_CREATE_DEVICE_DEBUG
;
490 if ((ret
= ff_thread_once(&functions_loaded
, load_functions
)) != 0)
491 return AVERROR_UNKNOWN
;
492 if (!mD3D11CreateDevice
|| !mCreateDXGIFactory
) {
493 av_log(ctx
, AV_LOG_ERROR
, "Failed to load D3D11 library or its functions\n");
494 return AVERROR_UNKNOWN
;
498 IDXGIFactory2
*pDXGIFactory
;
499 hr
= mCreateDXGIFactory(&IID_IDXGIFactory2
, (void **)&pDXGIFactory
);
501 int adapter
= atoi(device
);
502 if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory
, adapter
, &pAdapter
)))
504 IDXGIFactory2_Release(pDXGIFactory
);
508 hr
= mD3D11CreateDevice(pAdapter
, pAdapter
? D3D_DRIVER_TYPE_UNKNOWN
: D3D_DRIVER_TYPE_HARDWARE
, NULL
, creationFlags
, NULL
, 0,
509 D3D11_SDK_VERSION
, &device_hwctx
->device
, NULL
, NULL
);
511 IDXGIAdapter_Release(pAdapter
);
513 av_log(ctx
, AV_LOG_ERROR
, "Failed to create Direct3D device (%lx)\n", (long)hr
);
514 return AVERROR_UNKNOWN
;
517 hr
= ID3D11Device_QueryInterface(device_hwctx
->device
, &IID_ID3D10Multithread
, (void **)&pMultithread
);
519 ID3D10Multithread_SetMultithreadProtected(pMultithread
, TRUE
);
520 ID3D10Multithread_Release(pMultithread
);
523 #if !HAVE_UWP && HAVE_DXGIDEBUG_H
525 HANDLE dxgidebug_dll
= LoadLibrary("dxgidebug.dll");
527 HRESULT (WINAPI
* pf_DXGIGetDebugInterface
)(const GUID
*riid
, void **ppDebug
)
528 = (void *)GetProcAddress(dxgidebug_dll
, "DXGIGetDebugInterface");
529 if (pf_DXGIGetDebugInterface
) {
530 IDXGIDebug
*dxgi_debug
= NULL
;
531 hr
= pf_DXGIGetDebugInterface(&IID_IDXGIDebug
, (void**)&dxgi_debug
);
532 if (SUCCEEDED(hr
) && dxgi_debug
)
533 IDXGIDebug_ReportLiveObjects(dxgi_debug
, DXGI_DEBUG_ALL
, DXGI_DEBUG_RLO_ALL
);
542 const HWContextType ff_hwcontext_type_d3d11va
= {
543 .type
= AV_HWDEVICE_TYPE_D3D11VA
,
546 .device_hwctx_size
= sizeof(AVD3D11VADeviceContext
),
547 .frames_hwctx_size
= sizeof(AVD3D11VAFramesContext
),
548 .frames_priv_size
= sizeof(D3D11VAFramesContext
),
550 .device_create
= d3d11va_device_create
,
551 .device_init
= d3d11va_device_init
,
552 .device_uninit
= d3d11va_device_uninit
,
553 .frames_init
= d3d11va_frames_init
,
554 .frames_uninit
= d3d11va_frames_uninit
,
555 .frames_get_buffer
= d3d11va_get_buffer
,
556 .transfer_get_formats
= d3d11va_transfer_get_formats
,
557 .transfer_data_to
= d3d11va_transfer_data
,
558 .transfer_data_from
= d3d11va_transfer_data
,
560 .pix_fmts
= (const enum AVPixelFormat
[]){ AV_PIX_FMT_D3D11
, AV_PIX_FMT_NONE
},