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

Message ID 1497357072-10072-1-git-send-email-martin@martin.st
State Superseded
Headers show

Commit Message

Martin Storsjö June 13, 2017, 12:31 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.
---
I kept the behaviour where we don't error out if dxgi.dll is missing,
if the device parameter is null, although I'm not sure if the
distinction is relevant.

I changed checks for UWP into checks for the LoadLibrary function,
which in general feels more straightforward.

Do note that this uses the CreateDXGIFactory1 function if LoadLibrary
isn't available, since the unsuffixed version of the function isn't
available in UWP.

I don't have any actual environment to test this in, so I'd appreciate
if someone can do that.
---
 configure                     |  4 +++
 libavutil/hwcontext_d3d11va.c | 79 +++++++++++++++++++++++++------------------
 2 files changed, 51 insertions(+), 32 deletions(-)

Comments

wm4 June 13, 2017, 12:50 p.m. | #1
On Tue, 13 Jun 2017 15:31:12 +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.
> ---
> I kept the behaviour where we don't error out if dxgi.dll is missing,
> if the device parameter is null, although I'm not sure if the
> distinction is relevant.
> 
> I changed checks for UWP into checks for the LoadLibrary function,
> which in general feels more straightforward.
> 
> Do note that this uses the CreateDXGIFactory1 function if LoadLibrary
> isn't available, since the unsuffixed version of the function isn't
> available in UWP.
> 
> I don't have any actual environment to test this in, so I'd appreciate
> if someone can do that.
> ---
>  configure                     |  4 +++
>  libavutil/hwcontext_d3d11va.c | 79 +++++++++++++++++++++++++------------------
>  2 files changed, 51 insertions(+), 32 deletions(-)
> 
> diff --git a/configure b/configure
> index 019902c..e7556e0 100755
> --- a/configure
> +++ b/configure
> @@ -5159,6 +5159,10 @@ esac
>  
>  enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
>  
> +# d3d11va requires linking directly to dxgi and d3d11 if not building for
> +# the desktop api partition
> +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
> +
>  check_deps $CONFIG_LIST       \
>             $CONFIG_EXTRA      \
>             $HAVE_LIST         \
> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
> index 3940502..4de674d 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,33 @@
>  
>  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 createD3D;

I think following a consistent naming convention would be good. I like
"m" + real API name.

