direct3d11: only send HDR metadata when we don't tone map the source
[vlc.git] / modules / video_output / win32 / d3d11_swapchain.c
1 /*****************************************************************************
2  * d3d11_swapchain.c: Direct3D11 swapchain handled by the display module
3  *****************************************************************************
4  * Copyright (C) 2014-2019 VLC authors and VideoLAN
5  *
6  * Authors: Martell Malone <martellmalone@gmail.com>
7  *          Steve Lhomme <robux4@gmail.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc/libvlc.h>
29 #include <vlc/libvlc_picture.h>
30 #include <vlc/libvlc_media.h>
31 #include <vlc/libvlc_renderer_discoverer.h>
32 #include <vlc/libvlc_media_player.h>
33
34 #include <assert.h>
35
36 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0601 // _WIN32_WINNT_WIN7
37 # undef _WIN32_WINNT
38 # define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
39 #endif
40
41 #include <windows.h>
42
43 #define COBJMACROS
44 #include <initguid.h>
45 #include <d3d11.h>
46 #ifdef HAVE_DXGI1_6_H
47 # include <dxgi1_6.h>
48 #else
49 # include <dxgi1_5.h>
50 #endif
51
52 #include "d3d11_swapchain.h"
53 #include "d3d11_shaders.h"
54
55 typedef enum video_color_axis {
56     COLOR_AXIS_RGB,
57     COLOR_AXIS_YCBCR,
58 } video_color_axis;
59
60 typedef struct {
61     DXGI_COLOR_SPACE_TYPE   dxgi;
62     const char              *name;
63     video_color_axis        axis;
64     video_color_primaries_t primaries;
65     video_transfer_func_t   transfer;
66     video_color_space_t     color;
67     bool                    b_full_range;
68 } dxgi_color_space;
69
70 struct d3d11_local_swapchain
71 {
72     vlc_object_t           *obj;
73     d3d11_device_t         *d3d_dev;
74
75     const d3d_format_t     *pixelFormat;
76     const dxgi_color_space *colorspace;
77
78 #if !VLC_WINSTORE_APP
79     HWND                   swapchainHwnd;
80 #endif /* !VLC_WINSTORE_APP */
81     IDXGISwapChain1        *dxgiswapChain;   /* DXGI 1.2 swap chain */
82     IDXGISwapChain4        *dxgiswapChain4;  /* DXGI 1.5 for HDR metadata */
83     bool                    send_metadata;
84     DXGI_HDR_METADATA_HDR10 hdr10;
85
86     ID3D11RenderTargetView *swapchainTargetView[D3D11_MAX_RENDER_TARGET];
87
88     bool                   logged_capabilities;
89 };
90
91 DEFINE_GUID(GUID_SWAPCHAIN_WIDTH,  0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
92 DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
93
94 #define DXGI_COLOR_RANGE_FULL   1 /* 0-255 */
95 #define DXGI_COLOR_RANGE_STUDIO 0 /* 16-235 */
96
97 #define TRANSFER_FUNC_10    TRANSFER_FUNC_LINEAR
98 #define TRANSFER_FUNC_22    TRANSFER_FUNC_SRGB
99 #define TRANSFER_FUNC_2084  TRANSFER_FUNC_SMPTE_ST2084
100
101 #define COLOR_PRIMARIES_BT601  COLOR_PRIMARIES_BT601_525
102
103 static const dxgi_color_space color_spaces[] = {
104 #define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
105     { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
106       #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
107       COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
108       COLOR_SPACE_BT##PRIMARIES, DXGI_COLOR_RANGE_##RANGE},
109
110     DXGIMAP(RGB,   FULL,     22,    NONE,   709)
111     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   601)
112     DXGIMAP(YCBCR, FULL,     22,    LEFT,   601)
113     DXGIMAP(RGB,   FULL,     10,    NONE,   709)
114     DXGIMAP(RGB,   STUDIO,   22,    NONE,   709)
115     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   709)
116     DXGIMAP(YCBCR, FULL,     22,    LEFT,   709)
117     DXGIMAP(RGB,   STUDIO,   22,    NONE,  2020)
118     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,  2020)
119     DXGIMAP(YCBCR, FULL,     22,    LEFT,  2020)
120     DXGIMAP(YCBCR, STUDIO,   22, TOPLEFT,  2020)
121     DXGIMAP(RGB,   FULL,     22,    NONE,  2020)
122     DXGIMAP(RGB,   FULL,   2084,    NONE,  2020)
123     DXGIMAP(YCBCR, STUDIO, 2084,    LEFT,  2020)
124     DXGIMAP(RGB,   STUDIO, 2084,    NONE,  2020)
125     DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT,  2020)
126     DXGIMAP(YCBCR, STUDIO,  HLG, TOPLEFT,  2020)
127     DXGIMAP(YCBCR, FULL,    HLG, TOPLEFT,  2020)
128     /*DXGIMAP(YCBCR, FULL,     22,    NONE,  2020, 601)*/
129     {DXGI_COLOR_SPACE_RESERVED, NULL, 0, 0, 0, 0, 0},
130 #undef DXGIMAP
131 };
132
133 #ifdef HAVE_DXGI1_6_H
134 static bool canHandleConversion(const dxgi_color_space *src, const dxgi_color_space *dst)
135 {
136     if (src == dst)
137         return true;
138     if (src->primaries == COLOR_PRIMARIES_BT2020)
139         return true; /* we can convert BT2020 to 2020 or 709 */
140     if (dst->transfer == TRANSFER_FUNC_BT709)
141         return true; /* we can handle anything to 709 */
142     return false; /* let Windows do the rest */
143 }
144 #endif
145
146 static void SelectSwapchainColorspace(struct d3d11_local_swapchain *display, const libvlc_video_render_cfg_t *cfg)
147 {
148     HRESULT hr;
149     int best = 0;
150     int score, best_score = 0;
151     UINT support;
152     IDXGISwapChain3 *dxgiswapChain3 = NULL;
153     hr = IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain3, (void **)&dxgiswapChain3);
154     if (FAILED(hr)) {
155         msg_Warn(display->obj, "could not get a IDXGISwapChain3");
156         goto done;
157     }
158
159     /* pick the best output based on color support and transfer */
160     /* TODO support YUV output later */
161     best = -1;
162     for (int i=0; color_spaces[i].name; ++i)
163     {
164         hr = IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3, color_spaces[i].dxgi, &support);
165         if (SUCCEEDED(hr) && support) {
166             if (!display->logged_capabilities)
167                 msg_Dbg(display->obj, "supports colorspace %s", color_spaces[i].name);
168             score = 0;
169             if (color_spaces[i].primaries == (video_color_primaries_t) cfg->primaries)
170                 score++;
171             if (color_spaces[i].color == (video_color_space_t) cfg->colorspace)
172                 score += 2; /* we don't want to translate color spaces */
173             if (color_spaces[i].transfer == (video_transfer_func_t) cfg->transfer ||
174                 /* favor 2084 output for HLG source */
175                 (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && cfg->transfer == TRANSFER_FUNC_HLG))
176                 score++;
177             if (color_spaces[i].b_full_range == cfg->full_range)
178                 score++;
179             if (score > best_score || (score && best == -1)) {
180                 best = i;
181                 best_score = score;
182             }
183         }
184     }
185     display->logged_capabilities = true;
186
187     if (best == -1)
188     {
189         best = 0;
190         msg_Warn(display->obj, "no matching colorspace found force %s", color_spaces[best].name);
191     }
192
193     IDXGISwapChain_QueryInterface( display->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&display->dxgiswapChain4);
194
195 #ifdef HAVE_DXGI1_6_H
196     IDXGIOutput *dxgiOutput = NULL;
197
198     if (SUCCEEDED(IDXGISwapChain_GetContainingOutput( display->dxgiswapChain, &dxgiOutput )))
199     {
200         IDXGIOutput6 *dxgiOutput6 = NULL;
201         if (SUCCEEDED(IDXGIOutput_QueryInterface( dxgiOutput, &IID_IDXGIOutput6, (void **)&dxgiOutput6 )))
202         {
203             DXGI_OUTPUT_DESC1 desc1;
204             if (SUCCEEDED(IDXGIOutput6_GetDesc1( dxgiOutput6, &desc1 )))
205             {
206                 const dxgi_color_space *csp = NULL;
207                 for (int i=0; color_spaces[i].name; ++i)
208                 {
209                     if (color_spaces[i].dxgi == desc1.ColorSpace)
210                     {
211                         if (!canHandleConversion(&color_spaces[best], &color_spaces[i]))
212                             msg_Warn(display->obj, "Can't handle conversion to screen format %s", color_spaces[i].name);
213                         else
214                         {
215                             best = i;
216                             csp = &color_spaces[i];
217                         }
218                         break;
219                     }
220                 }
221
222                 msg_Dbg(display->obj, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1.MaxFullFrameLuminance, csp?csp->name:"unknown", desc1.BitsPerColor);
223                 //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
224             }
225             IDXGIOutput6_Release( dxgiOutput6 );
226         }
227         IDXGIOutput_Release( dxgiOutput );
228     }
229 #endif
230
231     hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
232     if (SUCCEEDED(hr))
233         msg_Dbg(display->obj, "using colorspace %s", color_spaces[best].name);
234     else
235         msg_Err(display->obj, "Failed to set colorspace %s. (hr=0x%lX)", color_spaces[best].name, hr);
236 done:
237     display->colorspace = &color_spaces[best];
238     display->send_metadata = color_spaces[best].transfer == (video_transfer_func_t) cfg->transfer &&
239                              color_spaces[best].primaries == (video_color_primaries_t) cfg->primaries &&
240                              color_spaces[best].color == (video_color_space_t) cfg->colorspace;
241     if (dxgiswapChain3)
242         IDXGISwapChain3_Release(dxgiswapChain3);
243 }
244
245 #if !VLC_WINSTORE_APP
246 static void FillSwapChainDesc(struct d3d11_local_swapchain *display, UINT width, UINT height, DXGI_SWAP_CHAIN_DESC1 *out)
247 {
248     ZeroMemory(out, sizeof(*out));
249     out->BufferCount = 3;
250     out->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
251     out->SampleDesc.Count = 1;
252     out->SampleDesc.Quality = 0;
253     out->Width = width;
254     out->Height = height;
255     out->Format = display->pixelFormat->formatTexture;
256     //out->Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
257
258     bool isWin10OrGreater = false;
259     HMODULE hKernel32 = GetModuleHandle(TEXT("kernel32.dll"));
260     if (likely(hKernel32 != NULL))
261         isWin10OrGreater = GetProcAddress(hKernel32, "GetSystemCpuSetInformation") != NULL;
262     if (isWin10OrGreater)
263         out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
264     else
265     {
266         bool isWin80OrGreater = false;
267         if (likely(hKernel32 != NULL))
268             isWin80OrGreater = GetProcAddress(hKernel32, "CheckTokenCapability") != NULL;
269         if (isWin80OrGreater)
270             out->SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
271         else
272         {
273             out->SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
274             out->BufferCount = 1;
275         }
276     }
277 }
278
279 static void CreateSwapchain(struct d3d11_local_swapchain *display, UINT width, UINT height)
280 {
281     if (display->swapchainHwnd == NULL)
282     {
283         msg_Err(display->obj, "missing a HWND to create the swapchain");
284         return;
285     }
286
287     DXGI_SWAP_CHAIN_DESC1 scd;
288     FillSwapChainDesc(display, width, height, &scd);
289
290     IDXGIAdapter *dxgiadapter = D3D11DeviceAdapter(display->d3d_dev->d3ddevice);
291     if (unlikely(dxgiadapter==NULL)) {
292         msg_Err(display->obj, "Could not get the DXGI Adapter");
293         return;
294     }
295
296     IDXGIFactory2 *dxgifactory;
297     HRESULT hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
298     IDXGIAdapter_Release(dxgiadapter);
299     if (FAILED(hr)) {
300         msg_Err(display->obj, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
301         return;
302     }
303
304     hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
305                                               display->swapchainHwnd, &scd,
306                                               NULL, NULL, &display->dxgiswapChain);
307     if (hr == DXGI_ERROR_INVALID_CALL && scd.Format == DXGI_FORMAT_R10G10B10A2_UNORM)
308     {
309         msg_Warn(display->obj, "10 bits swapchain failed, try 8 bits");
310         scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
311         hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)display->d3d_dev->d3ddevice,
312                                                   display->swapchainHwnd, &scd,
313                                                   NULL, NULL, &display->dxgiswapChain);
314     }
315     IDXGIFactory2_Release(dxgifactory);
316     if (FAILED(hr)) {
317         msg_Err(display->obj, "Could not create the SwapChain. (hr=0x%lX)", hr);
318     }
319 }
320 #endif /* !VLC_WINSTORE_APP */
321
322 static bool UpdateSwapchain( struct d3d11_local_swapchain *display, const libvlc_video_render_cfg_t *cfg )
323 {
324     ID3D11Texture2D* pBackBuffer;
325     HRESULT hr;
326
327     D3D11_TEXTURE2D_DESC dsc = { 0 };
328     uint8_t bitsPerChannel = 0;
329
330     if ( display->swapchainTargetView[0] ) {
331         ID3D11Resource *res = NULL;
332         ID3D11RenderTargetView_GetResource( display->swapchainTargetView[0], &res );
333         if ( res )
334         {
335             ID3D11Texture2D_GetDesc( (ID3D11Texture2D*) res, &dsc );
336             ID3D11Resource_Release( res );
337         }
338         assert(display->pixelFormat->formatTexture == dsc.Format);
339         bitsPerChannel = display->pixelFormat->bitsPerChannel;
340     }
341
342     if ( dsc.Width == cfg->width && dsc.Height == cfg->height && cfg->bitdepth <= bitsPerChannel )
343         /* TODO also check the colorimetry */
344         return true; /* nothing changed */
345
346     for ( size_t i = 0; i < ARRAY_SIZE( display->swapchainTargetView ); i++ )
347     {
348         if ( display->swapchainTargetView[i] ) {
349             ID3D11RenderTargetView_Release( display->swapchainTargetView[i] );
350             display->swapchainTargetView[i] = NULL;
351         }
352     }
353
354     const d3d_format_t *newPixelFormat = NULL;
355 #if VLC_WINSTORE_APP
356     display->dxgiswapChain = var_InheritInteger(display->obj, "winrt-swapchain");
357     if (display->dxgiswapChain != NULL)
358     {
359         DXGI_SWAP_CHAIN_DESC1 scd;
360         if (SUCCEEDED(IDXGISwapChain1_GetDesc(display->dxgiswapChain, &scd)))
361         {
362             for (const d3d_format_t *output_format = GetRenderFormatList();
363                  output_format->name != NULL; ++output_format)
364             {
365                 if (output_format->formatTexture == scd.Format &&
366                     !is_d3d11_opaque(output_format->fourcc))
367                 {
368                     newPixelFormat = output_format;
369                     break;
370                 }
371             }
372         }
373     }
374 #else /* !VLC_WINSTORE_APP */
375     /* favor RGB formats first */
376     newPixelFormat = FindD3D11Format( display->obj, display->d3d_dev, 0, D3D11_RGB_FORMAT,
377                                       cfg->bitdepth > 8 ? 10 : 8,
378                                       0, 0,
379                                       D3D11_CHROMA_CPU, D3D11_FORMAT_SUPPORT_DISPLAY );
380     if (unlikely(newPixelFormat == NULL))
381         newPixelFormat = FindD3D11Format( display->obj, display->d3d_dev, 0, D3D11_YUV_FORMAT,
382                                           cfg->bitdepth > 8 ? 10 : 8,
383                                           0, 0,
384                                           D3D11_CHROMA_CPU, D3D11_FORMAT_SUPPORT_DISPLAY );
385 #endif /* !VLC_WINSTORE_APP */
386     if (unlikely(newPixelFormat == NULL)) {
387         msg_Err(display->obj, "Could not get the SwapChain format.");
388         return false;
389     }
390
391 #if !VLC_WINSTORE_APP
392     if (display->dxgiswapChain != NULL && display->pixelFormat != newPixelFormat)
393     {
394         // the pixel format changed, we need a new swapchain
395         IDXGISwapChain_Release(display->dxgiswapChain);
396         display->dxgiswapChain = NULL;
397         display->logged_capabilities = false;
398     }
399
400     if ( display->dxgiswapChain == NULL )
401     {
402         display->pixelFormat = newPixelFormat;
403         CreateSwapchain(display, cfg->width, cfg->height);
404
405         if (display->dxgiswapChain == NULL)
406             return false;
407     }
408     else
409 #endif /* !VLC_WINSTORE_APP */
410     {
411         /* TODO detect is the size is the same as the output and switch to fullscreen mode */
412         hr = IDXGISwapChain_ResizeBuffers( display->dxgiswapChain, 0, cfg->width, cfg->height,
413                                            DXGI_FORMAT_UNKNOWN, 0 );
414         if ( FAILED( hr ) ) {
415             msg_Err( display->obj, "Failed to resize the backbuffer. (hr=0x%lX)", hr );
416             return false;
417         }
418     }
419
420     hr = IDXGISwapChain_GetBuffer( display->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *) &pBackBuffer );
421     if ( FAILED( hr ) ) {
422         msg_Err( display->obj, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr );
423         return false;
424     }
425
426     hr = D3D11_CreateRenderTargets( display->d3d_dev, (ID3D11Resource *) pBackBuffer,
427                                     display->pixelFormat, display->swapchainTargetView );
428     ID3D11Texture2D_Release( pBackBuffer );
429     if ( FAILED( hr ) ) {
430         msg_Err( display->obj, "Failed to create the target view. (hr=0x%lX)", hr );
431         return false;
432     }
433
434     D3D11_ClearRenderTargets( display->d3d_dev, display->pixelFormat, display->swapchainTargetView );
435
436     SelectSwapchainColorspace(display, cfg);
437
438     return true;
439 }
440
441 void LocalSwapchainCleanupDevice( void *opaque )
442 {
443     struct d3d11_local_swapchain *display = opaque;
444     for (size_t i=0; i < ARRAY_SIZE(display->swapchainTargetView); i++)
445     {
446         if (display->swapchainTargetView[i]) {
447             ID3D11RenderTargetView_Release(display->swapchainTargetView[i]);
448             display->swapchainTargetView[i] = NULL;
449         }
450     }
451     if (display->dxgiswapChain4)
452     {
453         IDXGISwapChain4_Release(display->dxgiswapChain4);
454         display->dxgiswapChain4 = NULL;
455     }
456     if (display->dxgiswapChain)
457     {
458         IDXGISwapChain_Release(display->dxgiswapChain);
459         display->dxgiswapChain = NULL;
460     }
461 }
462
463 void LocalSwapchainSwap( void *opaque )
464 {
465     struct d3d11_local_swapchain *display = opaque;
466     DXGI_PRESENT_PARAMETERS presentParams = { 0 };
467
468     HRESULT hr = IDXGISwapChain1_Present1( display->dxgiswapChain, 0, 0, &presentParams );
469     if ( hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET )
470     {
471         /* TODO device lost */
472         msg_Err( display->obj, "SwapChain Present failed. (hr=0x%lX)", hr );
473     }
474 }
475
476 bool LocalSwapchainUpdateOutput( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
477 {
478     struct d3d11_local_swapchain *display = opaque;
479     if ( !UpdateSwapchain( display, cfg ) )
480         return false;
481     out->dxgi_format    = display->pixelFormat->formatTexture;
482     out->full_range     = display->colorspace->b_full_range;
483     out->colorspace     = (libvlc_video_color_space_t)     display->colorspace->color;
484     out->primaries      = (libvlc_video_color_primaries_t) display->colorspace->primaries;
485     out->transfer       = (libvlc_video_transfer_func_t)   display->colorspace->transfer;
486     return true;
487 }
488
489 bool LocalSwapchainStartEndRendering( void *opaque, bool enter )
490 {
491     struct d3d11_local_swapchain *display = opaque;
492
493     if ( enter )
494     {
495 #if VLC_WINSTORE_APP
496         /* legacy UWP mode, the width/height was set in GUID_SWAPCHAIN_WIDTH/HEIGHT */
497         uint32_t i_width;
498         uint32_t i_height;
499         UINT dataSize = sizeof(i_width);
500         HRESULT hr = IDXGISwapChain_GetPrivateData(display->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
501         if (SUCCEEDED(hr)) {
502             dataSize = sizeof(i_height);
503             hr = IDXGISwapChain_GetPrivateData(display->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
504             if (SUCCEEDED(hr)) {
505                 if (i_width != sys->area.vdcfg.display.width || i_height != sys->area.vdcfg.display.height)
506                 {
507                     vout_display_SetSize(vd, i_width, i_height);
508                 }
509             }
510         }
511 #endif
512
513         D3D11_ClearRenderTargets( display->d3d_dev, display->pixelFormat, display->swapchainTargetView );
514     }
515     return true;
516 }
517
518 void LocalSwapchainSetMetadata( void *opaque, libvlc_video_metadata_type_t type, const void *metadata )
519 {
520     struct d3d11_local_swapchain *display = opaque;
521
522     assert(type == libvlc_video_metadata_frame_hdr10);
523     if ( type == libvlc_video_metadata_frame_hdr10 && metadata &&
524          display->send_metadata && display->dxgiswapChain4 )
525     {
526         const libvlc_video_frame_hdr10_metadata_t *p_hdr10 = metadata;
527         DXGI_HDR_METADATA_HDR10 hdr10 = { 0 };
528         hdr10.GreenPrimary[0] = p_hdr10->GreenPrimary[0];
529         hdr10.GreenPrimary[1] = p_hdr10->GreenPrimary[1];
530         hdr10.BluePrimary[0] = p_hdr10->BluePrimary[0];
531         hdr10.BluePrimary[1] = p_hdr10->BluePrimary[1];
532         hdr10.RedPrimary[0] = p_hdr10->RedPrimary[0];
533         hdr10.RedPrimary[1] = p_hdr10->RedPrimary[1];
534         hdr10.WhitePoint[0] = p_hdr10->WhitePoint[0];
535         hdr10.WhitePoint[1] = p_hdr10->WhitePoint[1];
536         hdr10.MinMasteringLuminance = p_hdr10->MinMasteringLuminance;
537         hdr10.MaxMasteringLuminance = p_hdr10->MaxMasteringLuminance;
538         hdr10.MaxContentLightLevel = p_hdr10->MaxContentLightLevel;
539         hdr10.MaxFrameAverageLightLevel = p_hdr10->MaxFrameAverageLightLevel;
540         if (memcmp(&display->hdr10, &hdr10, sizeof(hdr10)))
541         {
542             memcpy(&display->hdr10, &hdr10, sizeof(hdr10));
543             IDXGISwapChain4_SetHDRMetaData( display->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10,
544                                             sizeof( &display->hdr10 ), &display->hdr10 );
545         }
546     }
547 }
548
549 bool LocalSwapchainSelectPlane( void *opaque, size_t plane )
550 {
551     struct d3d11_local_swapchain *display = opaque;
552     if (!display->swapchainTargetView[plane])
553         return false;
554     ID3D11DeviceContext_OMSetRenderTargets(display->d3d_dev->d3dcontext, 1,
555                                             &display->swapchainTargetView[plane], NULL);
556     return true;
557 }
558
559 void *CreateLocalSwapchainHandle(vlc_object_t *o, HWND hwnd, d3d11_device_t *d3d_dev)
560 {
561     struct d3d11_local_swapchain *display = vlc_obj_calloc(o, 1, sizeof(*display));
562     if (unlikely(display == NULL))
563         return NULL;
564
565     display->obj = o;
566 #if !VLC_WINSTORE_APP
567     display->swapchainHwnd = hwnd;
568 #endif /* !VLC_WINSTORE_APP */
569     display->d3d_dev = d3d_dev;
570
571     return display;
572 }