[vlc-commits] direct3d11: split the DXGI swapchain parts from D3D11 parts

Steve Lhomme git at videolan.org
Wed Feb 10 11:06:41 UTC 2021


vlc | branch: master | Steve Lhomme <robux4 at ycbcr.xyz> | Fri Jan 22 16:26:56 2021 +0100| [610f928c68a2dc137d3b162e7668b82d56a0c51d] | committer: Steve Lhomme

direct3d11: split the DXGI swapchain parts from D3D11 parts

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=610f928c68a2dc137d3b162e7668b82d56a0c51d
---

 modules/video_output/Makefile.am             |   1 +
 modules/video_output/win32/d3d11_swapchain.c | 514 +++----------------------
 modules/video_output/win32/d3d11_swapchain.h |  20 +-
 modules/video_output/win32/direct3d11.c      |  30 +-
 modules/video_output/win32/dxgi_swapchain.c  | 541 +++++++++++++++++++++++++++
 modules/video_output/win32/dxgi_swapchain.h  |  64 ++++
 6 files changed, 691 insertions(+), 479 deletions(-)

diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 684bf11d73..36658aa4d7 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -135,6 +135,7 @@ libdirect3d11_plugin_la_SOURCES = video_output/win32/direct3d11.c \
  video_output/win32/d3d11_quad.c video_output/win32/d3d11_quad.h \
  video_output/win32/d3d11_shaders.c video_output/win32/d3d11_shaders.h \
  video_output/win32/d3d11_swapchain.c video_output/win32/d3d11_swapchain.h \
+ video_output/win32/dxgi_swapchain.c video_output/win32/dxgi_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
diff --git a/modules/video_output/win32/d3d11_swapchain.c b/modules/video_output/win32/d3d11_swapchain.c
index efeb69b205..d891636504 100644
--- a/modules/video_output/win32/d3d11_swapchain.c
+++ b/modules/video_output/win32/d3d11_swapchain.c
@@ -43,12 +43,8 @@
 #define COBJMACROS
 #include <initguid.h>
 #include <d3d11.h>
-#ifdef HAVE_DXGI1_6_H
-# include <dxgi1_6.h>
-#else
-# include <dxgi1_5.h>
-#endif
 
+#include "dxgi_swapchain.h"
 #include "d3d11_swapchain.h"
 #include "d3d11_shaders.h"
 
@@ -56,350 +52,21 @@
 #  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;
