[vlc-devel] [PATCH v2 01/12] d3d11: allow rendering video to DirectComposition surfaces

Pierre Lamot pierre at videolabs.io
Thu May 14 14:05:20 CEST 2020


---
 configure.ac                                 |  13 ++
 include/vlc_vout_window.h                    |   3 +
 modules/video_output/Makefile.am             |   3 +
 modules/video_output/win32/d3d11_swapchain.c | 120 +++++++++++++++++--
 modules/video_output/win32/d3d11_swapchain.h |   5 +-
 modules/video_output/win32/dcomp_wrapper.cpp |  35 ++++++
 modules/video_output/win32/dcomp_wrapper.h   |  35 ++++++
 modules/video_output/win32/direct3d11.c      |  15 ++-
 8 files changed, 216 insertions(+), 13 deletions(-)
 create mode 100644 modules/video_output/win32/dcomp_wrapper.cpp
 create mode 100644 modules/video_output/win32/dcomp_wrapper.h

diff --git a/configure.ac b/configure.ac
index 4b13af45e4..1cb24a4075 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2752,6 +2752,19 @@ dnl
 have_iapplication_data2="no"
 AC_CHECK_TYPES([IApplicationData2],[],[],[#include <windows.storage.h>])
 
+dnl
+dnl DirectComposition
+dnl
+AC_LANG_PUSH([C++])
+AC_CHECK_HEADERS([dcomp.h], [
+    have_dcomp="yes"
+], [], [
+  #include <windows.h>
+])
+AC_LANG_POP([C++])
+AM_CONDITIONAL([HAVE_DCOMP], [test "${have_dcomp}" = "yes"])
+
+
 dnl
 dnl  avformat demuxer/muxer plugin
 dnl
diff --git a/include/vlc_vout_window.h b/include/vlc_vout_window.h
index ad16955ace..ea16d4b414 100644
--- a/include/vlc_vout_window.h
+++ b/include/vlc_vout_window.h
@@ -63,6 +63,7 @@ enum vout_window_type {
     VOUT_WINDOW_TYPE_NSOBJECT /**< macOS/iOS view */,
     VOUT_WINDOW_TYPE_ANDROID_NATIVE /**< Android native window */,
     VOUT_WINDOW_TYPE_WAYLAND /**< Wayland surface */,
+    VOUT_WINDOW_TYPE_DCOMP /**< Win32 DirectComposition */,
 };
 
 /**
@@ -360,6 +361,7 @@ typedef struct vout_window_t {
         void     *nsobject;      /**< macOS/iOS view object */
         void     *anativewindow; /**< Android native window */
         struct wl_surface *wl;   /**< Wayland surface (client pointer) */
+        void     *dcomp_visual;  /**<  Win32 direct composition visual */
     } handle;
 
     /** Display server (mandatory)
@@ -373,6 +375,7 @@ typedef struct vout_window_t {
     union {
         char     *x11; /**< X11 display string (NULL = use default) */
         struct wl_display *wl; /**< Wayland display (client pointer) */
+        void* dcomp_device; /**< DirectComposition device */
     } display;
 
     const struct vout_window_operations *ops; /**< operations handled by the
diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index c82e9a0ed7..16c0cc4207 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -157,6 +157,9 @@ libdirect3d11_plugin_la_SOURCES = video_output/win32/direct3d11.c \
  video_output/win32/d3d11_swapchain.c video_output/win32/d3d11_swapchain.h \
  video_output/win32/common.c video_output/win32/common.h
 libdirect3d11_plugin_la_LIBADD = libchroma_copy.la libd3d11_common.la $(LIBCOM) -luuid
+if HAVE_DCOMP
+libdirect3d11_plugin_la_SOURCES += video_output/win32/dcomp_wrapper.cpp video_output/win32/dcomp_wrapper.h
+endif
 if !HAVE_WINSTORE
 libdirect3d11_plugin_la_SOURCES += video_output/win32/events.c \
  video_output/win32/events.h \
diff --git a/modules/video_output/win32/d3d11_swapchain.c b/modules/video_output/win32/d3d11_swapchain.c
index 7114bf57ef..efeb69b205 100644
--- a/modules/video_output/win32/d3d11_swapchain.c
+++ b/modules/video_output/win32/d3d11_swapchain.c
@@ -52,11 +52,20 @@
 #include "d3d11_swapchain.h"
 #include "d3d11_shaders.h"
 
+#ifdef HAVE_DCOMP_H
+#  include "dcomp_wrapper.h"
+#endif
+
 typedef enum video_color_axis {
     COLOR_AXIS_RGB,
     COLOR_AXIS_YCBCR,
 } video_color_axis;
 
+typedef enum swapchain_surface_type {
+    SWAPCHAIN_SURFACE_HWND,
+    SWAPCHAIN_SURFACE_DCOMP,
+} swapchain_surface_type;
+
 typedef struct {
     DXGI_COLOR_SPACE_TYPE   dxgi;
     const char              *name;
@@ -75,9 +84,17 @@ struct d3d11_local_swapchain
     const d3d_format_t     *pixelFormat;
     const dxgi_color_space *colorspace;
 
+    swapchain_surface_type  swapchainSurfaceType;
+    union {
 #if !VLC_WINSTORE_APP
-    HWND                   swapchainHwnd;
+        HWND                hwnd;
 #endif /* !VLC_WINSTORE_APP */
