[vlc-devel] [PATCH 4/5] [RFC] direct3d11: add a decoder pool in core for D3D11 opaque pictures

Steve Lhomme robux4 at gmail.com
Thu May 28 17:20:52 CEST 2015


---
 po/POTFILES.in                  |   4 +
 src/Makefile.am                 |   9 ++
 src/video_output/vout_wrapper.c |  15 +-
 src/win32/direct3d11_pool.c     | 318 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 342 insertions(+), 4 deletions(-)
 create mode 100644 src/win32/direct3d11_pool.c

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 18a44fb..3dcafd2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -158,6 +158,10 @@ src/video_output/vout_control.h
 src/video_output/vout_internal.h
 src/video_output/vout_intf.c
 src/video_output/vout_subpictures.c
+src/win32/direct3d11_pool.c
+src/win32/direct3d11_pool.h
+src/win32/picture.c
+src/win32/picture.h
 src/win32/thread.c
 
 lib/audio.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 1f02494..0c2d59b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -301,6 +301,15 @@ SOURCES_libvlc_win32 = \
 	win32/rand.c \
 	$(NULL)
 
+if HAVE_AVCODEC_D3D11VA
+SOURCES_libvlc_win32 += \
+	win32/direct3d11_pool.c \
+	win32/direct3d11_pool.h \
+	win32/picture.c \
+	win32/picture.h \
+	$(NULL)
+endif
+
 SOURCES_libvlc_symbian = \
 	symbian/path.cpp \
 	symbian/dirs.c \
diff --git a/src/video_output/vout_wrapper.c b/src/video_output/vout_wrapper.c
index dbaae78..6db1447 100644
--- a/src/video_output/vout_wrapper.c
+++ b/src/video_output/vout_wrapper.c
@@ -35,6 +35,9 @@
 #include <assert.h>
 #include "vout_internal.h"
 #include "display.h"