-    video_color_axis        axis;
-    video_color_primaries_t primaries;
-    video_transfer_func_t   transfer;
-    video_color_space_t     color;
-    bool                    b_full_range;
-} dxgi_color_space;
-
 struct d3d11_local_swapchain
 {
+    struct dxgi_swapchain *sys;
+
     vlc_object_t           *obj;
     d3d11_device_t         *d3d_dev;
 
-    const d3d_format_t     *pixelFormat;
-    const dxgi_color_space *colorspace;
-
-    swapchain_surface_type  swapchainSurfaceType;
-    union {
-#if !VLC_WINSTORE_APP
-        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;
-    DXGI_HDR_METADATA_HDR10 hdr10;
-
     ID3D11RenderTargetView *swapchainTargetView[D3D11_MAX_RENDER_TARGET];
-
-    bool                   logged_capabilities;
 };
 
 DEFINE_GUID(GUID_SWAPCHAIN_WIDTH,  0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
 DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
 
-#define DXGI_COLOR_RANGE_FULL   1 /* 0-255 */
-#define DXGI_COLOR_RANGE_STUDIO 0 /* 16-235 */
-
-#define TRANSFER_FUNC_10    TRANSFER_FUNC_LINEAR
-#define TRANSFER_FUNC_22    TRANSFER_FUNC_SRGB
-#define TRANSFER_FUNC_2084  TRANSFER_FUNC_SMPTE_ST2084
-
-#define COLOR_PRIMARIES_BT601  COLOR_PRIMARIES_BT601_525
-
-static const dxgi_color_space color_spaces[] = {
-#define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
-    { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
-      #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
-      COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
-      COLOR_SPACE_BT##PRIMARIES, DXGI_COLOR_RANGE_##RANGE},
-
-    DXGIMAP(RGB,   FULL,     22,    NONE,   709)
-    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   601)
-    DXGIMAP(YCBCR, FULL,     22,    LEFT,   601)
-    DXGIMAP(RGB,   FULL,     10,    NONE,   709)
-    DXGIMAP(RGB,   STUDIO,   22,    NONE,   709)
-    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   709)
-    DXGIMAP(YCBCR, FULL,     22,    LEFT,   709)
-    DXGIMAP(RGB,   STUDIO,   22,    NONE,  2020)
-    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,  2020)
-    DXGIMAP(YCBCR, FULL,     22,    LEFT,  2020)
-    DXGIMAP(YCBCR, STUDIO,   22, TOPLEFT,  2020)
-    DXGIMAP(RGB,   FULL,     22,    NONE,  2020)
-    DXGIMAP(RGB,   FULL,   2084,    NONE,  2020)
-    DXGIMAP(YCBCR, STUDIO, 2084,    LEFT,  2020)
-    DXGIMAP(RGB,   STUDIO, 2084,    NONE,  2020)
-    DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT,  2020)
-    DXGIMAP(YCBCR, STUDIO,  HLG, TOPLEFT,  2020)
-    DXGIMAP(YCBCR, FULL,    HLG, TOPLEFT,  2020)
-    /*DXGIMAP(YCBCR, FULL,     22,    NONE,  2020, 601)*/
-    {DXGI_COLOR_SPACE_RESERVED, NULL, 0, 0, 0, 0, 0},
-#undef DXGIMAP
-};
-
-#ifdef HAVE_DXGI1_6_H
-static bool canHandleConversion(const dxgi_color_space *src, const dxgi_color_space *dst)
-{
-    if (src == dst)
-        return true;
-    if (src->primaries == COLOR_PRIMARIES_BT2020)
-        return true; /* we can convert BT2020 to 2020 or 709 */
-    if (dst->transfer == TRANSFER_FUNC_BT709)
-        return true; /* we can handle anything to 709 */
-    return false; /* let Windows do the rest */
-}
-#endif
-
-static void SelectSwapchainColorspace(struct d3d11_local_swapchain *display, const libvlc_video_render_cfg_t *cfg)
-{
-    HRESULT hr;
-    int best = 0;
-    int score, best_score = 0;
-    UINT support;
-    IDXGISwapChain3 *dxgiswapChain3 = NULL;
-    hr = IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain3, (void **)&dxgiswapChain3);
-    if (FAILED(hr)) {
-        msg_Warn(display->obj, "could not get a IDXGISwapChain3");
-        goto done;
-    }
-
-    /* pick the best output based on color support and transfer */
-    /* TODO support YUV output later */
-    best = -1;
-    for (int i=0; color_spaces[i].name; ++i)
-    {
-        hr = IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3, color_spaces[i].dxgi, &support);
-        if (SUCCEEDED(hr) && support) {
-            if (!display->logged_capabilities)
-                msg_Dbg(display->obj, "supports colorspace %s", color_spaces[i].name);
-            score = 0;
-            if (color_spaces[i].primaries == (video_color_primaries_t) cfg->primaries)
-                score++;
-            if (color_spaces[i].color == (video_color_space_t) cfg->colorspace)
-                score += 2; /* we don't want to translate color spaces */
-            if (color_spaces[i].transfer == (video_transfer_func_t) cfg->transfer ||
-                /* favor 2084 output for HLG source */
-                (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && cfg->transfer == TRANSFER_FUNC_HLG))
-                score++;
-            if (color_spaces[i].b_full_range == cfg->full_range)
-                score++;
-            if (score > best_score || (score && best == -1)) {
-                best = i;
-                best_score = score;
-            }
-        }
-    }
-    display->logged_capabilities = true;
-
-    if (best == -1)
-    {
-        best = 0;
-        msg_Warn(display->obj, "no matching colorspace found force %s", color_spaces[best].name);
-    }
-
-    IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&display->dxgiswapChain4);
-
-#ifdef HAVE_DXGI1_6_H
-    IDXGIOutput *dxgiOutput = NULL;
-
-    if (SUCCEEDED(IDXGISwapChain_GetContainingOutput( display->dxgiswapChain, &dxgiOutput )))
-    {
-        IDXGIOutput6 *dxgiOutput6 = NULL;
-        if (SUCCEEDED(IDXGIOutput_QueryInterface( dxgiOutput, &IID_IDXGIOutput6, (void **)&dxgiOutput6 )))
-        {
-            DXGI_OUTPUT_DESC1 desc1;
-            if (SUCCEEDED(IDXGIOutput6_GetDesc1( dxgiOutput6, &desc1 )))
-            {
-                const dxgi_color_space *csp = NULL;
-                for (int i=0; color_spaces[i].name; ++i)
-                {
-                    if (color_spaces[i].dxgi == desc1.ColorSpace)
-                    {
-                        if (!canHandleConversion(&color_spaces[best], &color_spaces[i]))
-                            msg_Warn(display->obj, "Can't handle conversion to screen format %s", color_spaces[i].name);
-                        else
-                        {
-                            best = i;
-                            csp = &color_spaces[i];
-                        }
-                        break;
-                    }
-                }
-
-                msg_Dbg(display->obj, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1.MaxFullFrameLuminance, csp?csp->name:"unknown", desc1.BitsPerColor);
-                //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
-            }
-            IDXGIOutput6_Release( dxgiOutput6 );
-        }
-        IDXGIOutput_Release( dxgiOutput );
-    }
-#endif
-
-    hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
-    if (SUCCEEDED(hr))
-        msg_Dbg(display->obj, "using colorspace %s", color_spaces[best].name);
-    else
-        msg_Err(display->obj, "Failed to set colorspace %s. (hr=0x%lX)", color_spaces[best].name, hr);
-done:
-    display->colorspace = &color_spaces[best];
-    display->send_metadata = color_spaces[best].transfer == (video_transfer_func_t) cfg->transfer &&
-                             color_spaces[best].primaries == (video_color_primaries_t) cfg->primaries &&
-                             color_spaces[best].color == (video_color_space_t) cfg->colorspace;
-    if (dxgiswapChain3)
-        IDXGISwapChain3_Release(dxgiswapChain3);
-}
-
-#if !VLC_WINSTORE_APP
-static void FillSwapChainDesc(struct d3d11_local_swapchain *display, UINT width, UINT height, DXGI_SWAP_CHAIN_DESC1 *out)
-{
-    ZeroMemory(out, sizeof(*out));
-    out->BufferCount = 3;
-    out->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
-    out->SampleDesc.Count = 1;
-    out->SampleDesc.Quality = 0;
-    out->Width = width;
-    out->Height = height;
-    out->Format = display->pixelFormat->formatTexture;
-    //out->Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
-
-    bool isWin10OrGreater = false;
-    HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
-    if (likely(hKernel32 != NULL))
-        isWin10OrGreater = GetProcAddress(hKernel32, "GetSystemCpuSetInformation") != NULL;
-    if (isWin10OrGreater)
-        out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
-    else
-    {
-        bool isWin80OrGreater = false;
-        if (likely(hKernel32 != NULL))
-            isWin80OrGreater = GetProcAddress(hKernel32, "CheckTokenCapability") != NULL;
-        if (isWin80OrGreater)
-            out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
-        else
-        {
-            out->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
-            out->BufferCount = 1;
-        }
-    }
-}
-
-static void CreateSwapchainHwnd(struct d3d11_local_swapchain *display, UINT width, UINT height)
-{
-    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_HWND);
-    if (display->swapchainSurface.hwnd == NULL)
-    {
-        msg_Err(display->obj, "missing a HWND to create the swapchain");
-        return;
-    }
-
-    DXGI_SWAP_CHAIN_DESC1 scd;
-    FillSwapChainDesc(display, width, height, &scd);
-
-    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_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
-                                                  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->swapchainSurface.hwnd, &scd,
-                                                  NULL, NULL, &display->dxgiswapChain);
-    }
-    IDXGIFactory2_Release(dxgifactory);
-    if (FAILED(hr)) {
-        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 )
 {
-    ID3D11Texture2D* pBackBuffer;
     HRESULT hr;
 
     D3D11_TEXTURE2D_DESC dsc = { 0 };
@@ -413,8 +80,8 @@ static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc
             ID3D11Texture2D_GetDesc( (ID3D11Texture2D*) res, &dsc );
             ID3D11Resource_Release( res );
         }
-        assert(display->pixelFormat->formatTexture == dsc.Format);
-        bitsPerChannel = display->pixelFormat->bitsPerChannel;
+        assert(DXGI_GetPixelFormat(display->sys)->formatTexture == dsc.Format);
+        bitsPerChannel = DXGI_GetPixelFormat(display->sys)->bitsPerChannel;
     }
 
     if ( dsc.Width == cfg->width && dsc.Height == cfg->height && cfg->bitdepth <= bitsPerChannel )
@@ -431,11 +98,13 @@ static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc
 
     const d3d_format_t *newPixelFormat = NULL;
 #if VLC_WINSTORE_APP