+        struct {
+            void*           device;
+            void*           visual;
+        } dcomp;
+    } swapchainSurface;
+
     IDXGISwapChain1        *dxgiswapChain;   /* DXGI 1.2 swap chain */
     IDXGISwapChain4        *dxgiswapChain4;  /* DXGI 1.5 for HDR metadata */
     bool                    send_metadata;
@@ -276,9 +293,10 @@ static void FillSwapChainDesc(struct d3d11_local_swapchain *display, UINT width,
     }
 }
 
-static void CreateSwapchain(struct d3d11_local_swapchain *display, UINT width, UINT height)
+static void CreateSwapchainHwnd(struct d3d11_local_swapchain *display, UINT width, UINT height)
 {
-    if (display->swapchainHwnd == NULL)
+    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_HWND);
+    if (display->swapchainSurface.hwnd == NULL)
     {
         msg_Err(display->obj, "missing a HWND to create the swapchain");
         return;
@@ -302,14 +320,15 @@ static void CreateSwapchain(struct d3d11_local_swapchain *display, UINT width, U
     }
 
     hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
-                                              display->swapchainHwnd, &scd,
-                                              NULL, NULL, &display->dxgiswapChain);
+                                                  display->swapchainSurface.hwnd, &scd,
+                                                  NULL, NULL, &display->dxgiswapChain);
+
     if (hr == DXGI_ERROR_INVALID_CALL && scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
     {
         msg_Warn(display->obj, "10 bits swapchain failed, try 8 bits");
         scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
         hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
-                                                  display->swapchainHwnd, &scd,
+                                                  display->swapchainSurface.hwnd, &scd,
                                                   NULL, NULL, &display->dxgiswapChain);
     }
     IDXGIFactory2_Release(dxgifactory);
@@ -317,6 +336,65 @@ static void CreateSwapchain(struct d3d11_local_swapchain *display, UINT width, U
         msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
     }
 }