> +
> +static av_cold void load_functions(void)
> +{
> +#if HAVE_LOADLIBRARY

The UWP define wasn't sufficient?

> +    // 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)
> +        return;
> +
> +    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
> +    if (dxgilib)
> +        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
> +#else
> +    createD3D = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
> +    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
> +#endif
> +}
> +
>  typedef struct D3D11VAFramesContext {
>      int nb_surfaces_used;
>  
> @@ -407,47 +438,31 @@ 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 ret;

Does this really return an AVERROR code?

> +    if (!createD3D) {
> +        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;
> +        if (!mCreateDXGIFactory)
> +            return AVERROR_UNKNOWN;
> +        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,

Otherwise LGTM.
Diego Biurrun June 13, 2017, 12:55 p.m. | #2
On Tue, Jun 13, 2017 at 03:31:12PM +0300, Martin Storsjö 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.
> ---
> I kept the behaviour where we don't error out if dxgi.dll is missing,
> if the device parameter is null, although I'm not sure if the
> distinction is relevant.
> 
> I changed checks for UWP into checks for the LoadLibrary function,
> which in general feels more straightforward.
> 
> Do note that this uses the CreateDXGIFactory1 function if LoadLibrary
> isn't available, since the unsuffixed version of the function isn't
> available in UWP.
> 
> I don't have any actual environment to test this in, so I'd appreciate
> if someone can do that.
> ---
>  configure                     |  4 +++
>  libavutil/hwcontext_d3d11va.c | 79 +++++++++++++++++++++++++------------------
>  2 files changed, 51 insertions(+), 32 deletions(-)

I'll be bold and ask the naive question: why not skip LoadLibrary in all
cases?

> --- a/configure
> +++ b/configure
> @@ -5159,6 +5159,10 @@ esac
>  
>  enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
>  
> +# d3d11va requires linking directly to dxgi and d3d11 if not building for
> +# the desktop api partition
> +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
> +
>  check_deps $CONFIG_LIST       \
>             $CONFIG_EXTRA      \
>             $HAVE_LIST         \

That's not a good place for the hack. Problem is, I don't see a good place
anywhere. I'll try to think of something.

> --- 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

The comment confuses me, I don't see where AVOnce depends on _WIN32_WINNT..

Diego
Martin Storsjö June 13, 2017, 12:59 p.m. | #3
On Tue, 13 Jun 2017, wm4 wrote:

> On Tue, 13 Jun 2017 15:31:12 +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.
>> ---
>> I kept the behaviour where we don't error out if dxgi.dll is missing,
>> if the device parameter is null, although I'm not sure if the
>> distinction is relevant.
>> 
>> I changed checks for UWP into checks for the LoadLibrary function,
>> which in general feels more straightforward.
>> 
>> Do note that this uses the CreateDXGIFactory1 function if LoadLibrary
>> isn't available, since the unsuffixed version of the function isn't
>> available in UWP.
>> 
>> I don't have any actual environment to test this in, so I'd appreciate
>> if someone can do that.
>> ---
>>  configure                     |  4 +++
>>  libavutil/hwcontext_d3d11va.c | 79 +++++++++++++++++++++++++------------------
>>  2 files changed, 51 insertions(+), 32 deletions(-)
>> 
>> diff --git a/configure b/configure
>> index 019902c..e7556e0 100755
>> --- a/configure
>> +++ b/configure
>> @@ -5159,6 +5159,10 @@ esac
>>
>>  enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
>> 
>> +# d3d11va requires linking directly to dxgi and d3d11 if not building for
>> +# the desktop api partition
>> +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
>> +
>>  check_deps $CONFIG_LIST       \
>>             $CONFIG_EXTRA      \
>>             $HAVE_LIST         \
>> diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
>> index 3940502..4de674d 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,33 @@
>>
>>  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 createD3D;
>
> I think following a consistent naming convention would be good. I like
> "m" + real API name.

So mD3D11CreateDevice?

>> +
>> +static av_cold void load_functions(void)
>> +{
>> +#if HAVE_LOADLIBRARY
>
> The UWP define wasn't sufficient?

It was sufficient, but IMO this condition is simpler (especially for 
configure). But with CreateDXGIFactory vs CreateDXGIFactory1, having it 
specific for UWP might be better of course.

>> +    // 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)
>> +        return;
>> +
>> +    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
>> +    if (dxgilib)
>> +        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");

Was this intentional that we should try to continue even if dxgi.dll 
doesn't exist, in case the device parameter is null? Or was that just 
coincidental behaviour in the previous form of the code?

>> +#else
>> +    createD3D = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
>> +    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
>> +#endif
>> +}
>> +
>>  typedef struct D3D11VAFramesContext {
>>      int nb_surfaces_used;
>> 
>> @@ -407,47 +438,31 @@ 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 ret;
>
> Does this really return an AVERROR code?

Good catch - no it doesn't, changed to return AVERROR_UNKNOWN.

// Martin
Martin Storsjö June 13, 2017, 1 p.m. | #4
On Tue, 13 Jun 2017, Diego Biurrun wrote:

> On Tue, Jun 13, 2017 at 03:31:12PM +0300, Martin Storsjö 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.
>> ---
>> I kept the behaviour where we don't error out if dxgi.dll is missing,
>> if the device parameter is null, although I'm not sure if the
>> distinction is relevant.
>> 
>> I changed checks for UWP into checks for the LoadLibrary function,
>> which in general feels more straightforward.
>> 
>> Do note that this uses the CreateDXGIFactory1 function if LoadLibrary
>> isn't available, since the unsuffixed version of the function isn't
>> available in UWP.
>> 
>> I don't have any actual environment to test this in, so I'd appreciate
>> if someone can do that.
>> ---
>>  configure                     |  4 +++
>>  libavutil/hwcontext_d3d11va.c | 79 +++++++++++++++++++++++++------------------
>>  2 files changed, 51 insertions(+), 32 deletions(-)
>
> I'll be bold and ask the naive question: why not skip LoadLibrary in all
> cases?

Because then you can't have a single build which works for older windows 
versions while using it on demand on newer platforms if available. It's 
pretty much a must-have for anybody doing binary distributions for desktop 
windows.

>
>> --- a/configure
>> +++ b/configure
>> @@ -5159,6 +5159,10 @@ esac
>>
>>  enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
>> 
>> +# d3d11va requires linking directly to dxgi and d3d11 if not building for
>> +# the desktop api partition
>> +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
>> +
>>  check_deps $CONFIG_LIST       \
>>             $CONFIG_EXTRA      \
>>             $HAVE_LIST         \
>
> That's not a good place for the hack. Problem is, I don't see a good place
> anywhere. I'll try to think of something.
>
>> --- 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
>
> The comment confuses me, I don't see where AVOnce depends on _WIN32_WINNT..

compat/w32pthreads.h - look for pthread_once.

// Martin
wm4 June 13, 2017, 1:09 p.m. | #5
On Tue, 13 Jun 2017 15:59:00 +0300 (EEST)
Martin Storsjö <martin@martin.st> wrote:

> On Tue, 13 Jun 2017, wm4 wrote:
> 
> > On Tue, 13 Jun 2017 15:31:12 +0300
> > Martin Storsjö <martin@martin.st> wrote:
> >  

> >> +static AVOnce functions_loaded = AV_ONCE_INIT;
> >> +
> >> +static PFN_CREATE_DXGI_FACTORY mCreateDXGIFactory;
> >> +static PFN_D3D11_CREATE_DEVICE createD3D;  
> >
> > I think following a consistent naming convention would be good. I like
> > "m" + real API name.  
> 
> So mD3D11CreateDevice?

Yep.

> >> +
> >> +static av_cold void load_functions(void)
> >> +{
> >> +#if HAVE_LOADLIBRARY  
> >
> > The UWP define wasn't sufficient?  
> 
> It was sufficient, but IMO this condition is simpler (especially for 
> configure). But with CreateDXGIFactory vs CreateDXGIFactory1, having it 
> specific for UWP might be better of course.

I don't really see a need for the LoadLibrary test (in fact it could
succeed on UWP if you have this winstorecompat thing active?), but I'm
not really against it either.

> >> +    // 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)
> >> +        return;
> >> +
> >> +    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
> >> +    if (dxgilib)
> >> +        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");  
> 
> Was this intentional that we should try to continue even if dxgi.dll 
> doesn't exist, in case the device parameter is null? Or was that just 
> coincidental behaviour in the previous form of the code?

There is no reason to fail in that case, but on the other hand it
doesn't matter. If DXGI is unavailable, you're going to have worse
problems.
Diego Biurrun June 13, 2017, 2:24 p.m. | #6
On Tue, Jun 13, 2017 at 02:55:34PM +0200, Diego Biurrun wrote:
> On Tue, Jun 13, 2017 at 03:31:12PM +0300, Martin Storsjö wrote:
> > --- a/configure
> > +++ b/configure
> > @@ -5159,6 +5159,10 @@ esac
> >  
> >  enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
> >  
> > +# d3d11va requires linking directly to dxgi and d3d11 if not building for
> > +# the desktop api partition
> > +enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
> > +
> >  check_deps $CONFIG_LIST       \
> >             $CONFIG_EXTRA      \
> >             $HAVE_LIST         \
> 
> That's not a good place for the hack. Problem is, I don't see a good place
> anywhere. I'll try to think of something.

Move it above

  enabled vaapi && require vaapi va/va.h vaInitialize -lva

around line 4875 please. Then it's next to the other linker checks for
hardware-accelerated stuff.

Diego

Patch

diff --git a/configure b/configure
index 019902c..e7556e0 100755
--- a/configure
+++ b/configure
@@ -5159,6 +5159,10 @@  esac
 
 enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; }
 
+# d3d11va requires linking directly to dxgi and d3d11 if not building for
+# the desktop api partition
+enabled LoadLibrary || d3d11va_extralibs="-ldxgi -ld3d11"
+
 check_deps $CONFIG_LIST       \
            $CONFIG_EXTRA      \
            $HAVE_LIST         \
diff --git a/libavutil/hwcontext_d3d11va.c b/libavutil/hwcontext_d3d11va.c
index 3940502..4de674d 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,33 @@ 
 
 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 createD3D;
+
+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)
+        return;
+
+    createD3D = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(d3dlib, "D3D11CreateDevice");
+    if (dxgilib)
+        mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) GetProcAddress(dxgilib, "CreateDXGIFactory");
+#else
+    createD3D = (PFN_D3D11_CREATE_DEVICE) D3D11CreateDevice;
+    mCreateDXGIFactory = (PFN_CREATE_DXGI_FACTORY) CreateDXGIFactory1;
+#endif
+}
+
 typedef struct D3D11VAFramesContext {
     int nb_surfaces_used;
 
@@ -407,47 +438,31 @@  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 ret;
+    if (!createD3D) {
+        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;
+        if (!mCreateDXGIFactory)
+            return AVERROR_UNKNOWN;
+        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,