-    display->dxgiswapChain = (void*)(uintptr_t)var_InheritInteger(display->obj, "winrt-swapchain");
-    if (display->dxgiswapChain != NULL)
+    IDXGISwapChain1 *dxgiswapChain = DXGI_GetSwapChain1(display->sys);
+    if (dxgiswapChain == NULL)
+        dxgiswapChain = (void*)(uintptr_t)var_InheritInteger(display->obj, "winrt-swapchain");
+    if (dxgiswapChain != NULL)
     {
         DXGI_SWAP_CHAIN_DESC1 scd;
-        if (SUCCEEDED(IDXGISwapChain1_GetDesc1(display->dxgiswapChain, &scd)))
+        if (SUCCEEDED(IDXGISwapChain1_GetDesc1(dxgiswapChain, &scd)))
         {
             for (const d3d_format_t *output_format = GetRenderFormatList();
                  output_format->name != NULL; ++output_format)
@@ -466,63 +135,46 @@ static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc
         return false;
     }
 
-#if !VLC_WINSTORE_APP
-    if (display->dxgiswapChain != NULL && display->pixelFormat != newPixelFormat)
-    {
-        // the pixel format changed, we need a new swapchain
-        IDXGISwapChain_Release(display->dxgiswapChain);
-        display->dxgiswapChain = NULL;
-        display->logged_capabilities = false;
+    IDXGIDevice *pDXGIDevice = NULL;
+    hr = ID3D11Device_QueryInterface(display->d3d_dev->d3ddevice, &IID_IDXGIDevice, (void **)&pDXGIDevice);
+    if (FAILED(hr)) {
+        return false;
     }
-
-    if ( display->dxgiswapChain == NULL )
-    {
-        display->pixelFormat = newPixelFormat;
-
-#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;
+    IDXGIAdapter *dxgiadapter;
+    hr = IDXGIDevice_GetAdapter(pDXGIDevice, &dxgiadapter);
+    IDXGIDevice_Release(pDXGIDevice);
+    if (FAILED(hr)) {
+        return false;
     }
-    else
-#endif /* !VLC_WINSTORE_APP */
+
+    if (!DXGI_UpdateSwapChain( display->sys, dxgiadapter, (IUnknown*) display->d3d_dev->d3ddevice, newPixelFormat, cfg ))
     {
-        /* TODO detect is the size is the same as the output and switch to fullscreen mode */
-        hr = IDXGISwapChain_ResizeBuffers( display->dxgiswapChain, 0, cfg->width, cfg->height,
-                                           DXGI_FORMAT_UNKNOWN, 0 );
-        if ( FAILED( hr ) ) {
-            msg_Err( display->obj, "Failed to resize the backbuffer. (hr=0x%lX)", hr );
-            return false;
-        }
+        IDXGIAdapter_Release(dxgiadapter);
+        return false;
     }
+    IDXGIAdapter_Release(dxgiadapter);
 
-    hr = IDXGISwapChain_GetBuffer( display->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *) &pBackBuffer );
+    ID3D11Resource* pBackBuffer;
+    hr = IDXGISwapChain1_GetBuffer( DXGI_GetSwapChain1(display->sys), 0, &IID_ID3D11Resource, (LPVOID *) &pBackBuffer );
     if ( FAILED( hr ) ) {
         msg_Err( display->obj, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr );
         return false;
     }
 
-    hr = D3D11_CreateRenderTargets( display->d3d_dev, (ID3D11Resource *) pBackBuffer,
-                                    display->pixelFormat, display->swapchainTargetView );
-    ID3D11Texture2D_Release( pBackBuffer );
+    hr = D3D11_CreateRenderTargets( display->d3d_dev, pBackBuffer,
+                                    DXGI_GetPixelFormat(display->sys), display->swapchainTargetView );
+    ID3D11Resource_Release( pBackBuffer );
     if ( FAILED( hr ) ) {
         msg_Err( display->obj, "Failed to create the target view. (hr=0x%lX)", hr );
         return false;
     }
 
-    D3D11_ClearRenderTargets( display->d3d_dev, display->pixelFormat, display->swapchainTargetView );
-
-    SelectSwapchainColorspace(display, cfg);
+    D3D11_ClearRenderTargets( display->d3d_dev, DXGI_GetPixelFormat(display->sys), display->swapchainTargetView );
 
     return true;
 }
 
-void LocalSwapchainCleanupDevice( void *opaque )
+void D3D11_LocalSwapchainCleanupDevice( void *opaque )
 {
     struct d3d11_local_swapchain *display = opaque;
     for (size_t i=0; i < ARRAY_SIZE(display->swapchainTargetView); i++)
@@ -532,54 +184,40 @@ void LocalSwapchainCleanupDevice( void *opaque )
             display->swapchainTargetView[i] = NULL;
         }
     }
-    if (display->dxgiswapChain4)
-    {
-        IDXGISwapChain4_Release(display->dxgiswapChain4);
-        display->dxgiswapChain4 = NULL;
-    }
-    if (display->dxgiswapChain)
-    {
-        IDXGISwapChain_Release(display->dxgiswapChain);
-        display->dxgiswapChain = NULL;
-    }
+    DXGI_LocalSwapchainCleanupDevice(display->sys);
 }
 
-void LocalSwapchainSwap( void *opaque )
+bool D3D11_LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
 {
     struct d3d11_local_swapchain *display = opaque;
-    DXGI_PRESENT_PARAMETERS presentParams = { 0 };
+    if ( !UpdateSwapchain( display, cfg ) )
+        return false;
+    DXGI_SwapchainUpdateOutput(display->sys, out);
+    return true;
+}
 
-    HRESULT hr = IDXGISwapChain1_Present1( display->dxgiswapChain, 0, 0, &presentParams );
-    if ( hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET )
-    {
-        /* TODO device lost */
-        msg_Err( display->obj, "SwapChain Present failed. (hr=0x%lX)", hr );
-    }
+void D3D11_LocalSwapchainSwap( void *opaque )
+{
+    struct d3d11_local_swapchain *display = opaque;
+    DXGI_LocalSwapchainSwap( display->sys );
 }
 
-bool LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
+void D3D11_LocalSwapchainSetMetadata( void *opaque, libvlc_video_metadata_type_t type, const void *metadata )
 {
     struct d3d11_local_swapchain *display = opaque;
-    if ( !UpdateSwapchain( display, cfg ) )
-        return false;
-    out->dxgi_format    = display->pixelFormat->formatTexture;
-    out->full_range     = display->colorspace->b_full_range;
-    out->colorspace     = (libvlc_video_color_space_t)     display->colorspace->color;
-    out->primaries      = (libvlc_video_color_primaries_t) display->colorspace->primaries;
-    out->transfer       = (libvlc_video_transfer_func_t)   display->colorspace->transfer;
-    return true;
+    DXGI_LocalSwapchainSetMetadata( display->sys, type, metadata );
 }
 
