d3d11va: Link directly to dxgi.dll and d3d11.dll functions if LoadLibrary is unavailable

Message ID 1497380202-30673-1-git-send-email-martin@martin.st
State Committed
Commit fd1ffa1f10e940165035ccb79d4a6523da196062
Headers show

Commit Message

Martin Storsjö June 13, 2017, 6:56 p.m.
When targeting the UWP API subset, the LoadLibrary function is not
available (and the fallback, LoadPackagedLibrary, can't be used to
load system DLLs). In these cases, link directly to the functions
in the DLLs instead of trying to load them dynamically at runtime.
---
Requiring both d3d11.dll and dxgi.dll now, simplifying it slightly.

Moved the configure check as suggested by Diego, and renamed the
createD3D function as suggested by wm4.

Still untested at runtime, I've only compile tested it (both codepaths,
with MSVC, but not with mingw).
---
 configure                     |  4 +++
 libavutil/hwcontext_d3d11va.c | 80 +++++++++++++++++++++++++------------------
 2 files changed, 51 insertions(+), 33 deletions(-)

Comments

wm4 June 13, 2017, 8:14 p.m. | #1
On Tue, 13 Jun 2017 21:56:42 +0300
Martin Storsjö <martin@martin.st> wrote:

> When targeting the UWP API subset, the LoadLibrary function is not
> available (and the fallback, LoadPackagedLibrary, can't be used to
> load system DLLs). In these cases, link directly to the functions
> in the DLLs instead of trying to load them dynamically at runtime.
> ---
> Requiring both d3d11.dll and dxgi.dll now, simplifying it slightly.
> 
> Moved the configure check as suggested by Diego, and renamed the
> createD3D function as suggested by wm4.
> 
> Still untested at runtime, I've only compile tested it (both codepaths,
> with MSVC, but not with mingw).
> ---
>  configure                     |  4 +++
>  libavutil/hwcontext_d3d11va.c | 80 +++++++++++++++++++++++++------------------
>  2 files changed, 51 insertions(+), 33 deletions(-)
> 
> diff --git a/configure b/configure
> index 019902c..223106f 100755
> --- a/configure
> +++ b/configure
> @@ -4885,6 +4885,10 @@ if enabled libxcb; then
>          check_pkg_config libxcb_xfixes xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image
>  fi
>  
> +# d3d11va requires linking directly to dxgi and d3d11 if not building for
> +# the desktop api partition
> +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
> +
>  enabled vaapi && require vaapi va/va.h vaInitialize -lva
>  
>  enabled vaapi &&
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> index 3940502..5432dd8 100644
> --- a/libavutil/hwcontext_d3d11va.c
> +++ b/libavutil/hwcontext_d3d11va.c
> @@ -18,6 +18,10 @@
>  
>  #include <windows.h>
>  
> +// Include thread.h before redefining _WIN32_WINNT, to get
> +// the right implementation for AVOnce
> +#include "thread.h"
> +
>  #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
>  #undef _WIN32_WINNT
>  #define _WIN32_WINNT 0x0600
> @@ -39,6 +43,34 @@
>  
>  typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
>  
> +static AVOnce functions_loaded = AV_ONCE_INIT;
> +
> +static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
> +static PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice;
> +
> +static av_cold void load_functions(void)
> +{
> +#if HAVE_LOADLIBRARY
> +    // We let these "leak" - this is fine, as unloading has no great benefit, and
> +    // Windows will mark a DLL as loaded forever if its internal refcount overflows
> +    // from too many LoadLibrary calls.
> +    HANDLE d3dlib, dxgilib;
> +
> +    d3dlib  = LoadLibrary("d3d11.dll");
> +    dxgilib = LoadLibrary("dxgi.dll");
> +    if (!d3dlib || !dxgilib)
> +        return;
> +
> +    mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
> +    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
> +#else
> +    // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't available,
> +    // only CreateDXGIFactory1
> +    mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
> +    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
> +#endif
> +}
> +
>  typedef struct D3D11VAFramesContext {
>      int nb_surfaces_used;
>  
> @@ -407,50 +439,32 @@ static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
>                                   AVDictionary *opts, int flags)
>  {
>      AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
> -    HANDLE d3dlib;
>  
>      HRESULT hr;
> -    PFN_D3D11_CREATE_DEVICE createD3D;
>      IDXGIAdapter           *pAdapter = NULL;
>      ID3D10Multithread      *pMultithread;
>      UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
> +    int ret;
>  
> -    if (device) {
> -        PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
> -        HMODULE dxgilib = LoadLibrary("dxgi.dll");
> -        if (!dxgilib)
> -            return AVERROR_UNKNOWN;
> -
> -        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
> -        if (mCreateDXGIFactory) {
> -            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);
> -            }
> -        }
> -        FreeLibrary(dxgilib);
> -    }
> -
> -    // We let this "leak" - this is fine, as unloading has no great benefit, and
> -    // Windows will mark a DLL as loaded forever if its internal refcount overflows
> -    // from too many LoadLibrary calls.
> -    d3dlib = LoadLibrary("d3d11.dll");
> -    if (!d3dlib) {
> -        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
> +    if ((ret = ff_thread_once(&functions_loaded, load_functions)) != 0)
> +        return AVERROR_UNKNOWN;
> +    if (!mD3D11CreateDevice || !mCreateDXGIFactory) {
> +        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library or its functions\n");
>          return AVERROR_UNKNOWN;
>      }
>  
> -    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
> -    if (!createD3D) {
> -        av_log(ctx, AV_LOG_ERROR, "Failed to locate D3D11CreateDevice\n");
> -        return AVERROR_UNKNOWN;
> +    if (device) {
> +        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);
> +        }
>      }
>  
> -    hr = createD3D(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
> +    hr = mD3D11CreateDevice(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
>                     D3D11_SDK_VERSION, &device_hwctx->device, NULL, NULL);
>      if (pAdapter)
>          IDXGIAdapter_Release(pAdapter);

LGTM (I didn't test it yet either.)

Patch

diff --git a/configure b/configure
index 019902c..223106f 100755
--- a/configure
+++ b/configure
@@ -4885,6 +4885,10 @@  if enabled libxcb; then
         check_pkg_config libxcb_xfixes xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image
 fi
 
+# d3d11va requires linking directly to dxgi and d3d11 if not building for
+# the desktop api partition
+enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
+
 enabled vaapi && require vaapi va/va.h vaInitialize -lva
 
 enabled vaapi &&
diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
index 3940502..5432dd8 100644
--- a/libavutil/hwcontext_d3d11va.c
+++ b/libavutil/hwcontext_d3d11va.c
@@ -18,6 +18,10 @@ 
 
 #include <windows.h>
 
+// Include thread.h before redefining _WIN32_WINNT, to get
+// the right implementation for AVOnce
+#include "thread.h"
+
 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
 #undef _WIN32_WINNT
 #define _WIN32_WINNT 0x0600
@@ -39,6 +43,34 @@ 
 
 typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
 
+static AVOnce functions_loaded = AV_ONCE_INIT;
+
+static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
+static PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice;
+
+static av_cold void load_functions(void)
+{
+#if HAVE_LOADLIBRARY
+    // We let these "leak" - this is fine, as unloading has no great benefit, and
+    // Windows will mark a DLL as loaded forever if its internal refcount overflows
+    // from too many LoadLibrary calls.
+    HANDLE d3dlib, dxgilib;
+
+    d3dlib  = LoadLibrary("d3d11.dll");
+    dxgilib = LoadLibrary("dxgi.dll");
+    if (!d3dlib || !dxgilib)
+        return;
+
+    mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
+    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
+#else
+    // In UWP (which lacks LoadLibrary), CreateDXGIFactory isn't available,
+    // only CreateDXGIFactory1
+    mD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
+    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
+#endif
+}
+
 typedef struct D3D11VAFramesContext {
     int nb_surfaces_used;
 
@@ -407,50 +439,32 @@  static int d3d11va_device_create(AVHWDeviceContext *ctx, const char *device,
                                  AVDictionary *opts, int flags)
 {
     AVD3D11VADeviceContext *device_hwctx = ctx->hwctx;
-    HANDLE d3dlib;
 
     HRESULT hr;
-    PFN_D3D11_CREATE_DEVICE createD3D;
     IDXGIAdapter           *pAdapter = NULL;
     ID3D10Multithread      *pMultithread;
     UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+    int ret;
 
-    if (device) {
-        PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
-        HMODULE dxgilib = LoadLibrary("dxgi.dll");
-        if (!dxgilib)
-            return AVERROR_UNKNOWN;
-
-        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
-        if (mCreateDXGIFactory) {
-            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);
-            }
-        }
-        FreeLibrary(dxgilib);
-    }
-
-    // We let this "leak" - this is fine, as unloading has no great benefit, and
-    // Windows will mark a DLL as loaded forever if its internal refcount overflows
-    // from too many LoadLibrary calls.
-    d3dlib = LoadLibrary("d3d11.dll");
-    if (!d3dlib) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library\n");
+    if ((ret = ff_thread_once(&functions_loaded, load_functions)) != 0)
+        return AVERROR_UNKNOWN;
+    if (!mD3D11CreateDevice || !mCreateDXGIFactory) {
+        av_log(ctx, AV_LOG_ERROR, "Failed to load D3D11 library or its functions\n");
         return AVERROR_UNKNOWN;
     }
 
-    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
-    if (!createD3D) {
-        av_log(ctx, AV_LOG_ERROR, "Failed to locate D3D11CreateDevice\n");
-        return AVERROR_UNKNOWN;
+    if (device) {
+        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);
+        }
     }
 
-    hr = createD3D(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
+    hr = mD3D11CreateDevice(pAdapter, pAdapter ? D3D_DRIVER_TYPE_UNKNOWN : D3D_DRIVER_TYPE_HARDWARE, NULL, creationFlags, NULL, 0,
                    D3D11_SDK_VERSION, &device_hwctx->device, NULL, NULL);
     if (pAdapter)
         IDXGIAdapter_Release(pAdapter);