+
+#ifdef HAVE_DCOMP_H
+static void CreateSwapchainDComp(struct d3d11_local_swapchain *display, UINT width, UINT height)
+{
+    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_DCOMP);
+    if (display->swapchainSurface.dcomp.device == NULL || display->swapchainSurface.dcomp.visual == NULL)
+    {
+        msg_Err(display->obj, "missing a HWND to create the swapchain");
+        return;
+    }
+
+    DXGI_SWAP_CHAIN_DESC1 scd;
+    FillSwapChainDesc(display, width, height, &scd);
+    ZeroMemory(&scd, sizeof(scd));
+    scd.BufferCount = 3;
+    scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    scd.SampleDesc.Count = 1;
+    scd.SampleDesc.Quality = 0;
+    scd.Width = width;
+    scd.Height = height;
+    scd.Format = display->pixelFormat->formatTexture;
+    scd.Scaling = DXGI_SCALING_STRETCH;
+    scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+    scd.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
+
+    IDXGIAdapter *dxgiadapter = D3D11DeviceAdapter(display->d3d_dev->d3ddevice);
+    if (unlikely(dxgiadapter==NULL)) {
+        msg_Err(display->obj, "Could not get the DXGI Adapter");
+        return;
+    }
+
+    IDXGIFactory2 *dxgifactory;
+    HRESULT hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
+    IDXGIAdapter_Release(dxgiadapter);
+    if (FAILED(hr)) {
+        msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
+        return;
+    }
+
+    hr = IDXGIFactory2_CreateSwapChainForComposition(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
+                                                    &scd, NULL, &display->dxgiswapChain);
+    if (hr == DXGI_ERROR_INVALID_CALL && scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
+    {
+        msg_Warn(display->obj, "10 bits swapchain failed, try 8 bits");
+        scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+        hr = IDXGIFactory2_CreateSwapChainForComposition(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
+                                                        &scd, NULL, &display->dxgiswapChain);
+    }
+    IDXGIFactory2_Release(dxgifactory);
+    if (SUCCEEDED(hr)) {
+        IDCompositionVisual_SetContent(display->swapchainSurface.dcomp.visual, (IUnknown *)display->dxgiswapChain);
+        IDCompositionDevice_Commit(display->swapchainSurface.dcomp.device);
+    }
+    if (FAILED(hr)) {
+        msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
+    }
+}
+#endif /* HAVE_DCOMP_H */
+
 #endif /* !VLC_WINSTORE_APP */
 
 static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc_video_render_cfg_t *cfg )
@@ -400,7 +478,13 @@ static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc
     if ( display->dxgiswapChain == NULL )
     {
         display->pixelFormat = newPixelFormat;
-        CreateSwapchain(display, cfg->width, cfg->height);
+
+#ifdef HAVE_DCOMP_H
+        if (display->swapchainSurfaceType == SWAPCHAIN_SURFACE_DCOMP)
+            CreateSwapchainDComp(display, cfg->width, cfg->height);
+        else // SWAPCHAIN_TARGET_HWND
+#endif
+            CreateSwapchainHwnd(display, cfg->width, cfg->height);
 
         if (display->dxgiswapChain == NULL)
             return false;
@@ -555,7 +639,7 @@ bool LocalSwapchainSelectPlane( void *opaque, size_t plane )
     return true;
 }
 