-bool LocalSwapchainWinstoreSize( void *opaque, uint32_t *width, uint32_t *height )
+bool D3D11_LocalSwapchainWinstoreSize( void *opaque, uint32_t *width, uint32_t *height )
 {
 #if VLC_WINSTORE_APP
     struct d3d11_local_swapchain *display = opaque;
     /* legacy UWP mode, the width/height was set in GUID_SWAPCHAIN_WIDTH/HEIGHT */
     UINT dataSize = sizeof(*width);
-    HRESULT hr = IDXGISwapChain_GetPrivateData(display->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, width);
+    HRESULT hr = IDXGISwapChain1_GetPrivateData(DXGI_GetSwapChain1(display->sys), &GUID_SWAPCHAIN_WIDTH, &dataSize, width);
     if (SUCCEEDED(hr)) {
         dataSize = sizeof(*height);
-        hr = IDXGISwapChain_GetPrivateData(display->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, height);
+        hr = IDXGISwapChain1_GetPrivateData(DXGI_GetSwapChain1(display->sys), &GUID_SWAPCHAIN_HEIGHT, &dataSize, height);
         return SUCCEEDED(hr);
     }
 #else
@@ -588,48 +226,17 @@ bool LocalSwapchainWinstoreSize( void *opaque, uint32_t *width, uint32_t *height
     return false;
 }
 
-bool LocalSwapchainStartEndRendering( void *opaque, bool enter )
+bool D3D11_LocalSwapchainStartEndRendering( void *opaque, bool enter )
 {
     struct d3d11_local_swapchain *display = opaque;
 
     if ( enter )
-        D3D11_ClearRenderTargets( display->d3d_dev, display->pixelFormat, display->swapchainTargetView );
+        D3D11_ClearRenderTargets( display->d3d_dev, DXGI_GetPixelFormat(display->sys), display->swapchainTargetView );
 
     return true;
 }
 
-void LocalSwapchainSetMetadata( void *opaque, libvlc_video_metadata_type_t type, const void *metadata )
-{
-    struct d3d11_local_swapchain *display = opaque;
-
-    assert(type == libvlc_video_metadata_frame_hdr10);
-    if ( type == libvlc_video_metadata_frame_hdr10 && metadata &&
-         display->send_metadata && display->dxgiswapChain4 )
-    {
-        const libvlc_video_frame_hdr10_metadata_t *p_hdr10 = metadata;
-        DXGI_HDR_METADATA_HDR10 hdr10 = { 0 };
-        hdr10.GreenPrimary[0] = p_hdr10->GreenPrimary[0];
-        hdr10.GreenPrimary[1] = p_hdr10->GreenPrimary[1];
-        hdr10.BluePrimary[0] = p_hdr10->BluePrimary[0];
-        hdr10.BluePrimary[1] = p_hdr10->BluePrimary[1];
-        hdr10.RedPrimary[0] = p_hdr10->RedPrimary[0];
-        hdr10.RedPrimary[1] = p_hdr10->RedPrimary[1];
-        hdr10.WhitePoint[0] = p_hdr10->WhitePoint[0];
-        hdr10.WhitePoint[1] = p_hdr10->WhitePoint[1];
-        hdr10.MinMasteringLuminance = p_hdr10->MinMasteringLuminance;
-        hdr10.MaxMasteringLuminance = p_hdr10->MaxMasteringLuminance;
-        hdr10.MaxContentLightLevel = p_hdr10->MaxContentLightLevel;
-        hdr10.MaxFrameAverageLightLevel = p_hdr10->MaxFrameAverageLightLevel;
-        if (memcmp(&display->hdr10, &hdr10, sizeof(hdr10)))
-        {
-            memcpy(&display->hdr10, &hdr10, sizeof(hdr10));
-            IDXGISwapChain4_SetHDRMetaData( display->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10,
-                                            sizeof( &display->hdr10 ), &display->hdr10 );
-        }
-    }
-}
-
-bool LocalSwapchainSelectPlane( void *opaque, size_t plane )
+bool D3D11_LocalSwapchainSelectPlane( void *opaque, size_t plane )
 {
     struct d3d11_local_swapchain *display = opaque;
     if (!display->swapchainTargetView[plane])
@@ -639,35 +246,34 @@ bool LocalSwapchainSelectPlane( void *opaque, size_t plane )
     return true;
 }
 
-void *CreateLocalSwapchainHandleHwnd(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d_dev)
+void *D3D11_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))
         return NULL;
 
+    display->sys = DXGI_CreateLocalSwapchainHandleHwnd(o, hwnd);
+    if (unlikely(display->sys == NULL))
+        return NULL;
+
     display->obj = o;
-#if !VLC_WINSTORE_APP
-    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_HWND;
-    display->swapchainSurface.hwnd = hwnd;
-#else // VLC_WINSTORE_APP
-    VLC_UNUSED(hwnd);
-#endif // VLC_WINSTORE_APP
     display->d3d_dev = d3d_dev;
 
     return display;
 }
 
 #ifdef HAVE_DCOMP_H
-void *CreateLocalSwapchainHandleDComp(vlc_object_t *o, void* dcompDevice, void* dcompVisual, d3d11_device_t *d3d_dev)
+void *D3D11_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->sys = DXGI_CreateLocalSwapchainHandleDComp(o, dcompDevice, dcompVisual);
+    if (unlikely(display->sys == 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;
diff --git a/modules/video_output/win32/d3d11_swapchain.h b/modules/video_output/win32/d3d11_swapchain.h
index 69b2e9a80c..f5dd4c4cf4 100644
--- a/modules/video_output/win32/d3d11_swapchain.h
+++ b/modules/video_output/win32/d3d11_swapchain.h
@@ -25,20 +25,20 @@
 #define VLC_D3D11_SWAPCHAIN_H
 
 #include <vlc_common.h>
-#include <vlc_codec.h>
+#include "dxgi_swapchain.h"
 #include "../../video_chroma/d3d11_fmt.h"
 
-void *CreateLocalSwapchainHandleHwnd(vlc_object_t *, HWND, d3d11_device_t *d3d_dev);
+void *D3D11_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);
+void *D3D11_CreateLocalSwapchainHandleDComp(vlc_object_t *, void* dcompDevice, void* dcompVisual, d3d11_device_t *d3d_dev);
 #endif
 
-void LocalSwapchainCleanupDevice( void *opaque );
-void LocalSwapchainSwap( void *opaque );
-bool LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out );
-bool LocalSwapchainStartEndRendering( void *opaque, bool enter );
-void LocalSwapchainSetMetadata( void *opaque, libvlc_video_metadata_type_t, const void * );
-bool LocalSwapchainSelectPlane( void *opaque, size_t plane );
-bool LocalSwapchainWinstoreSize( void *opaque, uint32_t *, uint32_t * );
+void D3D11_LocalSwapchainCleanupDevice( void *opaque );
+bool D3D11_LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out );
+bool D3D11_LocalSwapchainStartEndRendering( void *opaque, bool enter );
+bool D3D11_LocalSwapchainSelectPlane( void *opaque, size_t plane );
+bool D3D11_LocalSwapchainWinstoreSize( void *opaque, uint32_t *, uint32_t * );
+void D3D11_LocalSwapchainSwap( void *opaque );
+void D3D11_LocalSwapchainSetMetadata( void *opaque, libvlc_video_metadata_type_t, const void * );
 
 #endif /* VLC_D3D11_SWAPCHAIN_H */
