[FFmpeg-devel] [PATCH] lavc/qsv: adding DX11 support

Artem Galin artem.galin at gmail.com
Thu Jan 23 18:54:43 EET 2020


On Fri, 10 Jan 2020 at 10:41, Anton Khirnov <anton at khirnov.net> wrote:

> 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.
>
>
Done.
Sorry for the thread duplication, something wrong with replying to this
thread with new patch version.
Updated patch is available by link below:
https://patchwork.ffmpeg.org/project/ffmpeg/patch/20200123151813.31739-1-artem.galin@gmail.com/

Thanks,
Artem.


More information about the ffmpeg-devel mailing list