hwcontext_vaapi: Fix build with libva 2.0
[ffmpeg.git] / libavutil / hwcontext_d3d11va.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg 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.
8  *
9  * FFmpeg 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.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "config.h"
20
21 #include <windows.h>
22
23 // Include thread.h before redefining _WIN32_WINNT, to get
24 // the right implementation for AVOnce
25 #include "thread.h"
26
27 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
28 #undef _WIN32_WINNT
29 #define _WIN32_WINNT 0x0600
30 #endif
31 #define COBJMACROS
32
33 #include <initguid.h>
34 #include <d3d11.h>
35 #include <dxgi1_2.h>
36
37 #if HAVE_DXGIDEBUG_H
38 #include <dxgidebug.h>
39 #endif
40
41 #include "avassert.h"
42 #include "common.h"
43 #include "hwcontext.h"
44 #include "hwcontext_d3d11va.h"
45 #include "hwcontext_internal.h"
46 #include "imgutils.h"
47 #include "pixdesc.h"
48 #include "pixfmt.h"
49
50 typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
51
52 static AVOnce functions_loaded = AV_ONCE_INIT;
53
54 static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
55 static PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice;
56
57 static av_cold void load_functions(void)
58 {
59 #if !HAVE_UWP
60     // We let these "leak" - this is fine, as unloading has no great benefit, and
61     // Windows will mark a DLL as loaded forever if its internal refcount overflows
62     // from too many LoadLibrary calls.
63     HANDLE d3dlib, dxgilib;
64
65     d3dlib  = LoadLibrary("d3d11.dll");
66     dxgilib = LoadLibrary("dxgi.dll");
67     if (!d3dlib || !dxgilib)
68         return;
69
70     mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
71     mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
72 #else
73     // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't available,
74     // only CreateDXGIFactory1
75     mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
76     mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
77 #endif
78 }
79
80 typedef struct D3D11VAFramesContext {
81     int nb_surfaces_used;
82
83     DXGI_FORMAT format;
84
85     ID3D11Texture2D *staging_texture;
86 } D3D11VAFramesContext;
87
88 static const struct {
89     DXGI_FORMAT d3d_format;
90     enum AVPixelFormat pix_fmt;
91 } supported_formats[] = {
92     { DXGI_FORMAT_NV12,         AV_PIX_FMT_NV12 },
93     { DXGI_FORMAT_P010,         AV_PIX_FMT_P010 },
94     // Special opaque formats. The pix_fmt is merely a place holder, as the
95     // opaque format cannot be accessed directly.
96     { DXGI_FORMAT_420_OPAQUE,   AV_PIX_FMT_YUV420P },
97 };
98
99 static void d3d11va_default_lock(void *ctx)
100 {
101     WaitForSingleObjectEx(ctx, INFINITE, FALSE);
102 }
103
104 static void d3d11va_default_unlock(void *ctx)
105 {
106     ReleaseMutex(ctx);
107 }
108
109 static void d3d11va_frames_uninit(AVHWFramesContext *ctx)
110 {
111     AVD3D11VAFramesContext *frames_hwctx = ctx->hwctx;
112     D3D11VAFramesContext *s = ctx->internal->priv;
113
114     if (frames_hwctx->texture)
115         ID3D11Texture2D_Release(frames_hwctx->texture);
116     frames_hwctx->texture = NULL;
117
118     if (s->staging_texture)
119         ID3D11Texture2D_Release(s->staging_texture);
120     s->staging_texture = NULL;
121 }
122
123 static void free_texture(void *opaque, uint8_t *data)
124 {
125     ID3D11Texture2D_Release((ID3D11Texture2D *)opaque);
126     av_free(data);
127 }
128
129 static AVBufferRef *wrap_texture_buf(ID3D11Texture2D *tex, int index)
130 {
131     AVBufferRef *buf;
132     AVD3D11FrameDescriptor *desc = av_mallocz(sizeof(*desc));
133     if (!desc) {
134         ID3D11Texture2D_Release(tex);
135         return NULL;
136     }
137
138     desc->texture = tex;
139     desc->index   = index;
140
141     buf = av_buffer_create((uint8_t *)desc, sizeof(desc), free_texture, tex, 0);
142     if (!buf) {
143         ID3D11Texture2D_Release(tex);
144         av_free(desc);
145         return NULL;
146     }
147
148     return buf;
149 }
150
151 static AVBufferRef *d3d11va_alloc_single(AVHWFramesContext *ctx)
152 {
153     D3D11VAFramesContext       *s = ctx->internal->priv;
154     AVD3D11VAFramesContext *hwctx = ctx->hwctx;
155     AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
156     HRESULT hr;
157     ID3D11Texture2D *tex;
158     D3D11_TEXTURE2D_DESC texDesc = {
159         .Width      = ctx->width,
160         .Height     = ctx->height,
161         .MipLevels  = 1,
162         .Format     = s->format,
163         .SampleDesc = { .Count = 1 },
164         .ArraySize  = 1,
165         .Usage      = D3D11_USAGE_DEFAULT,
166         .BindFlags  = hwctx->BindFlags,
167         .MiscFlags  = hwctx->MiscFlags,
168     };
169
170     hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &tex);
171     if (FAILED(hr)) {
172         av_log(ctx, AV_LOG_ERROR, "Could not create the texture (%lx)\n", (long)hr);
173         return NULL;
174     }
175
176     return wrap_texture_buf(tex, 0);
177 }
178
179 static AVBufferRef *d3d11va_pool_alloc(void *opaque, int size)
180 {
181     AVHWFramesContext        *ctx = (AVHWFramesContext*)opaque;
182     D3D11VAFramesContext       *s = ctx->internal->priv;
183     AVD3D11VAFramesContext *hwctx = ctx->hwctx;
184     D3D11_TEXTURE2D_DESC  texDesc;
185
186     if (!hwctx->texture)
187         return d3d11va_alloc_single(ctx);
188
189     ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc);
190
191     if (s->nb_surfaces_used >= texDesc.ArraySize) {
192         av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
193         return NULL;
194     }
195
196     ID3D11Texture2D_AddRef(hwctx->texture);
197     return wrap_texture_buf(hwctx->texture, s->nb_surfaces_used++);
198 }
199
200 static int d3d11va_frames_init(AVHWFramesContext *ctx)
201 {
202     AVD3D11VAFramesContext *hwctx        = ctx->hwctx;
203     AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
204     D3D11VAFramesContext              *s = ctx->internal->priv;
205
206     int i;
207     HRESULT hr;
208     D3D11_TEXTURE2D_DESC texDesc;
209
210     for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
211         if (ctx->sw_format == supported_formats[i].pix_fmt) {
212             s->format = supported_formats[i].d3d_format;
213             break;
214         }
215     }
216     if (i == FF_ARRAY_ELEMS(supported_formats)) {
217         av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n",
218                av_get_pix_fmt_name(ctx->sw_format));
219         return AVERROR(EINVAL);
220     }
221
222     texDesc = (D3D11_TEXTURE2D_DESC){
223         .Width      = ctx->width,
224         .Height     = ctx->height,
225         .MipLevels  = 1,
226         .Format     = s->format,
227         .SampleDesc = { .Count = 1 },
228         .ArraySize  = ctx->initial_pool_size,
229         .Usage      = D3D11_USAGE_DEFAULT,
230         .BindFlags  = hwctx->BindFlags,
231         .MiscFlags  = hwctx->MiscFlags,
232     };
233
234     if (hwctx->texture) {
235         D3D11_TEXTURE2D_DESC texDesc2;
236         ID3D11Texture2D_GetDesc(hwctx->texture, &texDesc2);
237
238         if (texDesc.Width != texDesc2.Width ||
239             texDesc.Height != texDesc2.Height ||
240             texDesc.Format != texDesc2.Format) {
241             av_log(ctx, AV_LOG_ERROR, "User-provided texture has mismatching parameters\n");
242             return AVERROR(EINVAL);
243         }
244     } else if (texDesc.ArraySize > 0) {
245         hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &hwctx->texture);
246         if (FAILED(hr)) {
247             av_log(ctx, AV_LOG_ERROR, "Could not create the texture (%lx)\n", (long)hr);
248             return AVERROR_UNKNOWN;
249         }
250     }
251
252     ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(AVD3D11FrameDescriptor),
253                                                         ctx, d3d11va_pool_alloc, NULL);
254     if (!ctx->internal->pool_internal)
255         return AVERROR(ENOMEM);
256
257     return 0;
258 }
259
260 static int d3d11va_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
261 {
262     AVD3D11FrameDescriptor *desc;
263
264     frame->buf[0] = av_buffer_pool_get(ctx->pool);
265     if (!frame->buf[0])
266         return AVERROR(ENOMEM);
267
268     desc = (AVD3D11FrameDescriptor *)frame->buf[0]->data;
269
270     frame->data[0] = (uint8_t *)desc->texture;
271     frame->data[1] = (uint8_t *)desc->index;
272     frame->format  = AV_PIX_FMT_D3D11;
273     frame->width   = ctx->width;
274     frame->height  = ctx->height;
275
276     return 0;
277 }
278
279 static int d3d11va_transfer_get_formats(AVHWFramesContext *ctx,
280                                         enum AVHWFrameTransferDirection dir,
281                                         enum AVPixelFormat **formats)
282 {
283     D3D11VAFramesContext *s = ctx->internal->priv;
284     enum AVPixelFormat *fmts;
285
286     fmts = av_malloc_array(2, sizeof(*fmts));
287     if (!fmts)
288         return AVERROR(ENOMEM);
289
290     fmts[0] = ctx->sw_format;
291     fmts[1] = AV_PIX_FMT_NONE;
292
293     // Don't signal support for opaque formats. Actual access would fail.
294     if (s->format == DXGI_FORMAT_420_OPAQUE)
295         fmts[0] = AV_PIX_FMT_NONE;
296
297     *formats = fmts;
298
299     return 0;
300 }
301
302 static int d3d11va_create_staging_texture(AVHWFramesContext *ctx)
303 {
304     AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
305     D3D11VAFramesContext              *s = ctx->internal->priv;
306     HRESULT hr;
307     D3D11_TEXTURE2D_DESC texDesc = {
308         .Width          = ctx->width,
309         .Height         = ctx->height,
310         .MipLevels      = 1,
311         .Format         = s->format,
312         .SampleDesc     = { .Count = 1 },
313         .ArraySize      = 1,
314         .Usage          = D3D11_USAGE_STAGING,
315         .CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE,
316     };
317
318     hr = ID3D11Device_CreateTexture2D(device_hwctx->device, &texDesc, NULL, &s->staging_texture);
319     if (FAILED(hr)) {
320         av_log(ctx, AV_LOG_ERROR, "Could not create the staging texture (%lx)\n", (long)hr);
321         return AVERROR_UNKNOWN;
322     }
323
324     return 0;
325 }
326
327 static void fill_texture_ptrs(uint8_t *data[4], int linesize[4],
328                               AVHWFramesContext *ctx,
329                               D3D11_TEXTURE2D_DESC *desc,
330                               D3D11_MAPPED_SUBRESOURCE *map)
331 {
332     int i;
333
334     for (i = 0; i < 4; i++)
335         linesize[i] = map->RowPitch;
336
337     av_image_fill_pointers(data, ctx->sw_format, desc->Height,
338                            (uint8_t*)map->pData, linesize);
339 }
340
341 static int d3d11va_transfer_data(AVHWFramesContext *ctx, AVFrame *dst,
342                                  const AVFrame *src)
343 {
344     AVD3D11VADeviceContext *device_hwctx = ctx->device_ctx->hwctx;
345     D3D11VAFramesContext              *s = ctx->internal->priv;
346     int download = src->format == AV_PIX_FMT_D3D11;
347     const AVFrame *frame = download ? src : dst;
348     const AVFrame *other = download ? dst : src;
349     // (The interface types are compatible.)
350     ID3D11Resource *texture = (ID3D11Resource *)(ID3D11Texture2D *)frame->data[0];
351     int index = (intptr_t)frame->data[1];
352     ID3D11Resource *staging;
353     int w = FFMIN(dst->width,  src->width);
354     int h = FFMIN(dst->height, src->height);
355     uint8_t *map_data[4];
356     int map_linesize[4];
357     D3D11_TEXTURE2D_DESC desc;
358     D3D11_MAPPED_SUBRESOURCE map;
359     HRESULT hr;
360
361     if (frame->hw_frames_ctx->data != (uint8_t *)ctx || other->format != ctx->sw_format)
362         return AVERROR(EINVAL);
363
364     device_hwctx->lock(device_hwctx->lock_ctx);
365
366     if (!s->staging_texture) {
367         int res = d3d11va_create_staging_texture(ctx);
368         if (res < 0)
369             return res;
370     }
371
372     staging = (ID3D11Resource *)s->staging_texture;
373
374     ID3D11Texture2D_GetDesc(s->staging_texture, &desc);
375
376     if (download) {
377         ID3D11DeviceContext_CopySubresourceRegion(device_hwctx->device_context,
378                                                   staging, 0, 0, 0, 0,
379                                                   texture, index, NULL);
380
381         hr = ID3D11DeviceContext_Map(device_hwctx->device_context,
382                                      staging, 0, D3D11_MAP_READ, 0, &map);
383         if (FAILED(hr))
384             goto map_failed;
385
386         fill_texture_ptrs(map_data, map_linesize, ctx, &desc, &map);
387
388         av_image_copy(dst->data, dst->linesize, map_data, map_linesize,
389                       ctx->sw_format, w, h);
390
391         ID3D11DeviceContext_Unmap(device_hwctx->device_context, staging, 0);
392     } else {
393         hr = ID3D11DeviceContext_Map(device_hwctx->device_context,
394                                      staging, 0, D3D11_MAP_WRITE, 0, &map);
395         if (FAILED(hr))
396             goto map_failed;
397
398         fill_texture_ptrs(map_data, map_linesize, ctx, &desc, &map);
399
400         av_image_copy(map_data, map_linesize, src->data, src->linesize,
401                       ctx->sw_format, w, h);
402
403         ID3D11DeviceContext_Unmap(device_hwctx->device_context, staging, 0);
404
405         ID3D11DeviceContext_CopySubresourceRegion(device_hwctx->device_context,
406                                                   texture, index, 0, 0, 0,
407                                                   staging, 0, NULL);
408     }
409
410     device_hwctx->unlock(device_hwctx->lock_ctx);
411     return 0;
412
413 map_failed:
414     av_log(ctx, AV_LOG_ERROR, "Unable to lock D3D11VA surface (%lx)\n", (long)hr);
415     device_hwctx->unlock(device_hwctx->lock_ctx);
416     return AVERROR_UNKNOWN;
417 }
418
419 static int d3d11va_device_init(AVHWDeviceContext *hwdev)
420 {
421     AVD3D11VADeviceContext *device_hwctx = hwdev->hwctx;
422     HRESULT hr;
423
424     if (!device_hwctx->lock) {
425         device_hwctx->lock_ctx = CreateMutex(NULL, 0, NULL);
426         if (device_hwctx->lock_ctx == INVALID_HANDLE_VALUE) {
427             av_log(NULL, AV_LOG_ERROR, "Failed to create a mutex\n");
428             return AVERROR(EINVAL);
429         }
430         device_hwctx->lock   = d3d11va_default_lock;
431         device_hwctx->unlock = d3d11va_default_unlock;
432     }
433
434     if (!device_hwctx->device_context) {
435         ID3D11Device_GetImmediateContext(device_hwctx->device, &device_hwctx->device_context);
436         if (!device_hwctx->device_context)
437             return AVERROR_UNKNOWN;
438     }
439
440     if (!device_hwctx->video_device) {
441         hr = ID3D11DeviceContext_QueryInterface(device_hwctx->device, &IID_ID3D11VideoDevice,
442                                                 (void **)&device_hwctx->video_device);
443         if (FAILED(hr))
444             return AVERROR_UNKNOWN;
445     }
446
447     if (!device_hwctx->video_context) {
448         hr = ID3D11DeviceContext_QueryInterface(device_hwctx->device_context, &IID_ID3D11VideoContext,
449                                                 (void **)&device_hwctx->video_context);
450         if (FAILED(hr))
451             return AVERROR_UNKNOWN;
452     }
453
454     return 0;
455 }
456
457 static void d3d11va_device_uninit(AVHWDeviceContext *hwdev)
458 {
459     AVD3D11VADeviceContext *device_hwctx = hwdev->hwctx;
460
461     if (device_hwctx->device)
462         ID3D11Device_Release(device_hwctx->device);
463
464     if (device_hwctx->device_context)
465         ID3D11DeviceContext_Release(device_hwctx->device_context);
466
467     if (device_hwctx->video_device)
468         ID3D11VideoDevice_Release(device_hwctx->video_device);
469
470     if (device_hwctx->video_context)
471         ID3D11VideoContext_Release(device_hwctx->video_context);
472
473     if (device_hwctx->lock == d3d11va_default_lock)
474         CloseHandle(device_hwctx->lock_ctx);
475 }
476
477 static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
478                                  AVDictionary *opts, int flags)
479 {
480     AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
481
482     HRESULT hr;
483     IDXGIAdapter           *pAdapter = NULL;
484     ID3D10Multithread      *pMultithread;
485     UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
486     int is_debug       = !!av_dict_get(opts, "debug", NULL, 0);
487     int ret;
488
489     // (On UWP we can't check this.)
490 #if !HAVE_UWP
491     if (!LoadLibrary("d3d11_1sdklayers.dll"))
492         is_debug = 0;
493 #endif
494
495     if (is_debug)
496         creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
497
498     if ((ret = ff_thread_once(&functions_loaded, load_functions)) != 0)
499         return AVERROR_UNKNOWN;
500     if (!mD3D11CreateDevice || !mCreateDXGIFactory) {
501         av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library or its functions\n");
502         return AVERROR_UNKNOWN;
503     }
504
505     if (device) {
506         IDXGIFactory2 *pDXGIFactory;
507         hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&pDXGIFactory);
508         if (SUCCEEDED(hr)) {
509             int adapter = atoi(device);
510             if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter)))
511                 pAdapter = NULL;
512             IDXGIFactory2_Release(pDXGIFactory);
513         }
514     }
515
516     hr = mD3D11CreateDevice(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
517                    D3D11_SDK_VERSION, &device_hwctx->device, NULL, NULL);
518     if (pAdapter)
519         IDXGIAdapter_Release(pAdapter);
520     if (FAILED(hr)) {
521         av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device (%lx)\n", (long)hr);
522         return AVERROR_UNKNOWN;
523     }
524
525     hr = ID3D11Device_QueryInterface(device_hwctx->device, &IID_ID3D10Multithread, (void **)&pMultithread);
526     if (SUCCEEDED(hr)) {
527         ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
528         ID3D10Multithread_Release(pMultithread);
529     }
530
531 #if !HAVE_UWP && HAVE_DXGIDEBUG_H
532     if (is_debug) {
533         HANDLE dxgidebug_dll = LoadLibrary("dxgidebug.dll");
534         if (dxgidebug_dll) {
535             HRESULT (WINAPI  * pf_DXGIGetDebugInterface)(const GUID *riid, void **ppDebug)
536                 = (void *)GetProcAddress(dxgidebug_dll, "DXGIGetDebugInterface");
537             if (pf_DXGIGetDebugInterface) {
538                 IDXGIDebug *dxgi_debug = NULL;
539                 hr = pf_DXGIGetDebugInterface(&IID_IDXGIDebug, (void**)&dxgi_debug);
540                 if (SUCCEEDED(hr) && dxgi_debug)
541                     IDXGIDebug_ReportLiveObjects(dxgi_debug, DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL);
542             }
543         }
544     }
545 #endif
546
547     return 0;
548 }
549
550 const HWContextType ff_hwcontext_type_d3d11va = {
551     .type                 = AV_HWDEVICE_TYPE_D3D11VA,
552     .name                 = "D3D11VA",
553
554     .device_hwctx_size    = sizeof(AVD3D11VADeviceContext),
555     .frames_hwctx_size    = sizeof(AVD3D11VAFramesContext),
556     .frames_priv_size     = sizeof(D3D11VAFramesContext),
557
558     .device_create        = d3d11va_device_create,
559     .device_init          = d3d11va_device_init,
560     .device_uninit        = d3d11va_device_uninit,
561     .frames_init          = d3d11va_frames_init,
562     .frames_uninit        = d3d11va_frames_uninit,
563     .frames_get_buffer    = d3d11va_get_buffer,
564     .transfer_get_formats = d3d11va_transfer_get_formats,
565     .transfer_data_to     = d3d11va_transfer_data,
566     .transfer_data_from   = d3d11va_transfer_data,
567
568     .pix_fmts             = (const enum AVPixelFormat[]){ AV_PIX_FMT_D3D11, AV_PIX_FMT_NONE },
569 };