diff --git a/modules/video_output/win32/direct3d11.c b/modules/video_output/win32/direct3d11.c
index 130cbf5f96..94364cf7cd 100644
--- a/modules/video_output/win32/direct3d11.c
+++ b/modules/video_output/win32/direct3d11.c
@@ -399,17 +399,17 @@ static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
         /* use our internal swapchain callbacks */
 #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);
+            sys->outside_opaque      = D3D11_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);
+            sys->outside_opaque      = D3D11_CreateLocalSwapchainHandleHwnd(VLC_OBJECT(vd), sys->sys.hvideownd, sys->d3d_dev);
         if (unlikely(sys->outside_opaque == NULL))
             goto error;
-        sys->updateOutputCb      = LocalSwapchainUpdateOutput;
-        sys->swapCb              = LocalSwapchainSwap;
-        sys->startEndRenderingCb = LocalSwapchainStartEndRendering;
-        sys->sendMetadataCb      = LocalSwapchainSetMetadata;
-        sys->selectPlaneCb       = LocalSwapchainSelectPlane;
+        sys->updateOutputCb      = D3D11_LocalSwapchainUpdateOutput;
+        sys->swapCb              = D3D11_LocalSwapchainSwap;
+        sys->startEndRenderingCb = D3D11_LocalSwapchainStartEndRendering;
+        sys->sendMetadataCb      = D3D11_LocalSwapchainSetMetadata;
+        sys->selectPlaneCb       = D3D11_LocalSwapchainSelectPlane;
     }
 
 #if !VLC_WINSTORE_APP