-void *CreateLocalSwapchainHandle(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d_dev)
+void *CreateLocalSwapchainHandleHwnd(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d_dev)
 {
     struct d3d11_local_swapchain *display = vlc_obj_calloc(o, 1, sizeof(*display));
     if (unlikely(display == NULL))
@@ -563,7 +647,8 @@ void *CreateLocalSwapchainHandle(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d
 
     display->obj = o;
 #if !VLC_WINSTORE_APP
-    display->swapchainHwnd = hwnd;
+    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_HWND;
+    display->swapchainSurface.hwnd = hwnd;
 #else // VLC_WINSTORE_APP
     VLC_UNUSED(hwnd);
 #endif // VLC_WINSTORE_APP
@@ -571,3 +656,20 @@ void *CreateLocalSwapchainHandle(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d
 
     return display;
 }
+
+#ifdef HAVE_DCOMP_H
+void *CreateLocalSwapchainHandleDComp(vlc_object_t *o, void* dcompDevice, void* dcompVisual, d3d11_device_t *d3d_dev)
+{
+    struct d3d11_local_swapchain *display = vlc_obj_calloc(o, 1, sizeof(*display));
+    if (unlikely(display == NULL))
+        return NULL;
+
+    display->obj = o;
+    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_DCOMP;
+    display->swapchainSurface.dcomp.device = dcompDevice;
+    display->swapchainSurface.dcomp.visual = dcompVisual;
+    display->d3d_dev = d3d_dev;
+
+    return display;
+}
+#endif
diff --git a/modules/video_output/win32/d3d11_swapchain.h b/modules/video_output/win32/d3d11_swapchain.h
index a5bfeac3c3..69b2e9a80c 100644
--- a/modules/video_output/win32/d3d11_swapchain.h
+++ b/modules/video_output/win32/d3d11_swapchain.h
@@ -28,7 +28,10 @@
 #include <vlc_codec.h>
 #include "../../video_chroma/d3d11_fmt.h"
 
-void *CreateLocalSwapchainHandle(vlc_object_t *, HWND, d3d11_device_t *d3d_dev);
+void *CreateLocalSwapchainHandleHwnd(vlc_object_t *, HWND, d3d11_device_t *d3d_dev);
+#ifdef HAVE_DCOMP_H
+void *CreateLocalSwapchainHandleDComp(vlc_object_t *, void* dcompDevice, void* dcompVisual, d3d11_device_t *d3d_dev);
+#endif
 
 void LocalSwapchainCleanupDevice( void *opaque );
 void LocalSwapchainSwap( void *opaque );
diff --git a/modules/video_output/win32/dcomp_wrapper.cpp b/modules/video_output/win32/dcomp_wrapper.cpp
new file mode 100644
index 0000000000..6b80721353
--- /dev/null
+++ b/modules/video_output/win32/dcomp_wrapper.cpp
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * Copyright (c) 2020 VideoLAN
+ *
+ * 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 "dcomp_wrapper.h"
+#include <dcomp.h>
+
+HRESULT IDCompositionVisual_SetContent(void* opaque, IUnknown *content)
+{
+    IDCompositionVisual* visual = (IDCompositionVisual*)opaque;
+    return visual->SetContent(content);
+}
+
+HRESULT IDCompositionDevice_Commit(void* opaque)
+{
+    IDCompositionDevice* device = (IDCompositionDevice*)opaque;
+    return device->Commit();
+}
diff --git a/modules/video_output/win32/dcomp_wrapper.h b/modules/video_output/win32/dcomp_wrapper.h
new file mode 100644
index 0000000000..365615a0fb
--- /dev/null
+++ b/modules/video_output/win32/dcomp_wrapper.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * Copyright (c) 2020 VideoLAN
+ *
+ * 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.
+ *****************************************************************************/
+#ifndef VLC_DCOMP_WRAPPER_H_
+#define VLC_DCOMP_WRAPPER_H_
+
+#include <windows.h>
+#include <unknwn.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+HRESULT IDCompositionVisual_SetContent(void* visual, IUnknown *content);
+HRESULT IDCompositionDevice_Commit(void* device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/video_output/win32/direct3d11.c b/modules/video_output/win32/direct3d11.c
index 66b06c50b9..ada535e977 100644
--- a/modules/video_output/win32/direct3d11.c
+++ b/modules/video_output/win32/direct3d11.c
@@ -321,13 +321,22 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
     if ( sys->swapCb == NULL || sys->startEndRenderingCb == NULL || sys->updateOutputCb == NULL )
     {
 #if !VLC_WINSTORE_APP
-        if (CommonWindowInit(VLC_OBJECT(vd), &sys->area, &sys->sys,
+        if (cfg->window->type == VOUT_WINDOW_TYPE_HWND)
+        {
+            if (CommonWindowInit(VLC_OBJECT(vd), &sys->area, &sys->sys,
                        vd->source.projection_mode != PROJECTION_MODE_RECTANGULAR))
-            goto error;
+                goto error;
+        }
+
 #endif /* !VLC_WINSTORE_APP */
 
         /* use our internal swapchain callbacks */
-        sys->outside_opaque      = CreateLocalSwapchainHandle(VLC_OBJECT(vd), sys->sys.hvideownd, sys->d3d_dev);
+#ifdef HAVE_DCOMP_H
+        if (cfg->window->type == VOUT_WINDOW_TYPE_DCOMP)
+            sys->outside_opaque      = CreateLocalSwapchainHandleDComp(VLC_OBJECT(vd), cfg->window->display.dcomp_device, cfg->window->handle.dcomp_visual, sys->d3d_dev);
+        else
+#endif
+            sys->outside_opaque      = CreateLocalSwapchainHandleHwnd(VLC_OBJECT(vd), sys->sys.hvideownd, sys->d3d_dev);
         if (unlikely(sys->outside_opaque == NULL))
             goto error;
         sys->updateOutputCb      = LocalSwapchainUpdateOutput;
-- 
2.25.1



More information about the vlc-devel mailing list