[FFmpeg-devel] [PATCH] lavc/qsv: adding DX11 support
Anton Khirnov
anton at khirnov.net
Fri Jan 10 12:41:07 EET 2020
Quoting Artem Galin (2019-12-23 19:31:04)
> This enables DX11 support for QSV with higher priority than DX9.
> In case of multiple GPUs configuration, DX9 API does not allow to get
> access to QSV device in some cases - headless.
> Implementation based on DX11 resolves that restriction by enumerating list of available GPUs and finding device with QSV support.
>
> Signed-off-by: Artem Galin <artem.galin at gmail.com>
> ---
> libavcodec/qsv.c | 13 +++--
> libavcodec/qsv_internal.h | 1 +
> libavutil/hwcontext_d3d11va.c | 57 +++++++++++++++++++++-
> libavutil/hwcontext_qsv.c | 89 +++++++++++++++++++++++++++++++----
> libavutil/hwcontext_qsv.h | 1 +
> 5 files changed, 147 insertions(+), 14 deletions(-)
>
> diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c
> index b00e427435..33ac7a3373 100644
> --- a/libavcodec/qsv.c
> +++ b/libavcodec/qsv.c
> @@ -414,7 +414,7 @@ static int ff_qsv_set_display_handle(AVCodecContext *avctx, QSVSession *qs)
> int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs,
> const char *load_plugins, int gpu_copy)
> {
> - mfxIMPL impl = MFX_IMPL_AUTO_ANY;
> + mfxIMPL impl = MFX_IMPL_AUTO_ANY | MFX_IMPL_VIA_D3D11;
> mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } };
> mfxInitParam init_par = { MFX_IMPL_AUTO_ANY };
>
> @@ -504,6 +504,7 @@ static AVBufferRef *qsv_create_mids(AVBufferRef *hw_frames_ref)
> for (i = 0; i < nb_surfaces; i++) {
> QSVMid *mid = &mids[i];
> mid->handle = frames_hwctx->surfaces[i].Data.MemId;
> + mid->texture = frames_hwctx->texture;
> mid->hw_frames_ref = hw_frames_ref1;
> }
>
> @@ -713,7 +714,13 @@ static mfxStatus qsv_frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
> static mfxStatus qsv_frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl)
> {
> QSVMid *qsv_mid = (QSVMid*)mid;
> - *hdl = qsv_mid->handle;
> + if (qsv_mid->texture) {
> + mfxHDLPair *pair = (mfxHDLPair*)hdl;
> + pair->first = qsv_mid->texture;
> + pair->second = qsv_mid->handle;
> + } else {
> + *hdl = qsv_mid->handle;
> + }
> return MFX_ERR_NONE;
> }
>
> @@ -723,8 +730,8 @@ int ff_qsv_init_session_device(AVCodecContext *avctx, mfxSession *psession,
> {
> static const mfxHandleType handle_types[] = {
> MFX_HANDLE_VA_DISPLAY,
> - MFX_HANDLE_D3D9_DEVICE_MANAGER,
> MFX_HANDLE_D3D11_DEVICE,
> + MFX_HANDLE_D3D9_DEVICE_MANAGER,
> };
> AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)device_ref->data;
> AVQSVDeviceContext *device_hwctx = device_ctx->hwctx;
> diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h
> index 37559270e5..09425fb431 100644
> --- a/libavcodec/qsv_internal.h
> +++ b/libavcodec/qsv_internal.h
> @@ -60,6 +60,7 @@
>
> typedef struct QSVMid {
> AVBufferRef *hw_frames_ref;
> + void *texture;
> mfxHDL handle;
>
> AVFrame *locked_frame;
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> index 6670c47579..a08479fd96 100644
> --- a/libavutil/hwcontext_d3d11va.c
> +++ b/libavutil/hwcontext_d3d11va.c
> @@ -244,7 +244,7 @@ static int d3d11va_frames_init(AVHWFramesContext *ctx)
> return AVERROR(EINVAL);
> }
>
> - texDesc = (D3D11_TEXTURE2D_DESC){
> + texDesc = (D3D11_TEXTURE2D_DESC) {
> .Width = ctx->width,
> .Height = ctx->height,
> .MipLevels = 1,
> @@ -510,6 +510,46 @@ static void d3d11va_device_uninit(AVHWDeviceContext *hwdev)
> }
> }
>
> +static int d3d11va_device_find_qsv_adapter(AVHWDeviceContext *ctx, UINT creationFlags)
> +{
> + HRESULT hr;
> + IDXGIAdapter *adapter = NULL;
> + int adapter_id = 0;
> + int vendor_id = 0x8086;
> + IDXGIFactory2 *factory;
> + hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&factory);
> + while (IDXGIFactory2_EnumAdapters(factory, adapter_id++, &adapter) != DXGI_ERROR_NOT_FOUND)
> + {
> + ID3D11Device* device = NULL;
> + DXGI_ADAPTER_DESC adapter_desc;
> +
> + hr = mD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, creationFlags, NULL, 0, D3D11_SDK_VERSION, &device, NULL, NULL);
> + if (FAILED(hr)) {
> + av_log(ctx, AV_LOG_ERROR, "D3D11CreateDevice returned error\n");
> + continue;
> + }
> +
> + hr = IDXGIAdapter2_GetDesc(adapter, &adapter_desc);
> + if (FAILED(hr)) {
> + av_log(ctx, AV_LOG_ERROR, "IDXGIAdapter2_GetDesc returned error\n");
> + continue;
> + }
> +
> + if(device)
> + ID3D11Device_Release(device);
> +
> + if (adapter)
> + IDXGIAdapter_Release(adapter);
> +
> + if (adapter_desc.VendorId == vendor_id) {
> + IDXGIFactory2_Release(factory);
> + return adapter_id - 1;
> + }
> + }
> + IDXGIFactory2_Release(factory);
> + return -1;
> +}
> +
> static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
> AVDictionary *opts, int flags)
> {
> @@ -519,7 +559,9 @@ static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
> IDXGIAdapter *pAdapter = NULL;
> ID3D10Multithread *pMultithread;
> UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
> + int adapter = -1;
> int is_debug = !!av_dict_get(opts, "debug", NULL, 0);
> + int is_qsv = !!av_dict_get(opts, "d3d11va_qsv", NULL, 0);
> int ret;
>
> // (On UWP we can't check this.)
> @@ -538,11 +580,22 @@ static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
> return AVERROR_UNKNOWN;
> }
>
> + if (is_qsv) {
> + adapter = d3d11va_device_find_qsv_adapter(ctx, creationFlags);
> + if (adapter < 0) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to find DX11 adapter with QSV support\n");
> + return AVERROR_UNKNOWN;
> + }
> + }
> +
> if (device) {
> + adapter = atoi(device);
> + }
> +
> + if (adapter >= 0) {
> IDXGIFactory2 *pDXGIFactory;
> hr = mCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&pDXGIFactory);
> if (SUCCEEDED(hr)) {
> - int adapter = atoi(device);
> if (FAILED(IDXGIFactory2_EnumAdapters(pDXGIFactory, adapter, &pAdapter)))
> pAdapter = NULL;
> IDXGIFactory2_Release(pDXGIFactory);
> diff --git a/libavutil/hwcontext_qsv.c b/libavutil/hwcontext_qsv.c
> index b1b67400de..ae4247abf2 100644
> --- a/libavutil/hwcontext_qsv.c
> +++ b/libavutil/hwcontext_qsv.c
> @@ -27,9 +27,13 @@
> #include <pthread.h>
> #endif
>
> +#define COBJMACROS
> #if CONFIG_VAAPI
> #include "hwcontext_vaapi.h"
> #endif
> +#if CONFIG_D3D11VA
> +#include "hwcontext_d3d11va.h"
> +#endif
> #if CONFIG_DXVA2
> #include "hwcontext_dxva2.h"
> #endif
> @@ -89,6 +93,9 @@ static const struct {
> #if CONFIG_VAAPI
> { MFX_HANDLE_VA_DISPLAY, AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI },
> #endif
> +#if CONFIG_D3D11VA
> + { MFX_HANDLE_D3D11_DEVICE, AV_HWDEVICE_TYPE_D3D11VA, AV_PIX_FMT_D3D11 },
> +#endif
> #if CONFIG_DXVA2
> { MFX_HANDLE_D3D9_DEVICE_MANAGER, AV_HWDEVICE_TYPE_DXVA2, AV_PIX_FMT_DXVA2_VLD },
> #endif
> @@ -229,9 +236,17 @@ static int qsv_init_child_ctx(AVHWFramesContext *ctx)
> child_device_hwctx->display = (VADisplay)device_priv->handle;
> }
> #endif
> +#if CONFIG_D3D11VA
> + if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
> + AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx;
> + ID3D11Device_AddRef((ID3D11Device*)device_priv->handle);
> + child_device_hwctx->device = (ID3D11Device*)device_priv->handle;
> + }
> +#endif
> #if CONFIG_DXVA2
> if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
> AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx;
> + IDirect3DDeviceManager9_AddRef((IDirect3DDeviceManager9*)device_priv->handle);
> child_device_hwctx->devmgr = (IDirect3DDeviceManager9*)device_priv->handle;
> }
> #endif
> @@ -255,6 +270,16 @@ static int qsv_init_child_ctx(AVHWFramesContext *ctx)
> child_frames_ctx->width = FFALIGN(ctx->width, 16);
> child_frames_ctx->height = FFALIGN(ctx->height, 16);
>
> +#if CONFIG_D3D11VA
> + if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
> + AVD3D11VAFramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
> + child_frames_hwctx->MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
> + if (hwctx->frame_type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)
> + child_frames_hwctx->BindFlags = D3D11_BIND_RENDER_TARGET ;
> + else
> + child_frames_hwctx->BindFlags = D3D11_BIND_DECODER;
> + }
> +#endif
> #if CONFIG_DXVA2
> if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
> AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
> @@ -279,6 +304,18 @@ static int qsv_init_child_ctx(AVHWFramesContext *ctx)
> hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
> }
> #endif
> +#if CONFIG_D3D11VA
> + if (child_device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) {
> + AVD3D11VAFramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
> + hwctx->texture = child_frames_hwctx->texture;
> + for (i = 0; i < ctx->initial_pool_size; i++)
> + s->surfaces_internal[i].Data.MemId = (mfxMemId)(int64_t)i;
> + if (child_frames_hwctx->BindFlags == D3D11_BIND_DECODER)
> + hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
> + else
> + hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET;
> + }
> +#endif
> #if CONFIG_DXVA2
> if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) {
> AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx;
> @@ -421,7 +458,16 @@ static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr)
>
> static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl)
> {
> - *hdl = mid;
> + AVHWFramesContext *ctx = pthis;
> + AVQSVFramesContext *hwctx = ctx->hwctx;
> +
> + if (hwctx->texture) {
> + mfxHDLPair *pair = (mfxHDLPair*)hdl;
> + pair->first = hwctx->texture;
> + pair->second = mid;
> + } else {
> + *hdl = mid;
> + }
> return MFX_ERR_NONE;
> }
>
> @@ -492,7 +538,7 @@ static int qsv_init_internal_session(AVHWFramesContext *ctx,
>
> err = MFXVideoVPP_Init(*session, &par);
> if (err != MFX_ERR_NONE) {
> - av_log(ctx, AV_LOG_VERBOSE, "Error opening the internal VPP session."
> + av_log(ctx, AV_LOG_ERROR, "Error opening the internal VPP session."
> "Surface upload/download will not be possible\n");
> MFXClose(*session);
> *session = NULL;
> @@ -1074,7 +1120,7 @@ static void qsv_device_free(AVHWDeviceContext *ctx)
> av_freep(&priv);
> }
>
> -static mfxIMPL choose_implementation(const char *device)
> +static mfxIMPL choose_implementation(const char *device, enum AVHWDeviceType child_device_type)
> {
> static const struct {
> const char *name;
> @@ -1103,6 +1149,10 @@ static mfxIMPL choose_implementation(const char *device)
> impl = strtol(device, NULL, 0);
> }
>
> + if ( (child_device_type == AV_HWDEVICE_TYPE_D3D11VA) && (impl != MFX_IMPL_SOFTWARE) ) {
> + impl |= MFX_IMPL_VIA_D3D11;
> + }
> +
> return impl;
> }
>
> @@ -1129,6 +1179,15 @@ static int qsv_device_derive_from_child(AVHWDeviceContext *ctx,
> }
> break;
> #endif
> +#if CONFIG_D3D11VA
> + case AV_HWDEVICE_TYPE_D3D11VA:
> + {
> + AVD3D11VADeviceContext *child_device_hwctx = child_device_ctx->hwctx;
> + handle_type = MFX_HANDLE_D3D11_DEVICE;
> + handle = (mfxHDL)child_device_hwctx->device;
> + }
> + break;
> +#endif
> #if CONFIG_DXVA2
> case AV_HWDEVICE_TYPE_DXVA2:
> {
> @@ -1226,23 +1285,35 @@ static int qsv_device_create(AVHWDeviceContext *ctx, const char *device,
> // possible, even when multiple devices and drivers are available.
> av_dict_set(&child_device_opts, "kernel_driver", "i915", 0);
> av_dict_set(&child_device_opts, "driver", "iHD", 0);
> - } else if (CONFIG_DXVA2)
> + } else if (CONFIG_D3D11VA) {
> + child_device_type = AV_HWDEVICE_TYPE_D3D11VA;
> + av_dict_set(&child_device_opts, "d3d11va_qsv", "enabled", 0);
> + } else if (CONFIG_DXVA2) {
> child_device_type = AV_HWDEVICE_TYPE_DXVA2;
> - else {
> + } else {
> av_log(ctx, AV_LOG_ERROR, "No supported child device type is enabled\n");
> return AVERROR(ENOSYS);
> }
>
> ret = av_hwdevice_ctx_create(&priv->child_device_ctx, child_device_type,
> e ? e->value : NULL, child_device_opts, 0);
> -
> av_dict_free(&child_device_opts);
> - if (ret < 0)
> - return ret;
> + if (ret < 0) {
> + if (CONFIG_DXVA2 && (child_device_type == AV_HWDEVICE_TYPE_D3D11VA)) {
> + // in case of d3d11va fail, try one more chance to create device via dxva2
> + child_device_type = AV_HWDEVICE_TYPE_DXVA2;
> + child_device_opts = NULL;
> + ret = av_hwdevice_ctx_create(&priv->child_device_ctx, child_device_type,
> + e ? e->value : NULL, child_device_opts, 0);
> + }
> + if (ret < 0) {
> + return ret;
> + }
> + }
>
> child_device = (AVHWDeviceContext*)priv->child_device_ctx->data;
>
> - impl = choose_implementation(device);
> + impl = choose_implementation(device, child_device_type);
>
> return qsv_device_derive_from_child(ctx, impl, child_device, 0);
> }
> diff --git a/libavutil/hwcontext_qsv.h b/libavutil/hwcontext_qsv.h
> index b98d611cfc..f5a9691949 100644
> --- a/libavutil/hwcontext_qsv.h
> +++ b/libavutil/hwcontext_qsv.h
> @@ -42,6 +42,7 @@ typedef struct AVQSVDeviceContext {
> typedef struct AVQSVFramesContext {
> mfxFrameSurface1 *surfaces;
> int nb_surfaces;
> + void *texture;
This is a public struct, so new fields have to be added at the end,
otherwise it's an ABI break.
Also, API changes require a minor library version bump and an APIchanges
entry. Some documentation would be nice too.
--
Anton Khirnov
More information about the ffmpeg-devel
mailing list