@@ -663,12 +663,12 @@ static void Prepare(vout_display_t *vd, picture_t *picture,
 
     d3d11_device_lock( sys->d3d_dev );
 #if VLC_WINSTORE_APP
-    if ( sys->swapCb == LocalSwapchainSwap )
+    if ( sys->swapCb == D3D11_LocalSwapchainSwap )
     {
         /* legacy UWP mode, the width/height was set in GUID_SWAPCHAIN_WIDTH/HEIGHT */
         uint32_t i_width;
         uint32_t i_height;
-        if (LocalSwapchainWinstoreSize( sys->outside_opaque, &i_width, &i_height ))
+        if (D3D11_LocalSwapchainWinstoreSize( sys->outside_opaque, &i_width, &i_height ))
         {
             if (i_width != vd->cfg->display.width || i_height != vd->cfg->display.height)
                 vout_display_SetSize(vd, i_width, i_height);
@@ -794,8 +794,8 @@ static int Direct3D11Open(vout_display_t *vd, video_format_t *fmtp, vlc_video_co
         }
         if (err != VLC_SUCCESS)
         {
-            if ( sys->swapCb == LocalSwapchainSwap )
-                LocalSwapchainCleanupDevice( sys->outside_opaque );
+            if ( sys->swapCb == D3D11_LocalSwapchainSwap )
+                D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
             return err;
         }
     }
@@ -820,8 +820,8 @@ static int Direct3D11Open(vout_display_t *vd, video_format_t *fmtp, vlc_video_co
 
     if (Direct3D11CreateGenericResources(vd)) {
         msg_Err(vd, "Failed to allocate resources");
-        if ( sys->swapCb == LocalSwapchainSwap )
-            LocalSwapchainCleanupDevice( sys->outside_opaque );
+        if ( sys->swapCb == D3D11_LocalSwapchainSwap )
+            D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
         return VLC_EGENERIC;
     }
 
@@ -963,8 +963,8 @@ static void Direct3D11Close(vout_display_t *vd)
 
     Direct3D11DestroyResources(vd);
 
-    if ( sys->swapCb == LocalSwapchainSwap )
-        LocalSwapchainCleanupDevice( sys->outside_opaque );
+    if ( sys->swapCb == D3D11_LocalSwapchainSwap )
+        D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
 
     if (sys->d3d_dev && sys->d3d_dev == &sys->local_d3d_dev->d3d_dev)
         D3D11_ReleaseDevice( sys->local_d3d_dev );
diff --git a/modules/video_output/win32/dxgi_swapchain.c b/modules/video_output/win32/dxgi_swapchain.c
new file mode 100644
index 0000000000..334e6503ae
--- /dev/null
+++ b/modules/video_output/win32/dxgi_swapchain.c
@@ -0,0 +1,541 @@
+/*****************************************************************************
+ * dxgi_swapchain.c: DXGI swapchain handled by the display module
+ *****************************************************************************
+ * Copyright (C) 2014-2021 VLC authors and VideoLAN
+ *
+ * Authors: Martell Malone <martellmalone at gmail.com>
+ *          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 <assert.h>
+
+#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0601 // _WIN32_WINNT_WIN7
+# undef _WIN32_WINNT
+# define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
+#endif
+
+#include <vlc_es.h>
+
+#define COBJMACROS
+
+#ifdef HAVE_DCOMP_H
+#  include "dcomp_wrapper.h"
+#endif
+
+#include <initguid.h>
+#include "dxgi_swapchain.h"
+
+#ifdef HAVE_DXGI1_6_H
+# include <dxgi1_6.h>
+#endif
+
+#include "../../video_chroma/dxgi_fmt.h"
+
+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;
+    video_color_axis        axis;
+    video_color_primaries_t primaries;
+    video_transfer_func_t   transfer;
+    video_color_space_t     color;
+    bool                    b_full_range;
+} dxgi_color_space;
+
+struct dxgi_swapchain
+{
+    vlc_object_t           *obj;
+
+    const d3d_format_t     *pixelFormat;
+    const dxgi_color_space *colorspace;
+
+    swapchain_surface_type  swapchainSurfaceType;
+    union {
+#if !VLC_WINSTORE_APP
+        HWND                hwnd;
+#endif /* !VLC_WINSTORE_APP */
+#ifdef HAVE_DCOMP_H
+        struct {
+            void*           device; // IDCompositionDevice
+            void*           visual; // IDCompositionVisual
+        } dcomp;
+#endif // HAVE_DCOMP_H
+    } swapchainSurface;
+
+    IDXGISwapChain1        *dxgiswapChain;   /* DXGI 1.2 swap chain */
+    IDXGISwapChain4        *dxgiswapChain4;  /* DXGI 1.5 for HDR metadata */
+    bool                    send_metadata;
+    DXGI_HDR_METADATA_HDR10 hdr10;
+
+    bool                   logged_capabilities;
+};
+
+DEFINE_GUID(GUID_SWAPCHAIN_WIDTH,  0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
+DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
+
+#define DXGI_COLOR_RANGE_FULL   1 /* 0-255 */
+#define DXGI_COLOR_RANGE_STUDIO 0 /* 16-235 */
+
+#define TRANSFER_FUNC_10    TRANSFER_FUNC_LINEAR
+#define TRANSFER_FUNC_22    TRANSFER_FUNC_SRGB
+#define TRANSFER_FUNC_2084  TRANSFER_FUNC_SMPTE_ST2084
+
+#define COLOR_PRIMARIES_BT601  COLOR_PRIMARIES_BT601_525
+
+static const dxgi_color_space color_spaces[] = {
+#define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
+    { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
+      #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
+      COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
+      COLOR_SPACE_BT##PRIMARIES, DXGI_COLOR_RANGE_##RANGE},
+
+    DXGIMAP(RGB,   FULL,     22,    NONE,   709)
+    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   601)
+    DXGIMAP(YCBCR, FULL,     22,    LEFT,   601)
+    DXGIMAP(RGB,   FULL,     10,    NONE,   709)
+    DXGIMAP(RGB,   STUDIO,   22,    NONE,   709)
+    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   709)
+    DXGIMAP(YCBCR, FULL,     22,    LEFT,   709)
+    DXGIMAP(RGB,   STUDIO,   22,    NONE,  2020)
+    DXGIMAP(YCBCR, STUDIO,   22,    LEFT,  2020)
+    DXGIMAP(YCBCR, FULL,     22,    LEFT,  2020)
+    DXGIMAP(YCBCR, STUDIO,   22, TOPLEFT,  2020)
+    DXGIMAP(RGB,   FULL,     22,    NONE,  2020)
+    DXGIMAP(RGB,   FULL,   2084,    NONE,  2020)
+    DXGIMAP(YCBCR, STUDIO, 2084,    LEFT,  2020)
+    DXGIMAP(RGB,   STUDIO, 2084,    NONE,  2020)
+    DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT,  2020)
+    DXGIMAP(YCBCR, STUDIO,  HLG, TOPLEFT,  2020)
+    DXGIMAP(YCBCR, FULL,    HLG, TOPLEFT,  2020)
+    /*DXGIMAP(YCBCR, FULL,     22,    NONE,  2020, 601)*/
+    {DXGI_COLOR_SPACE_RESERVED, NULL, 0, 0, 0, 0, 0},
+#undef DXGIMAP
+};
+
+#ifdef HAVE_DXGI1_6_H
+static bool canHandleConversion(const dxgi_color_space *src, const dxgi_color_space *dst)
+{
+    if (src == dst)
+        return true;
+    if (src->primaries == COLOR_PRIMARIES_BT2020)
+        return true; /* we can convert BT2020 to 2020 or 709 */
+    if (dst->transfer == TRANSFER_FUNC_BT709)
+        return true; /* we can handle anything to 709 */
+    return false; /* let Windows do the rest */
+}
+#endif
+
+void DXGI_SelectSwapchainColorspace(struct dxgi_swapchain *display, const libvlc_video_render_cfg_t *cfg)
+{
+    HRESULT hr;
+    int best = 0;
+    int score, best_score = 0;
+    UINT support;
+    IDXGISwapChain3 *dxgiswapChain3 = NULL;
+    hr = IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain3, (void **)&dxgiswapChain3);
+    if (FAILED(hr)) {
+        msg_Warn(display->obj, "could not get a IDXGISwapChain3");
+        goto done;
+    }
+
+    /* pick the best output based on color support and transfer */
+    /* TODO support YUV output later */
+    best = -1;
+    for (int i=0; color_spaces[i].name; ++i)
+    {
+        hr = IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3, color_spaces[i].dxgi, &support);
+        if (SUCCEEDED(hr) && support) {
+            if (!display->logged_capabilities)
+                msg_Dbg(display->obj, "supports colorspace %s", color_spaces[i].name);
+            score = 0;
+            if (color_spaces[i].primaries == (video_color_primaries_t) cfg->primaries)
+                score++;
+            if (color_spaces[i].color == (video_color_space_t) cfg->colorspace)
+                score += 2; /* we don't want to translate color spaces */
+            if (color_spaces[i].transfer == (video_transfer_func_t) cfg->transfer ||
+                /* favor 2084 output for HLG source */
+                (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && cfg->transfer == TRANSFER_FUNC_HLG))
+                score++;
+            if (color_spaces[i].b_full_range == cfg->full_range)
+                score++;
+            if (score > best_score || (score && best == -1)) {
+                best = i;
+                best_score = score;
+            }
+        }
+    }
+    display->logged_capabilities = true;
+
+    if (best == -1)
+    {
+        best = 0;
+        msg_Warn(display->obj, "no matching colorspace found force %s", color_spaces[best].name);
+    }
+
+    IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&display->dxgiswapChain4);
+
+#ifdef HAVE_DXGI1_6_H
+    IDXGIOutput *dxgiOutput = NULL;
+
+    if (SUCCEEDED(IDXGISwapChain_GetContainingOutput( display->dxgiswapChain, &dxgiOutput )))
+    {
+        IDXGIOutput6 *dxgiOutput6 = NULL;
+        if (SUCCEEDED(IDXGIOutput_QueryInterface( dxgiOutput, &IID_IDXGIOutput6, (void **)&dxgiOutput6 )))
+        {
+            DXGI_OUTPUT_DESC1 desc1;
+            if (SUCCEEDED(IDXGIOutput6_GetDesc1( dxgiOutput6, &desc1 )))
+            {
+                const dxgi_color_space *csp = NULL;
+                for (int i=0; color_spaces[i].name; ++i)
+                {
+                    if (color_spaces[i].dxgi == desc1.ColorSpace)
+                    {
+                        if (!canHandleConversion(&color_spaces[best], &color_spaces[i]))
+                            msg_Warn(display->obj, "Can't handle conversion to screen format %s", color_spaces[i].name);
+                        else
+                        {
+                            best = i;
+                            csp = &color_spaces[i];
+                        }
+                        break;
+                    }
+                }
+
+                msg_Dbg(display->obj, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1.MaxFullFrameLuminance, csp?csp->name:"unknown", desc1.BitsPerColor);
+                //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
+            }
+            IDXGIOutput6_Release( dxgiOutput6 );
+        }
+        IDXGIOutput_Release( dxgiOutput );
+    }
+#endif
+
+    hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
+    if (SUCCEEDED(hr))
+        msg_Dbg(display->obj, "using colorspace %s", color_spaces[best].name);
+    else
+        msg_Err(display->obj, "Failed to set colorspace %s. (hr=0x%lX)", color_spaces[best].name, hr);
+done:
+    display->colorspace = &color_spaces[best];
+    display->send_metadata = color_spaces[best].transfer == (video_transfer_func_t) cfg->transfer &&
+                             color_spaces[best].primaries == (video_color_primaries_t) cfg->primaries &&
+                             color_spaces[best].color == (video_color_space_t) cfg->colorspace;
+    if (dxgiswapChain3)
+        IDXGISwapChain3_Release(dxgiswapChain3);
+}
+
+#if !VLC_WINSTORE_APP
+static void FillSwapChainDesc(struct dxgi_swapchain *display, UINT width, UINT height, DXGI_SWAP_CHAIN_DESC1 *out)
+{
+    ZeroMemory(out, sizeof(*out));
+    out->BufferCount = DXGI_SWAP_FRAME_COUNT;
+    out->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+    out->SampleDesc.Count = 1;
+    out->SampleDesc.Quality = 0;
+    out->Width = width;
+    out->Height = height;
+    out->Format = display->pixelFormat->formatTexture;
+    //out->Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
+
+    bool isWin10OrGreater = false;
+    HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+    if (likely(hKernel32 != NULL))
+        isWin10OrGreater = GetProcAddress(hKernel32, "GetSystemCpuSetInformation") != NULL;
+    if (isWin10OrGreater)
+        out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
+    else
+    {
+        bool isWin80OrGreater = false;
+        if (likely(hKernel32 != NULL))
+            isWin80OrGreater = GetProcAddress(hKernel32, "CheckTokenCapability") != NULL;
+        if (isWin80OrGreater)
+            out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
+        else
+        {
+            out->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+            out->BufferCount = 1;
+        }
+    }
+}
+
+static void DXGI_CreateSwapchainHwnd(struct dxgi_swapchain *display,
+                              IDXGIAdapter *dxgiadapter, IUnknown *pFactoryDevice,
+                              UINT width, UINT height)
+{
+    vlc_assert(display->swapchainSurfaceType == SWAPCHAIN_SURFACE_HWND);
+    if (display->swapchainSurface.hwnd == NULL)
+    {
+        msg_Err(display->obj, "missing a HWND to create the swapchain");
+        return;
+    }
+
+    DXGI_SWAP_CHAIN_DESC1 scd;
+    FillSwapChainDesc(display, width, height, &scd);
+
+    IDXGIFactory2 *dxgifactory;
+    HRESULT hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
+    if (FAILED(hr)) {
+        msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
+        return;
+    }
+
+    hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, pFactoryDevice,
+                                              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, pFactoryDevice,
+                                                  display->swapchainSurface.hwnd, &scd,
+                                                  NULL, NULL, &display->dxgiswapChain);
+    }
+    IDXGIFactory2_Release(dxgifactory);
+    if (FAILED(hr)) {
+        msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
+    }
+}
+
+#ifdef HAVE_DCOMP_H
+static void DXGI_CreateSwapchainDComp(struct dxgi_swapchain *display,
+                               IDXGIAdapter *dxgiadapter, IUnknown *pFactoryDevice,
+                               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;
+
+    IDXGIFactory2 *dxgifactory;
+    HRESULT hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
+    if (FAILED(hr)) {
+        msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
+        return;
+    }
+
+    hr = IDXGIFactory2_CreateSwapChainForComposition(dxgifactory, pFactoryDevice,
+                                                    &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, pFactoryDevice,
+                                                        &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 */
+
+void DXGI_LocalSwapchainSwap( struct dxgi_swapchain *display )
+{
+    DXGI_PRESENT_PARAMETERS presentParams = { 0 };
+
+    HRESULT hr = IDXGISwapChain1_Present1( display->dxgiswapChain, 0, 0, &presentParams );
+    if ( hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET )
+    {
+        /* TODO device lost */
+        msg_Err( display->obj, "SwapChain Present failed. (hr=0x%lX)", hr );
+    }
+}
+
+void DXGI_LocalSwapchainSetMetadata( struct dxgi_swapchain *display, libvlc_video_metadata_type_t type, const void *metadata )
+{
+    assert(type == libvlc_video_metadata_frame_hdr10);
+    if ( type == libvlc_video_metadata_frame_hdr10 && metadata &&
+         display->send_metadata && display->dxgiswapChain4 )
+    {
+        const libvlc_video_frame_hdr10_metadata_t *p_hdr10 = metadata;
+        DXGI_HDR_METADATA_HDR10 hdr10 = { 0 };
+        hdr10.GreenPrimary[0] = p_hdr10->GreenPrimary[0];
+        hdr10.GreenPrimary[1] = p_hdr10->GreenPrimary[1];
+        hdr10.BluePrimary[0] = p_hdr10->BluePrimary[0];
+        hdr10.BluePrimary[1] = p_hdr10->BluePrimary[1];
+        hdr10.RedPrimary[0] = p_hdr10->RedPrimary[0];
+        hdr10.RedPrimary[1] = p_hdr10->RedPrimary[1];
+        hdr10.WhitePoint[0] = p_hdr10->WhitePoint[0];
+        hdr10.WhitePoint[1] = p_hdr10->WhitePoint[1];
+        hdr10.MinMasteringLuminance = p_hdr10->MinMasteringLuminance;
+        hdr10.MaxMasteringLuminance = p_hdr10->MaxMasteringLuminance;
+        hdr10.MaxContentLightLevel = p_hdr10->MaxContentLightLevel;
+        hdr10.MaxFrameAverageLightLevel = p_hdr10->MaxFrameAverageLightLevel;
+        if (memcmp(&display->hdr10, &hdr10, sizeof(hdr10)))
+        {
+            memcpy(&display->hdr10, &hdr10, sizeof(hdr10));
+            IDXGISwapChain4_SetHDRMetaData( display->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10,
+                                            sizeof( &display->hdr10 ), &display->hdr10 );
+        }
+    }
+}
+
+struct dxgi_swapchain *DXGI_CreateLocalSwapchainHandleHwnd(vlc_object_t *o, HWND hwnd)
+{
+    struct dxgi_swapchain *display = vlc_obj_calloc(o, 1, sizeof(*display));
+    if (unlikely(display == NULL))
+        return NULL;
+
+    display->obj = o;
+#if !VLC_WINSTORE_APP
+    display->swapchainSurfaceType = SWAPCHAIN_SURFACE_HWND;
+    display->swapchainSurface.hwnd = hwnd;
+#else // VLC_WINSTORE_APP
+    VLC_UNUSED(hwnd);
+#endif // VLC_WINSTORE_APP
+
+    return display;
+}
+
+#ifdef HAVE_DCOMP_H
+struct dxgi_swapchain *DXGI_CreateLocalSwapchainHandleDComp(vlc_object_t *o, void* dcompDevice, void* dcompVisual)
+{
+    struct dxgi_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;
+
+    return display;
+}
+#endif
+
+void DXGI_LocalSwapchainCleanupDevice( struct dxgi_swapchain *display )
+{
+    if (display->dxgiswapChain4)
+    {
+        IDXGISwapChain4_Release(display->dxgiswapChain4);
+        display->dxgiswapChain4 = NULL;
+    }
+    if (display->dxgiswapChain)
+    {
+        IDXGISwapChain_Release(display->dxgiswapChain);
+        display->dxgiswapChain = NULL;
+    }
+}
+
+void DXGI_SwapchainUpdateOutput( struct dxgi_swapchain *display, libvlc_video_output_cfg_t *out )
+{
+    out->dxgi_format    = display->pixelFormat->formatTexture;
+    out->full_range     = display->colorspace->b_full_range;
+    out->colorspace     = (libvlc_video_color_space_t)     display->colorspace->color;
+    out->primaries      = (libvlc_video_color_primaries_t) display->colorspace->primaries;
+    out->transfer       = (libvlc_video_transfer_func_t)   display->colorspace->transfer;
+}
+
+bool DXGI_UpdateSwapChain( struct dxgi_swapchain *display, IDXGIAdapter *dxgiadapter,
+                           IUnknown *pFactoryDevice,
+                           const d3d_format_t *newPixelFormat, const libvlc_video_render_cfg_t *cfg )
+{
+#if !VLC_WINSTORE_APP
+    if (display->dxgiswapChain != NULL && display->pixelFormat != newPixelFormat)
+    {
+        // the pixel format changed, we need a new swapchain
+        IDXGISwapChain_Release(display->dxgiswapChain);
+        display->dxgiswapChain = NULL;
+        display->logged_capabilities = false;
+    }
+
+    if ( display->dxgiswapChain == NULL )
+    {
+        display->pixelFormat = newPixelFormat;
+
+#ifdef HAVE_DCOMP_H
+        if (display->swapchainSurfaceType == SWAPCHAIN_SURFACE_DCOMP)
+            DXGI_CreateSwapchainDComp(display, dxgiadapter, pFactoryDevice,
+                                      cfg->width, cfg->height);
+        else // SWAPCHAIN_TARGET_HWND
+#endif
+            DXGI_CreateSwapchainHwnd(display, dxgiadapter, pFactoryDevice,
+                                     cfg->width, cfg->height);
+
+    }
+#else /* VLC_WINSTORE_APP */
+    if ( display->dxgiswapChain == NULL )
+    {
+        display->dxgiswapChain = (void*)(uintptr_t)var_InheritInteger(display->obj, "winrt-swapchain");
+    }
+#endif /* VLC_WINSTORE_APP */
+    if (display->dxgiswapChain == NULL)
+        return false;
+
+    /* TODO detect is the size is the same as the output and switch to fullscreen mode */
+    HRESULT hr;
+    hr = IDXGISwapChain_ResizeBuffers( display->dxgiswapChain, 0, cfg->width, cfg->height,
+                                        DXGI_FORMAT_UNKNOWN, 0 );
+    if ( FAILED( hr ) ) {
+        msg_Err( display->obj, "Failed to resize the backbuffer. (hr=0x%lX)", hr );
+        return false;
+    }
+
+    DXGI_SelectSwapchainColorspace(display, cfg);
+    return true;
+}
+
+IDXGISwapChain1 *DXGI_GetSwapChain1( struct dxgi_swapchain *display )
+{
+    return display->dxgiswapChain;
+}
+
+IDXGISwapChain4 *DXGI_GetSwapChain4( struct dxgi_swapchain *display )
+{
+    return display->dxgiswapChain4;
+}
+
+const d3d_format_t  *DXGI_GetPixelFormat( struct dxgi_swapchain *display )
+{
+    return display->pixelFormat;
+}
diff --git a/modules/video_output/win32/dxgi_swapchain.h b/modules/video_output/win32/dxgi_swapchain.h
new file mode 100644
index 0000000000..77910570ae
--- /dev/null
+++ b/modules/video_output/win32/dxgi_swapchain.h
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * dxgi_swapchain.h: DXGI swapchain handled by the display module
+ *****************************************************************************
+ * Copyright (C) 2014-2021 VLC authors and VideoLAN
+ *
+ * Authors: Martell Malone <martellmalone at gmail.com>
+ *          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.
+ *****************************************************************************/
+
+#ifndef VLC_DXGI_SWAPCHAIN_H
+#define VLC_DXGI_SWAPCHAIN_H
+
+#include <vlc_common.h>
+
+#include <vlc/libvlc.h>
+#include <vlc/libvlc_picture.h>
+#include <vlc/libvlc_media.h>
+#include <vlc/libvlc_renderer_discoverer.h>
+#include <vlc/libvlc_media_player.h>
+
+#include <dxgi1_5.h>
+#include "../../video_chroma/dxgi_fmt.h"
+
+#define DXGI_SWAP_FRAME_COUNT   3
+
+struct dxgi_swapchain;
+
+struct dxgi_swapchain *DXGI_CreateLocalSwapchainHandleHwnd(vlc_object_t *, HWND);
+
+#ifdef HAVE_DCOMP_H
+struct dxgi_swapchain *DXGI_CreateLocalSwapchainHandleDComp(vlc_object_t *,
+                                           void /*IDCompositionDevice*/ * dcompDevice,
+                                           void /*IDCompositionVisual*/ * dcompVisual);
+#endif
+
+IDXGISwapChain1 *DXGI_GetSwapChain1( struct dxgi_swapchain * );
+IDXGISwapChain4 *DXGI_GetSwapChain4( struct dxgi_swapchain * );
+const d3d_format_t  *DXGI_GetPixelFormat( struct dxgi_swapchain * );
+
+void DXGI_SelectSwapchainColorspace( struct dxgi_swapchain *, const libvlc_video_render_cfg_t * );
+void DXGI_LocalSwapchainCleanupDevice( struct dxgi_swapchain * );
+void DXGI_SwapchainUpdateOutput( struct dxgi_swapchain *, libvlc_video_output_cfg_t * );
+bool DXGI_UpdateSwapChain( struct dxgi_swapchain *, IDXGIAdapter *,
+                           IUnknown *pFactoryDevice,
+                           const d3d_format_t *, const libvlc_video_render_cfg_t * );
+
+void DXGI_LocalSwapchainSwap( struct dxgi_swapchain * );
+void DXGI_LocalSwapchainSetMetadata( struct dxgi_swapchain *, libvlc_video_metadata_type_t, const void * );
+
+#endif /* VLC_D3D11_SWAPCHAIN_H */



More information about the vlc-commits mailing list