+#if defined(HAVE_AVCODEC_D3D11VA)
+#include "../win32/direct3d11_pool.h"
+#endif
 
 /*****************************************************************************
  * Local prototypes
@@ -145,10 +148,14 @@ int vout_InitWrapper(vout_thread_t *vout)
         sys->decoder_pool = display_pool;
         sys->display_pool = display_pool;
     } else if (!sys->decoder_pool) {
-        sys->decoder_pool =
-            picture_pool_NewFromFormat(&source,
-                                       __MAX(VOUT_MAX_PICTURES,
-                                             reserved_picture + decoder_picture - DISPLAY_PICTURE_COUNT));
+        const unsigned decoder_pool_size = __MAX(VOUT_MAX_PICTURES,
+                                                 reserved_picture + decoder_picture - DISPLAY_PICTURE_COUNT);
+#if defined(HAVE_AVCODEC_D3D11VA)
+        if (source.i_chroma == VLC_CODEC_D3D11_OPAQUE)
+            sys->decoder_pool = AllocPoolD3D11( VLC_OBJECT(vout), &source, decoder_pool_size );
+        else
+#endif
+            sys->decoder_pool = picture_pool_NewFromFormat( &source, decoder_pool_size );
         if (!sys->decoder_pool)
             return VLC_EGENERIC;
         if (allow_dr) {
diff --git a/src/win32/direct3d11_pool.c b/src/win32/direct3d11_pool.c
new file mode 100644
index 0000000..e30ecf3
--- /dev/null
+++ b/src/win32/direct3d11_pool.c
@@ -0,0 +1,318 @@
+/*****************************************************************************
+ * direct3d9_pool.c: Windows Direct3D9 picture pool creation
+ *****************************************************************************
+ * Copyright (C) 2015 VLC authors and VideoLAN
+ *$Id$
+ *
+ * Authors: Steve Lhomme <robux4 at gmail.com>,
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "picture.h"
+
+#define COBJMACROS
+#include "direct3d11_pool.h"
+
+#include <initguid.h> /* must be last included to not redefine existing GUIDs */
+
+#ifdef __MINGW32__
+# include <_mingw.h>
+
+# if defined(__MINGW64_VERSION_MAJOR)
+#  include <dxva.h>
+# endif
+
+#endif /* __MINGW32__ */
+
+DEFINE_GUID(IID_ID3D10Multithread,   0x9b7e4e00, 0x342c, 0x4106, 0xa1, 0x9f, 0x4f, 0x27, 0x04, 0xf6, 0x89, 0xf0);
+
+static int Direct3D11MapTexture(picture_t *);
+
+typedef struct
+{
+    const char   *name;
+    DXGI_FORMAT  formatTexture;
+    vlc_fourcc_t fourcc;
+    DXGI_FORMAT  formatY;
+    DXGI_FORMAT  formatUV;
+} d3d_format_t;
+
+static const d3d_format_t d3d_formats[] = {
+    { "I420",     DXGI_FORMAT_NV12,           VLC_CODEC_I420,     DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8G8_UNORM },
+    { "YV12",     DXGI_FORMAT_NV12,           VLC_CODEC_YV12,     DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8G8_UNORM },
+    { "NV12",     DXGI_FORMAT_NV12,           VLC_CODEC_NV12,     DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8G8_UNORM },
+    { "D3D11VA",  DXGI_FORMAT_NV12,           VLC_CODEC_D3D11_OPAQUE,  DXGI_FORMAT_R8_UNORM,      DXGI_FORMAT_R8G8_UNORM },
+#ifdef BROKEN_PIXEL
+    { "YUY2",     DXGI_FORMAT_YUY2,           VLC_CODEC_I422,     DXGI_FORMAT_R8G8B8A8_UNORM,     0 },
+    { "AYUV",     DXGI_FORMAT_AYUV,           VLC_CODEC_YUVA,     DXGI_FORMAT_R8G8B8A8_UNORM,     0 },
+    { "Y416",     DXGI_FORMAT_Y416,           VLC_CODEC_I444_16L, DXGI_FORMAT_R16G16B16A16_UINT,  0 },
+#endif
+#ifdef UNTESTED
+    { "P010",     DXGI_FORMAT_P010,           VLC_CODEC_I420_10L, DXGI_FORMAT_R16_UNORM,          DXGI_FORMAT_R16_UNORM },
+    { "Y210",     DXGI_FORMAT_Y210,           VLC_CODEC_I422_10L, DXGI_FORMAT_R16G16B16A16_UNORM, 0 },
+    { "Y410",     DXGI_FORMAT_Y410,           VLC_CODEC_I444_10L, DXGI_FORMAT_R10G10B10A2_UNORM,  0 },
+    { "NV11",     DXGI_FORMAT_NV11,           VLC_CODEC_I411,     DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8G8_UNORM },
+#endif
+    { "R8G8B8A8", DXGI_FORMAT_R8G8B8A8_UNORM, VLC_CODEC_RGBA,     DXGI_FORMAT_R8G8B8A8_UNORM,     0 },
+    { "B8G8R8A8", DXGI_FORMAT_B8G8R8A8_UNORM, VLC_CODEC_BGRA,     DXGI_FORMAT_B8G8R8A8_UNORM,     0 },
+    { "R8G8B8X8", DXGI_FORMAT_B8G8R8X8_UNORM, VLC_CODEC_RGB32,    DXGI_FORMAT_B8G8R8X8_UNORM,     0 },
+    { "B5G6R5",   DXGI_FORMAT_B5G6R5_UNORM,   VLC_CODEC_RGB16,    DXGI_FORMAT_B5G6R5_UNORM,       0 },
+
+    { NULL, 0, 0, 0, 0}
+};
+
+picture_pool_t *AllocPoolD3D11( vlc_object_t *va, const video_format_t *fmt, unsigned pool_size )
+{
+    HINSTANCE         hd3d11_dll = NULL;
+    ID3D11Device      *d3ddev = NULL;
+    HRESULT           hr;
+
+    if (fmt->i_chroma != VLC_CODEC_D3D11_OPAQUE)
+        return picture_pool_NewFromFormat(fmt, pool_size);
+
+    OSVERSIONINFO winVer;
+    winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6) {
+        msg_Warn(va, "windows version not compatible with D3D11");
+        goto error;
+    }
+
+    hd3d11_dll = LoadLibrary(TEXT("D3D11.DLL"));
+    if (!hd3d11_dll) {
+        msg_Warn(va, "cannot load d3d11.dll, aborting");
+        goto error;
+    }
+
+    PFN_D3D11_CREATE_DEVICE pf_CreateDevice;
+    pf_CreateDevice = (PFN_D3D11_CREATE_DEVICE) GetProcAddress(hd3d11_dll, "D3D11CreateDevice");
+    if (!pf_CreateDevice) {
+        msg_Err(va, "Cannot locate reference to D3D11CreateDevice ABI in DLL");
+        goto error;
+    }
+
+    /* Create the D3D object. */
+    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
+# if !defined(NDEBUG) && defined(_MSC_VER)
+    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
+# endif
+
+    /* */
+    static const D3D_DRIVER_TYPE driverAttempts[] = {
+        D3D_DRIVER_TYPE_HARDWARE,
+        D3D_DRIVER_TYPE_WARP,
+        D3D_DRIVER_TYPE_REFERENCE,
+    };
+
+    ID3D11DeviceContext *d3dctx;
+    for (UINT driver = 0; driver < ARRAYSIZE(driverAttempts); driver++) {
+        hr = pf_CreateDevice(NULL, driverAttempts[driver], NULL,
+                                 creationFlags, NULL, 0,
+                                 D3D11_SDK_VERSION, &d3ddev, NULL, &d3dctx);
+        if (SUCCEEDED(hr)) {
+#ifndef NDEBUG
+            msg_Dbg(va, "Created the D3D11 pool device 0x%p ctx 0x%p type %d.", d3ddev, d3dctx, driverAttempts[driver]);
+#endif
+            break;
+        }
+    }
+    if (FAILED(hr)) {
+        msg_Err(va, "D3D11CreateDevice failed. (hr=0x%lX)", hr);
+        goto error;
+    }
+
+    vlc_fourcc_t i_src_chroma;
+    if (fmt->i_chroma == VLC_CODEC_D3D9_OPAQUE)
+        i_src_chroma = VLC_CODEC_NV12; // favor NV12
+    else
+        i_src_chroma = fmt->i_chroma;
+
+    DXGI_FORMAT outputFormat =  DXGI_FORMAT_UNKNOWN;
+
+    // look for the request pixel format first
+    UINT i_quadSupportFlags = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_LOAD;
+    UINT i_formatSupport;
+    for (unsigned i = 0; d3d_formats[i].name != 0; i++)
+    {
+        if( i_src_chroma == d3d_formats[i].fourcc)
+        {
+            if( SUCCEEDED( ID3D11Device_CheckFormatSupport(d3ddev,
+                                                           d3d_formats[i].formatTexture,
+                                                           &i_formatSupport)) &&
+                    ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags)
+            {
+                msg_Dbg(va, "Using pixel format %s", d3d_formats[i].name );
+                outputFormat = d3d_formats[i].formatTexture;
+                break;
+            }
+        }
+    }
+
+    // look for any pixel format that we can handle
+    if ( outputFormat == DXGI_FORMAT_UNKNOWN )
+    {
+        for (unsigned i = 0; d3d_formats[i].name != 0; i++)
+        {
+            if( SUCCEEDED( ID3D11Device_CheckFormatSupport(d3ddev,
+                                                           d3d_formats[i].formatTexture,
+                                                           &i_formatSupport)) &&
+                    ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags)
+            {
+                msg_Dbg(va, "Using pixel format %s", d3d_formats[i].name );
+                outputFormat = d3d_formats[i].formatTexture;
+                break;
+            }
+        }
+    }
+    if ( outputFormat == DXGI_FORMAT_UNKNOWN )
+    {
+       msg_Err(va, "Could not get a suitable texture pixel format");
+       goto error;
+    }
+
+    picture_pool_t *result = AllocPoolD3D11Ex(va, d3ddev, d3dctx, fmt, outputFormat, pool_size);
+    if (!result)
+        goto error;
+
+    FreeLibrary(hd3d11_dll);
+    return result;
+
+error:
+    if (d3ddev)
+        ID3D11Device_Release(d3ddev);
+    if (hd3d11_dll)
+        FreeLibrary(hd3d11_dll);
+    return NULL;
+}
+
+picture_pool_t *AllocPoolD3D11Ex(vlc_object_t *va, ID3D11Device *d3ddev, ID3D11DeviceContext *d3dctx,
+                                 const video_format_t *fmt, DXGI_FORMAT output_format,
+                                 unsigned pool_size)
+{
+    picture_t**       pictures = NULL;
+    unsigned          picture_count = 0;
+    HRESULT           hr;
+
+    ID3D10Multithread *pMultithread;
+    hr = ID3D11Device_QueryInterface( d3ddev, &IID_ID3D10Multithread, (void **)&pMultithread);
+    if (SUCCEEDED(hr)) {
+        ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
+        ID3D10Multithread_Release(pMultithread);
+    }
+
+    pictures = calloc(pool_size, sizeof(*pictures));
+    if (!pictures)
+        goto error;
+
+    D3D11_TEXTURE2D_DESC texDesc;
+    ZeroMemory(&texDesc, sizeof(texDesc));
+    texDesc.Width = fmt->i_width;
+    texDesc.Height = fmt->i_height;
+    texDesc.MipLevels = 1;
+    texDesc.Format = output_format;
+    texDesc.SampleDesc.Count = 1;
+    texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
+    texDesc.ArraySize = 1;
+    texDesc.Usage = D3D11_USAGE_DYNAMIC;
+    texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+    texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+    unsigned surface_count;
+    for (surface_count = 0; surface_count < pool_size; surface_count++) {
+        picture_sys_t *picsys = calloc(1, sizeof(*picsys));
+        if (unlikely(picsys == NULL))
+            goto error;
+
+        hr = ID3D11Device_CreateTexture2D( d3ddev, &texDesc, NULL, &picsys->texture );
+        if (FAILED(hr)) {
+            msg_Err(va, "CreateTexture2D %d failed. (hr=0x%0lx)", pool_size, hr);
+            goto error;
+        }
+
+        picsys->context = d3dctx;
+        picsys->device = d3ddev;
+
+        picture_resource_t resource = {
+            .p_sys = picsys,
+            .pf_destroy = DestroyD3D11Picture,
+        };
+
+        picture_t *picture = picture_NewFromResource(fmt, &resource);
+        if (unlikely(picture == NULL)) {
+            free(picsys);
+            goto error;
+        }
+
+        pictures[surface_count] = picture;
+        /* each picture_t holds a ref to the context and release it on Destroy */
+        ID3D11DeviceContext_AddRef(picsys->context);
+        /* each picture_t holds a ref to the DLL */
+        picsys->hd3d11_dll = LoadLibrary(TEXT("D3D11.DLL"));
+    }
+    msg_Dbg(va, "ID3D11VideoDecoderOutputView succeed with %d surfaces (%dx%d)",
+            pool_size, fmt->i_width, fmt->i_height);
+
+    /* release the system resources, they will be free'd with the pool */
+    //ID3D11Device_Release(d3ddev); /* TODO check this */
+
+    picture_pool_configuration_t pool_cfg;
+    memset(&pool_cfg, 0, sizeof(pool_cfg));
+    pool_cfg.picture_count = pool_size;
+    pool_cfg.picture       = pictures;
+    pool_cfg.lock          = Direct3D11MapTexture; /* TODO we only need to map once */
+
+    return picture_pool_NewExtended( &pool_cfg );
+
+error:
+    if (pictures) {
+        for (unsigned i=0;i<picture_count; ++i)
+            DestroyD3D11Picture(pictures[i]);
+        free(pictures);
+    }
+    return NULL;
+}
+
+static int Direct3D11MapTexture(picture_t *picture)
+{
+    D3D11_MAPPED_SUBRESOURCE mappedResource;
+    HRESULT hr;
+    int res;
+    picture_sys_t *p_sys = picture->p_sys;
+    hr = ID3D11DeviceContext_Map(picture->p_sys->context, (ID3D11Resource *)p_sys->texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
+    if( FAILED(hr) )
+        return VLC_EGENERIC;
+
+    res = CommonUpdatePicture(picture, NULL, mappedResource.pData, mappedResource.RowPitch);
+    ID3D11DeviceContext_Unmap(picture->p_sys->context,(ID3D11Resource *)p_sys->texture, 0);
+    return res;
+}
+
+void DestroyD3D11Picture(picture_t *picture)
+{
+    picture_sys_t *p_sys = picture->p_sys;
+    ID3D11DeviceContext_Release(p_sys->context);
+
+    if (p_sys->texture)
+        ID3D11Texture2D_Release(p_sys->texture);
+
+    FreeLibrary(p_sys->hd3d11_dll);
+
+    free(p_sys);
+    free(picture);
+}
-- 
2.4.0




More information about the vlc-devel mailing list