direct3d11: factorize code to check if a format supports some features
[vlc.git] / modules / video_output / win32 / direct3d11.c
1 /*****************************************************************************
2  * direct3d11.c: Windows Direct3D11 video output module
3  *****************************************************************************
4  * Copyright (C) 2014-2015 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 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_WIN7
25 # undef _WIN32_WINNT
26 # define _WIN32_WINNT _WIN32_WINNT_WIN7
27 #endif
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
36
37 #include <assert.h>
38 #include <math.h>
39
40 #define COBJMACROS
41 #include <initguid.h>
42 #include <d3d11.h>
43 #include <dxgi1_5.h>
44 #include <d3dcompiler.h>
45
46 /* avoided until we can pass ISwapchainPanel without c++/cx mode
47 # include <windows.ui.xaml.media.dxinterop.h> */
48
49 #include "../../video_chroma/d3d11_fmt.h"
50
51 #include "common.h"
52
53 #if !VLC_WINSTORE_APP
54 # define D3D11CreateDevice(args...)             sys->OurD3D11CreateDevice(args)
55 # define D3DCompile(args...)                    sys->OurD3DCompile(args)
56 #endif
57
58 DEFINE_GUID(GUID_SWAPCHAIN_WIDTH,  0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
59 DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
60
61 static int  Open(vlc_object_t *);
62 static void Close(vlc_object_t *);
63
64 #define D3D11_HELP N_("Recommended video output for Windows 8 and later versions")
65 #define HW_BLENDING_TEXT N_("Use hardware blending support")
66 #define HW_BLENDING_LONGTEXT N_(\
67     "Try to use hardware acceleration for subtitle/OSD blending.")
68
69 vlc_module_begin ()
70     set_shortname("Direct3D11")
71     set_description(N_("Direct3D11 video output"))
72     set_help(D3D11_HELP)
73     set_category(CAT_VIDEO)
74     set_subcategory(SUBCAT_VIDEO_VOUT)
75
76     add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
77
78 #if VLC_WINSTORE_APP
79     add_integer("winrt-d3dcontext",    0x0, NULL, NULL, true); /* ID3D11DeviceContext* */
80     add_integer("winrt-swapchain",     0x0, NULL, NULL, true); /* IDXGISwapChain1*     */
81 #endif
82
83     set_capability("vout display", 240)
84     add_shortcut("direct3d11")
85     set_callbacks(Open, Close)
86 vlc_module_end ()
87
88 /* A Quad is texture that can be displayed in a rectangle */
89 typedef struct
90 {
91     picture_sys_t             picSys;
92     ID3D11Buffer              *pVertexBuffer;
93     UINT                      vertexCount;
94     ID3D11VertexShader        *d3dvertexShader;
95     ID3D11Buffer              *pIndexBuffer;
96     UINT                      indexCount;
97     ID3D11Buffer              *pVertexShaderConstants;
98     ID3D11Buffer              *pPixelShaderConstants[2];
99     UINT                       PSConstantsCount;
100     ID3D11PixelShader         *d3dpixelShader;
101     D3D11_VIEWPORT            cropViewport;
102     unsigned int              i_x_offset;
103     unsigned int              i_y_offset;
104     unsigned int              i_width;
105     unsigned int              i_height;
106 } d3d_quad_t;
107
108 typedef enum video_color_axis {
109     COLOR_AXIS_RGB,
110     COLOR_AXIS_YCBCR,
111 } video_color_axis;
112
113 typedef struct {
114     DXGI_COLOR_SPACE_TYPE   dxgi;
115     const char              *name;
116     video_color_axis        axis;
117     video_color_primaries_t primaries;
118     video_transfer_func_t   transfer;
119     video_color_space_t     color;
120     bool                    b_full_range;
121 } dxgi_color_space;
122
123 struct vout_display_sys_t
124 {
125     vout_display_sys_win32_t sys;
126
127     struct { /* TODO may go in vout_display_info_t without DXGI */
128         const dxgi_color_space   *colorspace;
129         unsigned                 luminance_peak;
130     } display;
131
132 #if !VLC_WINSTORE_APP
133     HINSTANCE                hdxgi_dll;        /* handle of the opened dxgi dll */
134     HINSTANCE                hd3d11_dll;       /* handle of the opened d3d11 dll */
135     HINSTANCE                hd3dcompiler_dll; /* handle of the opened d3dcompiler dll */
136     /* We should find a better way to store this or atleast a shorter name */
137     PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN OurD3D11CreateDeviceAndSwapChain;
138     PFN_D3D11_CREATE_DEVICE                OurD3D11CreateDevice;
139     pD3DCompile                            OurD3DCompile;
140 #endif
141 #if defined(HAVE_ID3D11VIDEODECODER)
142     HANDLE                   context_lock;     /* D3D11 Context lock necessary
143                                                   for hw decoding */
144 #endif
145     IDXGISwapChain1          *dxgiswapChain;   /* DXGI 1.1 swap chain */
146     IDXGISwapChain4          *dxgiswapChain4;  /* DXGI 1.5 for HDR */
147     ID3D11Device             *d3ddevice;       /* D3D device */
148     ID3D11DeviceContext      *d3dcontext;      /* D3D context */
149     d3d_quad_t               picQuad;
150     const d3d_format_t       *picQuadConfig;
151     ID3D11PixelShader        *picQuadPixelShader;
152
153     picture_sys_t            stagingSys;
154
155     ID3D11RenderTargetView   *d3drenderTargetView;
156     ID3D11DepthStencilView   *d3ddepthStencilView;
157
158     ID3D11VertexShader        *flatVSShader;
159     ID3D11VertexShader        *projectionVSShader;
160
161     /* copy from the decoder pool into picSquad before display
162      * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
163     bool                     legacy_shader;
164
165     // SPU
166     vlc_fourcc_t             pSubpictureChromas[2];
167     ID3D11PixelShader        *pSPUPixelShader;
168     const d3d_format_t       *d3dregion_format;
169     int                      d3dregion_count;
170     picture_t                **d3dregions;
171 };
172
173 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
174 typedef struct d3d_vertex_t {
175     struct {
176         FLOAT x;
177         FLOAT y;
178         FLOAT z;
179     } position;
180     struct {
181         FLOAT x;
182         FLOAT y;
183     } texture;
184 } d3d_vertex_t;
185
186 typedef struct {
187     FLOAT Opacity;
188     FLOAT opacityPadding[3];
189 } PS_CONSTANT_BUFFER;
190
191 typedef struct {
192     FLOAT WhitePoint[4*4];
193     FLOAT Colorspace[4*4];
194 } PS_COLOR_TRANSFORM;
195
196 typedef struct {
197     FLOAT RotX[4*4];
198     FLOAT RotY[4*4];
199     FLOAT RotZ[4*4];
200     FLOAT View[4*4];
201     FLOAT Projection[4*4];
202 } VS_PROJECTION_CONST;
203
204 #define SPHERE_RADIUS 1.f
205
206 #define RECTWidth(r)   (int)((r).right - (r).left)
207 #define RECTHeight(r)  (int)((r).bottom - (r).top)
208
209 static picture_pool_t *Pool(vout_display_t *vd, unsigned count);
210
211 static void Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
212 static void Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
213
214 static HINSTANCE Direct3D11LoadShaderLibrary(void);
215 static void Direct3D11Destroy(vout_display_t *);
216
217 static int  Direct3D11Open (vout_display_t *, video_format_t *);
218 static void Direct3D11Close(vout_display_t *);
219
220 static int  Direct3D11CreateResources (vout_display_t *, video_format_t *);
221 static void Direct3D11DestroyResources(vout_display_t *);
222
223 static void Direct3D11DestroyPool(vout_display_t *);
224
225 static void DestroyDisplayPoolPicture(picture_t *);
226 static void Direct3D11DeleteRegions(int, picture_t **);
227 static int Direct3D11MapSubpicture(vout_display_t *, int *, picture_t ***, subpicture_t *);
228
229 static int SetupQuad(vout_display_t *, const video_format_t *, d3d_quad_t *,
230                      const d3d_format_t *, ID3D11PixelShader *, video_projection_mode_t);
231 static void ReleaseQuad(d3d_quad_t *);
232 static void UpdatePicQuadPosition(vout_display_t *);
233
234 static int Control(vout_display_t *vd, int query, va_list args);
235 static void Manage(vout_display_t *vd);
236
237 /* TODO: Move to a direct3d11_shaders header */
238 static const char* globVertexShaderFlat = "\
239   struct VS_INPUT\
240   {\
241     float4 Position   : POSITION;\
242     float4 Texture    : TEXCOORD0;\
243   };\
244   \
245   struct VS_OUTPUT\
246   {\
247     float4 Position   : SV_POSITION;\
248     float4 Texture    : TEXCOORD0;\
249   };\
250   \
251   VS_OUTPUT main( VS_INPUT In )\
252   {\
253     return In;\
254   }\
255 ";
256
257 #define STRINGIZE2(s) #s
258 #define STRINGIZE(s) STRINGIZE2(s)
259
260 static const char* globVertexShaderProjection = "\
261   cbuffer VS_PROJECTION_CONST : register(b0)\
262   {\
263      float4x4 RotX;\
264      float4x4 RotY;\
265      float4x4 RotZ;\
266      float4x4 View;\
267      float4x4 Projection;\
268   };\
269   struct VS_INPUT\
270   {\
271     float4 Position   : POSITION;\
272     float4 Texture    : TEXCOORD0;\
273   };\
274   \
275   struct VS_OUTPUT\
276   {\
277     float4 Position   : SV_POSITION;\
278     float4 Texture    : TEXCOORD0;\
279   };\
280   \
281   VS_OUTPUT main( VS_INPUT In )\
282   {\
283     VS_OUTPUT Output;\
284     float4 pos = In.Position;\
285     pos = mul(RotY, pos);\
286     pos = mul(RotX, pos);\
287     pos = mul(RotZ, pos);\
288     pos = mul(View, pos);\
289     pos = mul(Projection, pos);\
290     Output.Position = pos;\
291     Output.Texture = In.Texture;\
292     return Output;\
293   }\
294 ";
295
296 static const char* globPixelShaderDefault = "\
297   cbuffer PS_CONSTANT_BUFFER : register(b0)\
298   {\
299     float Opacity;\
300     float opacityPadding[3];\
301   };\
302   cbuffer PS_COLOR_TRANSFORM : register(b1)\
303   {\
304     float4x4 WhitePoint;\
305     float4x4 Colorspace;\
306   };\
307   Texture2D%s shaderTexture[" STRINGIZE(D3D11_MAX_SHADER_VIEW) "];\
308   SamplerState SampleType;\
309   \
310   struct PS_INPUT\
311   {\
312     float4 Position   : SV_POSITION;\
313     float4 Texture    : TEXCOORD0;\
314   };\
315   \
316   /* see http://filmicworlds.com/blog/filmic-tonemapping-operators/ */\
317   inline float hable(float x) {\
318       const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;\
319       return ((x * (A*x + (C*B))+(D*E))/(x * (A*x + B) + (D*F))) - E/F;\
320   }\
321   \
322   inline float3 hable(float3 x) {\
323       x.r = hable(x.r);\
324       x.g = hable(x.g);\
325       x.b = hable(x.b);\
326       return x;\
327   }\
328   \
329   /* https://en.wikipedia.org/wiki/Hybrid_Log-Gamma#Technical_details */\
330   inline float inverse_HLG(float x){\
331       const float B67_a = 0.17883277;\
332       const float B67_b = 0.28466892;\
333       const float B67_c = 0.55991073;\
334       const float B67_inv_r2 = 4.0; /* 1/0.5² */\
335       if (x <= 0.5)\
336           x = x * x * B67_inv_r2;\
337       else\
338           x = exp((x - B67_c) / B67_a) + B67_b;\
339       return x;\
340   }\
341   \
342   inline float3 sourceToLinear(float3 rgb) {\
343       %s;\
344   }\
345   \
346   inline float3 linearToDisplay(float3 rgb) {\
347       %s;\
348   }\
349   \
350   inline float3 toneMapping(float3 rgb) {\
351       %s;\
352   }\
353   \
354   inline float3 adjustPeakLuminance(float3 rgb) {\
355       %s;\
356   }\
357   \
358   inline float3 adjustRange(float3 rgb) {\
359       %s;\
360   }\
361   \
362   float4 main( PS_INPUT In ) : SV_TARGET\
363   {\
364     float4 sample;\
365     \
366     %s /* sampling routine in sample */\
367     float4 rgba = mul(mul(sample, WhitePoint), Colorspace);\
368     float opacity = rgba.a * Opacity;\
369     float3 rgb = (float3)rgba;\
370     rgb = sourceToLinear(rgb);\
371     rgb = toneMapping(rgb);\
372     rgb = adjustPeakLuminance(rgb);\
373     rgb = linearToDisplay(rgb);\
374     rgb = adjustRange(rgb);\
375     return float4(rgb, saturate(opacity));\
376   }\
377 ";
378
379 #define ST2084_PQ_CONSTANTS  "const float ST2084_m1 = 2610.0 / (4096.0 * 4);\
380 const float ST2084_m2 = (2523.0 / 4096.0) * 128.0;\
381 const float ST2084_c1 = 3424.0 / 4096.0;\
382 const float ST2084_c2 = (2413.0 / 4096.0) * 32.0;\
383 const float ST2084_c3 = (2392.0 / 4096.0) * 32.0;"
384
385 #define DEFAULT_BRIGHTNESS 80
386
387
388 static int Direct3D11MapPoolTexture(picture_t *picture)
389 {
390     picture_sys_t *p_sys = picture->p_sys;
391     D3D11_MAPPED_SUBRESOURCE mappedResource;
392     HRESULT hr;
393     hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
394     if( FAILED(hr) )
395     {
396         return VLC_EGENERIC;
397     }
398     return CommonUpdatePicture(picture, NULL, mappedResource.pData, mappedResource.RowPitch);
399 }
400
401 static void Direct3D11UnmapPoolTexture(picture_t *picture)
402 {
403     picture_sys_t *p_sys = picture->p_sys;
404     ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0);
405 }
406
407 #if !VLC_WINSTORE_APP
408 static int OpenHwnd(vout_display_t *vd)
409 {
410     vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
411     if (!sys)
412         return VLC_ENOMEM;
413
414     sys->hd3d11_dll = LoadLibrary(TEXT("D3D11.DLL"));
415     if (!sys->hd3d11_dll) {
416         msg_Warn(vd, "cannot load d3d11.dll, aborting");
417         return VLC_EGENERIC;
418     }
419
420     sys->hd3dcompiler_dll = Direct3D11LoadShaderLibrary();
421     if (!sys->hd3dcompiler_dll) {
422         msg_Err(vd, "cannot load d3dcompiler.dll, aborting");
423         Direct3D11Destroy(vd);
424         return VLC_EGENERIC;
425     }
426
427     sys->OurD3DCompile = (void *)GetProcAddress(sys->hd3dcompiler_dll, "D3DCompile");
428     if (!sys->OurD3DCompile) {
429         msg_Err(vd, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
430         Direct3D11Destroy(vd);
431         return VLC_EGENERIC;
432     }
433
434     sys->OurD3D11CreateDevice =
435         (void *)GetProcAddress(sys->hd3d11_dll, "D3D11CreateDevice");
436     if (!sys->OurD3D11CreateDevice) {
437         msg_Err(vd, "Cannot locate reference to D3D11CreateDevice in d3d11 DLL");
438         Direct3D11Destroy(vd);
439         return VLC_EGENERIC;
440     }
441     return VLC_SUCCESS;
442 }
443 #else
444 static int OpenCoreW(vout_display_t *vd)
445 {
446     IDXGISwapChain1* dxgiswapChain  = var_InheritInteger(vd, "winrt-swapchain");
447     if (!dxgiswapChain)
448         return VLC_EGENERIC;
449     ID3D11DeviceContext* d3dcontext = var_InheritInteger(vd, "winrt-d3dcontext");
450     if (!d3dcontext)
451         return VLC_EGENERIC;
452     ID3D11Device* d3ddevice = NULL;
453     ID3D11DeviceContext_GetDevice(d3dcontext, &d3ddevice);
454     if (!d3ddevice)
455         return VLC_EGENERIC;
456
457     vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
458     if (!sys)
459         return VLC_ENOMEM;
460
461     sys->dxgiswapChain = dxgiswapChain;
462     sys->d3ddevice     = d3ddevice;
463     sys->d3dcontext    = d3dcontext;
464     IDXGISwapChain_AddRef     (sys->dxgiswapChain);
465     ID3D11Device_AddRef       (sys->d3ddevice);
466     ID3D11DeviceContext_AddRef(sys->d3dcontext);
467
468     return VLC_SUCCESS;
469 }
470 #endif
471
472 static bool is_d3d11_opaque(vlc_fourcc_t chroma)
473 {
474     switch (chroma)
475     {
476     case VLC_CODEC_D3D11_OPAQUE:
477     case VLC_CODEC_D3D11_OPAQUE_10B:
478         return true;
479     default:
480         return false;
481     }
482 }
483
484 #if VLC_WINSTORE_APP
485 static bool GetRect(const vout_display_sys_win32_t *p_sys, RECT *out)
486 {
487     const vout_display_sys_t *sys = (const vout_display_sys_t *)p_sys;
488     out->left   = 0;
489     out->top    = 0;
490     uint32_t i_width;
491     uint32_t i_height;
492     UINT dataSize = sizeof(i_width);
493     HRESULT hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
494     if (FAILED(hr)) {
495         return false;
496     }
497     dataSize = sizeof(i_height);
498     hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
499     if (FAILED(hr)) {
500         return false;
501     }
502     out->right  = i_width;
503     out->bottom = i_height;
504     return true;
505 }
506 #endif
507
508 static int Open(vlc_object_t *object)
509 {
510     vout_display_t *vd = (vout_display_t *)object;
511
512 #if !VLC_WINSTORE_APP
513     int ret = OpenHwnd(vd);
514 #else
515     int ret = OpenCoreW(vd);
516 #endif
517
518     if (ret != VLC_SUCCESS)
519         return ret;
520
521     if (CommonInit(vd))
522         goto error;
523
524 #if VLC_WINSTORE_APP
525     vd->sys->sys.pf_GetRect = GetRect;
526 #endif
527
528     video_format_t fmt;
529     if (Direct3D11Open(vd, &fmt)) {
530         msg_Err(vd, "Direct3D11 could not be opened");
531         goto error;
532     }
533
534     video_format_Clean(&vd->fmt);
535     video_format_Copy(&vd->fmt, &fmt);
536
537     vd->info.has_double_click     = true;
538     vd->info.has_hide_mouse       = false;
539     vd->info.has_pictures_invalid = vd->info.is_slow;
540
541     if (var_InheritBool(vd, "direct3d11-hw-blending") &&
542         vd->sys->d3dregion_format != NULL)
543     {
544         vd->sys->pSubpictureChromas[0] = vd->sys->d3dregion_format->fourcc;
545         vd->sys->pSubpictureChromas[1] = 0;
546         vd->info.subpicture_chromas = vd->sys->pSubpictureChromas;
547     }
548     else
549         vd->info.subpicture_chromas = NULL;
550
551     vd->pool    = Pool;
552     vd->prepare = Prepare;
553     vd->display = Display;
554     vd->control = Control;
555     vd->manage  = Manage;
556
557     msg_Dbg(vd, "Direct3D11 Open Succeeded");
558
559     return VLC_SUCCESS;
560
561 error:
562     Close(object);
563     return VLC_EGENERIC;
564 }
565
566 static void Close(vlc_object_t *object)
567 {
568     vout_display_t * vd = (vout_display_t *)object;
569
570     Direct3D11Close(vd);
571     CommonClean(vd);
572     Direct3D11Destroy(vd);
573     free(vd->sys);
574 }
575
576 static const d3d_format_t *GetOutputFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma,
577                                            uint8_t bits_per_channel, bool b_allow_opaque,
578                                            bool blendable)
579 {
580     vout_display_sys_t *sys = vd->sys;
581     UINT supportFlags = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_LOAD;
582     if (blendable)
583         supportFlags |= D3D11_FORMAT_SUPPORT_BLENDABLE;
584     if (b_allow_opaque && is_d3d11_opaque(i_src_chroma))
585         supportFlags |= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT;
586
587     for (const d3d_format_t *output_format = GetRenderFormatList();
588          output_format->name != NULL; ++output_format)
589     {
590         if (i_src_chroma && i_src_chroma != output_format->fourcc)
591             continue;
592         if (bits_per_channel && bits_per_channel > output_format->bitsPerChannel)
593             continue;
594         if (!b_allow_opaque && is_d3d11_opaque(output_format->fourcc))
595             continue;
596
597         if( DeviceSupportsFormat( sys->d3ddevice, output_format->formatTexture,
598                                   supportFlags ) )
599             return output_format;
600     }
601     return NULL;
602 }
603
604 /* map texture planes to resource views */
605 static int AllocateShaderView(vout_display_t *vd, const d3d_format_t *format,
606                               int slice_index, picture_sys_t *picsys)
607 {
608     HRESULT hr;
609     vout_display_sys_t *sys = vd->sys;
610     int i;
611     D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc = { 0 };
612     D3D11_TEXTURE2D_DESC texDesc;
613     ID3D11Texture2D_GetDesc(picsys->texture[0], &texDesc);
614
615     if (texDesc.ArraySize == 1)
616     {
617         resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
618         resviewDesc.Texture2D.MipLevels = 1;
619     }
620     else
621     {
622         resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
623         resviewDesc.Texture2DArray.MipLevels = -1;
624         resviewDesc.Texture2DArray.ArraySize = 1;
625         resviewDesc.Texture2DArray.FirstArraySlice = slice_index;
626     }
627     for (i=0; i<D3D11_MAX_SHADER_VIEW; i++)
628     {
629         if (!picsys->texture[i])
630             picsys->resourceView[i] = NULL;
631         else
632         {
633             resviewDesc.Format = format->resourceFormat[i];
634             hr = ID3D11Device_CreateShaderResourceView(sys->d3ddevice, picsys->resource[i], &resviewDesc, &picsys->resourceView[i]);
635             if (FAILED(hr)) {
636                 msg_Err(vd, "Could not Create the Texture ResourceView %d slice %d. (hr=0x%lX)", i, slice_index, hr);
637                 break;
638             }
639         }
640     }
641
642     if (i != D3D11_MAX_SHADER_VIEW)
643     {
644         while (--i >= 0)
645         {
646             ID3D11ShaderResourceView_Release(picsys->resourceView[i]);
647             picsys->resourceView[i] = NULL;
648         }
649         return VLC_EGENERIC;
650     }
651
652     return VLC_SUCCESS;
653 }
654
655 static int AllocateTextures(vout_display_t *vd, const d3d_format_t *cfg,
656                             video_format_t *fmt, unsigned pool_size,
657                             ID3D11Texture2D *textures[])
658 {
659     vout_display_sys_t *sys = vd->sys;
660     int plane;
661     HRESULT hr;
662     ID3D11Texture2D *slicedTexture = NULL;
663     D3D11_TEXTURE2D_DESC texDesc;
664     ZeroMemory(&texDesc, sizeof(texDesc));
665     texDesc.MipLevels = 1;
666     texDesc.SampleDesc.Count = 1;
667     texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
668     texDesc.Usage = D3D11_USAGE_DEFAULT;
669     texDesc.CPUAccessFlags = 0;
670     texDesc.Format = cfg->formatTexture;
671     texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
672     if (is_d3d11_opaque(fmt->i_chroma)) {
673         texDesc.BindFlags |= D3D11_BIND_DECODER;
674         texDesc.Usage = D3D11_USAGE_DEFAULT;
675         texDesc.CPUAccessFlags = 0;
676     } else {
677         texDesc.Usage = D3D11_USAGE_DYNAMIC;
678         texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
679     }
680     texDesc.ArraySize = pool_size;
681     texDesc.Height = fmt->i_height;
682     texDesc.Width = fmt->i_width;
683
684     hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &slicedTexture );
685     if (FAILED(hr)) {
686         msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
687         goto error;
688     }
689
690     for (unsigned picture_count = 0; picture_count < pool_size; picture_count++) {
691         textures[picture_count * D3D11_MAX_SHADER_VIEW] = slicedTexture;
692         ID3D11Texture2D_AddRef(slicedTexture);
693
694         for (plane = 1; plane < D3D11_MAX_SHADER_VIEW; plane++) {
695             if (!cfg->resourceFormat[plane])
696                 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = NULL;
697             else
698             {
699                 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = textures[picture_count * D3D11_MAX_SHADER_VIEW];
700                 ID3D11Texture2D_AddRef(textures[picture_count * D3D11_MAX_SHADER_VIEW + plane]);
701             }
702         }
703     }
704
705     if (!is_d3d11_opaque(fmt->i_chroma)) {
706         D3D11_MAPPED_SUBRESOURCE mappedResource;
707         const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
708
709         hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource*)textures[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
710         if( FAILED(hr) ) {
711             msg_Err(vd, "The texture cannot be mapped. (hr=0x%lX)", hr);
712             goto error;
713         }
714         ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource*)textures[0], 0);
715         if (mappedResource.RowPitch < p_chroma_desc->pixel_size * texDesc.Width) {
716             msg_Err( vd, "The texture row pitch is too small (%d instead of %d)", mappedResource.RowPitch,
717                      p_chroma_desc->pixel_size * texDesc.Width );
718             goto error;
719         }
720     }
721
722     if (slicedTexture)
723         ID3D11Texture2D_Release(slicedTexture);
724     return VLC_SUCCESS;
725 error:
726     if (slicedTexture)
727         ID3D11Texture2D_Release(slicedTexture);
728     return VLC_EGENERIC;
729 }
730
731 static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
732 {
733     vout_display_sys_t *sys = vd->sys;
734     ID3D11Texture2D  *textures[pool_size * D3D11_MAX_SHADER_VIEW];
735     picture_t **pictures = NULL;
736     picture_t *picture;
737     unsigned  plane;
738     unsigned  picture_count = 0;
739     picture_pool_configuration_t pool_cfg = {};
740
741     if (sys->sys.pool)
742         return sys->sys.pool;
743
744     if (vd->info.is_slow)
745         pool_size = 1;
746
747     video_format_t surface_fmt = vd->fmt;
748     if (is_d3d11_opaque(surface_fmt.i_chroma))
749     {
750         /* worst case scenario we need 128 alignment for HEVC */
751         surface_fmt.i_width  = (surface_fmt.i_width  + 0x7F) & ~0x7F;
752         surface_fmt.i_height = (surface_fmt.i_height + 0x7F) & ~0x7F;
753     }
754
755     if (AllocateTextures(vd, sys->picQuadConfig, &surface_fmt, pool_size, textures))
756         goto error;
757
758     if (vd->info.is_slow) {
759         picture_resource_t resource = {
760             .p_sys = &sys->picQuad.picSys,
761             .pf_destroy = DestroyDisplayPoolPicture,
762         };
763
764         picture = picture_NewFromResource(&surface_fmt, &resource);
765         if (likely(picture != NULL)) {
766             pool_cfg.picture       = &picture;
767             pool_cfg.lock          = Direct3D11MapPoolTexture;
768             //pool_cfg.unlock        = Direct3D11UnmapPoolTexture;
769         }
770     } else {
771         HRESULT           hr;
772         ID3D10Multithread *pMultithread;
773         hr = ID3D11Device_QueryInterface( sys->d3ddevice, &IID_ID3D10Multithread, (void **)&pMultithread);
774         if (SUCCEEDED(hr)) {
775             ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
776             ID3D10Multithread_Release(pMultithread);
777         }
778     }
779
780     pictures = calloc(pool_size, sizeof(*pictures));
781     if (!pictures)
782         goto error;
783
784     for (picture_count = 0; picture_count < pool_size; picture_count++) {
785         picture_sys_t *picsys = calloc(1, sizeof(*picsys));
786         if (unlikely(picsys == NULL))
787             goto error;
788
789         for (plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
790             picsys->texture[plane] = textures[picture_count * D3D11_MAX_SHADER_VIEW + plane];
791
792         picsys->slice_index = picture_count;
793         picsys->formatTexture = sys->picQuadConfig->formatTexture;
794         picsys->context = sys->d3dcontext;
795
796         picture_resource_t resource = {
797             .p_sys = picsys,
798             .pf_destroy = DestroyDisplayPoolPicture,
799         };
800
801         picture = picture_NewFromResource(&surface_fmt, &resource);
802         if (unlikely(picture == NULL)) {
803             free(picsys);
804             msg_Err( vd, "Failed to create picture %d in the pool.", picture_count );
805             goto error;
806         }
807
808         pictures[picture_count] = picture;
809         /* each picture_t holds a ref to the context and release it on Destroy */
810         ID3D11DeviceContext_AddRef(picsys->context);
811     }
812
813 #ifdef HAVE_ID3D11VIDEODECODER
814     if (!is_d3d11_opaque(surface_fmt.i_chroma) || sys->legacy_shader)
815     {
816         /* we need a staging texture */
817         video_format_t staging_fmt;
818         video_format_Copy(&staging_fmt, &surface_fmt);
819         if (AllocateTextures(vd, sys->picQuadConfig, &staging_fmt, 1, textures))
820             goto error;
821
822         sys->picQuad.i_x_offset = 0;
823         sys->picQuad.i_y_offset = 0;
824         sys->picQuad.i_width    = staging_fmt.i_width;
825         sys->picQuad.i_height   = staging_fmt.i_height;
826
827         for (unsigned plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
828             sys->stagingSys.texture[plane] = textures[plane];
829
830         if (AllocateShaderView(vd, sys->picQuadConfig, 0, &sys->stagingSys))
831             goto error;
832     } else
833 #endif
834     {
835         sys->picQuad.i_x_offset = surface_fmt.i_x_offset;
836         sys->picQuad.i_y_offset = surface_fmt.i_y_offset;
837         sys->picQuad.i_width    = surface_fmt.i_width;
838         sys->picQuad.i_height   = surface_fmt.i_height;
839
840         for (picture_count = 0; picture_count < pool_size; picture_count++) {
841             if (AllocateShaderView(vd, sys->picQuadConfig, picture_count, pictures[picture_count]->p_sys))
842                 goto error;
843         }
844     }
845
846     if (SetupQuad( vd, &surface_fmt, &sys->picQuad, sys->picQuadConfig, sys->picQuadPixelShader,
847                    surface_fmt.projection_mode) != VLC_SUCCESS) {
848         msg_Err(vd, "Could not Create the main quad picture.");
849         return NULL;
850     }
851
852     if (vd->info.is_slow) {
853         pool_cfg.lock          = Direct3D11MapPoolTexture;
854         //pool_cfg.unlock        = Direct3D11UnmapPoolTexture;
855     }
856     pool_cfg.picture       = pictures;
857     pool_cfg.picture_count = pool_size;
858     sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
859
860 error:
861     if (sys->sys.pool == NULL) {
862         if (pictures) {
863             msg_Dbg(vd, "Failed to create the picture d3d11 pool");
864             for (unsigned i=0;i<picture_count; ++i)
865                 picture_Release(pictures[i]);
866             free(pictures);
867         }
868
869         /* create an empty pool to avoid crashing */
870         pool_cfg.picture_count = 0;
871         sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
872     } else {
873         msg_Dbg(vd, "ID3D11VideoDecoderOutputView succeed with %d surfaces (%dx%d) context 0x%p",
874                 picture_count, surface_fmt.i_width, surface_fmt.i_height, sys->d3dcontext);
875     }
876     return sys->sys.pool;
877 }
878
879 static void ReleasePictureResources(picture_sys_t *p_sys)
880 {
881     for (int i=0; i<D3D11_MAX_SHADER_VIEW; i++) {
882         if (p_sys->resourceView[i]) {
883             ID3D11ShaderResourceView_Release(p_sys->resourceView[i]);
884             p_sys->resourceView[i] = NULL;
885         }
886         if (p_sys->texture[i]) {
887             ID3D11Texture2D_Release(p_sys->texture[i]);
888             p_sys->texture[i] = NULL;
889         }
890     }
891     if (p_sys->context) {
892         ID3D11DeviceContext_Release(p_sys->context);
893         p_sys->context = NULL;
894     }
895     if (p_sys->decoder) {
896         ID3D11VideoDecoderOutputView_Release(p_sys->decoder);
897         p_sys->decoder = NULL;
898     }
899     if (p_sys->processorInput) {
900         ID3D11VideoProcessorInputView_Release(p_sys->processorInput);
901         p_sys->processorInput = NULL;
902     }
903 }
904
905 static void DestroyDisplayPoolPicture(picture_t *picture)
906 {
907     picture_sys_t *p_sys = picture->p_sys;
908     ReleasePictureResources( p_sys );
909     free(p_sys);
910     free(picture);
911 }
912
913 static HRESULT UpdateBackBuffer(vout_display_t *vd)
914 {
915     vout_display_sys_t *sys = vd->sys;
916     HRESULT hr;
917     ID3D11Texture2D* pDepthStencil;
918     ID3D11Texture2D* pBackBuffer;
919     RECT rect;
920 #if VLC_WINSTORE_APP
921     if (!GetRect(&sys->sys, &rect))
922 #endif
923         rect = sys->sys.rect_dest_clipped;
924     uint32_t i_width = RECTWidth(rect);
925     uint32_t i_height = RECTHeight(rect);
926
927     if (sys->d3drenderTargetView) {
928         ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
929         sys->d3drenderTargetView = NULL;
930     }
931     if (sys->d3ddepthStencilView) {
932         ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
933         sys->d3ddepthStencilView = NULL;
934     }
935
936     /* TODO detect is the size is the same as the output and switch to fullscreen mode */
937     hr = IDXGISwapChain_ResizeBuffers(sys->dxgiswapChain, 0, i_width, i_height,
938         DXGI_FORMAT_UNKNOWN, 0);
939     if (FAILED(hr)) {
940        msg_Err(vd, "Failed to resize the backbuffer. (hr=0x%lX)", hr);
941        return hr;
942     }
943
944     hr = IDXGISwapChain_GetBuffer(sys->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *)&pBackBuffer);
945     if (FAILED(hr)) {
946        msg_Err(vd, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr);
947        return hr;
948     }
949
950     hr = ID3D11Device_CreateRenderTargetView(sys->d3ddevice, (ID3D11Resource *)pBackBuffer, NULL, &sys->d3drenderTargetView);
951     ID3D11Texture2D_Release(pBackBuffer);
952     if (FAILED(hr)) {
953         msg_Err(vd, "Failed to create the target view. (hr=0x%lX)", hr);
954         return hr;
955     }
956
957     D3D11_TEXTURE2D_DESC deptTexDesc;
958     memset(&deptTexDesc, 0,sizeof(deptTexDesc));
959     deptTexDesc.ArraySize = 1;
960     deptTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
961     deptTexDesc.CPUAccessFlags = 0;
962     deptTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
963     deptTexDesc.Width = i_width;
964     deptTexDesc.Height = i_height;
965     deptTexDesc.MipLevels = 1;
966     deptTexDesc.MiscFlags = 0;
967     deptTexDesc.SampleDesc.Count = 1;
968     deptTexDesc.SampleDesc.Quality = 0;
969     deptTexDesc.Usage = D3D11_USAGE_DEFAULT;
970
971     hr = ID3D11Device_CreateTexture2D(sys->d3ddevice, &deptTexDesc, NULL, &pDepthStencil);
972     if (FAILED(hr)) {
973        msg_Err(vd, "Could not create the depth stencil texture. (hr=0x%lX)", hr);
974        return hr;
975     }
976
977     D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc;
978     memset(&depthViewDesc, 0, sizeof(depthViewDesc));
979
980     depthViewDesc.Format = deptTexDesc.Format;
981     depthViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
982     depthViewDesc.Texture2D.MipSlice = 0;
983
984     hr = ID3D11Device_CreateDepthStencilView(sys->d3ddevice, (ID3D11Resource *)pDepthStencil, &depthViewDesc, &sys->d3ddepthStencilView);
985     ID3D11Texture2D_Release(pDepthStencil);
986
987     if (FAILED(hr)) {
988        msg_Err(vd, "Could not create the depth stencil view. (hr=0x%lX)", hr);
989        return hr;
990     }
991
992     return S_OK;
993 }
994
995 /* rotation around the Z axis */
996 static void getZRotMatrix(float theta, FLOAT matrix[static 16])
997 {
998     float st, ct;
999
1000     sincosf(theta, &st, &ct);
1001
1002     const FLOAT m[] = {
1003     /*  x    y    z    w */
1004         ct,  -st, 0.f, 0.f,
1005         st,  ct,  0.f, 0.f,
1006         0.f, 0.f, 1.f, 0.f,
1007         0.f, 0.f, 0.f, 1.f
1008     };
1009
1010     memcpy(matrix, m, sizeof(m));
1011 }
1012
1013 /* rotation around the Y axis */
1014 static void getYRotMatrix(float theta, FLOAT matrix[static 16])
1015 {
1016     float st, ct;
1017
1018     sincosf(theta, &st, &ct);
1019
1020     const FLOAT m[] = {
1021     /*  x    y    z    w */
1022         ct,  0.f, -st, 0.f,
1023         0.f, 1.f, 0.f, 0.f,
1024         st,  0.f, ct,  0.f,
1025         0.f, 0.f, 0.f, 1.f
1026     };
1027
1028     memcpy(matrix, m, sizeof(m));
1029 }
1030
1031 /* rotation around the X axis */
1032 static void getXRotMatrix(float phi, FLOAT matrix[static 16])
1033 {
1034     float sp, cp;
1035
1036     sincosf(phi, &sp, &cp);
1037
1038     const FLOAT m[] = {
1039     /*  x    y    z    w */
1040         1.f, 0.f, 0.f, 0.f,
1041         0.f, cp,  sp,  0.f,
1042         0.f, -sp, cp,  0.f,
1043         0.f, 0.f, 0.f, 1.f
1044     };
1045
1046     memcpy(matrix, m, sizeof(m));
1047 }
1048
1049 static void getZoomMatrix(float zoom, FLOAT matrix[static 16]) {
1050
1051     const FLOAT m[] = {
1052         /* x   y     z     w */
1053         1.0f, 0.0f, 0.0f, 0.0f,
1054         0.0f, 1.0f, 0.0f, 0.0f,
1055         0.0f, 0.0f, 1.0f, 0.0f,
1056         0.0f, 0.0f, zoom, 1.0f
1057     };
1058
1059     memcpy(matrix, m, sizeof(m));
1060 }
1061
1062 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
1063 static void getProjectionMatrix(float sar, float fovy, FLOAT matrix[static 16]) {
1064
1065     float zFar  = 1000;
1066     float zNear = 0.01;
1067
1068     float f = 1.f / tanf(fovy / 2.f);
1069
1070     const FLOAT m[] = {
1071         f / sar, 0.f,                   0.f,                0.f,
1072         0.f,     f,                     0.f,                0.f,
1073         0.f,     0.f,     (zNear + zFar) / (zNear - zFar), -1.f,
1074         0.f,     0.f, (2 * zNear * zFar) / (zNear - zFar),  0.f};
1075
1076      memcpy(matrix, m, sizeof(m));
1077 }
1078
1079 static float UpdateFOVy(float f_fovx, float f_sar)
1080 {
1081     return 2 * atanf(tanf(f_fovx / 2) / f_sar);
1082 }
1083
1084 static float UpdateZ(float f_fovx, float f_fovy)
1085 {
1086     /* Do trigonometry to calculate the minimal z value
1087      * that will allow us to zoom out without seeing the outside of the
1088      * sphere (black borders). */
1089     float tan_fovx_2 = tanf(f_fovx / 2);
1090     float tan_fovy_2 = tanf(f_fovy / 2);
1091     float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
1092                     tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
1093
1094     /* The FOV value above which z is dynamically calculated. */
1095     const float z_thresh = 90.f;
1096
1097     float f_z;
1098     if (f_fovx <= z_thresh * M_PI / 180)
1099         f_z = 0;
1100     else
1101     {
1102         float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
1103         f_z = f * f_fovx - f * z_thresh * M_PI / 180;
1104         if (f_z < z_min)
1105             f_z = z_min;
1106     }
1107     return f_z;
1108 }
1109
1110 static void SetQuadVSProjection(vout_display_t *vd, d3d_quad_t *quad, const vlc_viewpoint_t *p_vp)
1111 {
1112     if (!quad->pVertexShaderConstants)
1113         return;
1114
1115 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1116     float f_fovx = RAD(p_vp->fov);
1117     if ( f_fovx > FIELD_OF_VIEW_DEGREES_MAX * M_PI / 180 + 0.001f ||
1118          f_fovx < -0.001f )
1119         return;
1120
1121     float f_sar = (float) vd->cfg->display.width / vd->cfg->display.height;
1122     float f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
1123     float f_phi  = RAD(p_vp->pitch);
1124     float f_roll = RAD(p_vp->roll);
1125     float f_fovy = UpdateFOVy(f_fovx, f_sar);
1126     float f_z = UpdateZ(f_fovx, f_fovy);
1127
1128     vout_display_sys_t *sys = vd->sys;
1129     HRESULT hr;
1130     D3D11_MAPPED_SUBRESOURCE mapped;
1131     hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1132     if (SUCCEEDED(hr)) {
1133         VS_PROJECTION_CONST *dst_data = mapped.pData;
1134         getXRotMatrix(f_phi, dst_data->RotX);
1135         getYRotMatrix(f_teta,   dst_data->RotY);
1136         getZRotMatrix(f_roll,  dst_data->RotZ);
1137         getZoomMatrix(SPHERE_RADIUS * f_z, dst_data->View);
1138         getProjectionMatrix(f_sar, f_fovy, dst_data->Projection);
1139     }
1140     ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0);
1141 #undef RAD
1142 }
1143
1144 static int Control(vout_display_t *vd, int query, va_list args)
1145 {
1146     int res = CommonControl( vd, query, args );
1147
1148     if (query == VOUT_DISPLAY_CHANGE_VIEWPOINT)
1149     {
1150         const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t*);
1151         if ( vd->sys->picQuad.pVertexShaderConstants )
1152         {
1153             SetQuadVSProjection( vd, &vd->sys->picQuad, &cfg->viewpoint );
1154             res = VLC_SUCCESS;
1155         }
1156     }
1157
1158     return res;
1159 }
1160
1161 static void Manage(vout_display_t *vd)
1162 {
1163     vout_display_sys_t *sys = vd->sys;
1164     RECT size_before = sys->sys.rect_dest_clipped;
1165
1166     CommonManage(vd);
1167
1168     if (RECTWidth(size_before)  != RECTWidth(sys->sys.rect_dest_clipped) ||
1169         RECTHeight(size_before) != RECTHeight(sys->sys.rect_dest_clipped))
1170     {
1171 #if defined(HAVE_ID3D11VIDEODECODER)
1172         if( sys->context_lock != INVALID_HANDLE_VALUE )
1173         {
1174             WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1175         }
1176 #endif
1177         msg_Dbg(vd, "Manage detected size change %dx%d", RECTWidth(sys->sys.rect_dest_clipped),
1178                 RECTHeight(sys->sys.rect_dest_clipped));
1179
1180         UpdateBackBuffer(vd);
1181
1182         UpdatePicQuadPosition(vd);
1183 #if defined(HAVE_ID3D11VIDEODECODER)
1184         if( sys->context_lock != INVALID_HANDLE_VALUE )
1185         {
1186             ReleaseMutex( sys->context_lock );
1187         }
1188 #endif
1189     }
1190 }
1191
1192 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
1193 {
1194     vout_display_sys_t *sys = vd->sys;
1195
1196 #if defined(HAVE_ID3D11VIDEODECODER)
1197     if( sys->context_lock != INVALID_HANDLE_VALUE )
1198         WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1199 #endif
1200     if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader) {
1201         picture_sys_t *p_sys = picture->p_sys;
1202         D3D11_TEXTURE2D_DESC texDesc;
1203         if (!is_d3d11_opaque(picture->format.i_chroma))
1204             Direct3D11UnmapPoolTexture(picture);
1205         ID3D11Texture2D_GetDesc(sys->stagingSys.texture[0], &texDesc);
1206         D3D11_BOX box = {
1207             .top = picture->format.i_y_offset,
1208             .bottom = picture->format.i_y_offset + texDesc.Height,
1209             .left = picture->format.i_x_offset,
1210             .right = picture->format.i_x_offset + texDesc.Width,
1211             .back = 1,
1212         };
1213         ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
1214                                                   sys->stagingSys.resource[KNOWN_DXGI_INDEX],
1215                                                   0, 0, 0, 0,
1216                                                   p_sys->resource[KNOWN_DXGI_INDEX],
1217                                                   p_sys->slice_index, &box);
1218     }
1219
1220     if (subpicture) {
1221         int subpicture_region_count    = 0;
1222         picture_t **subpicture_regions = NULL;
1223         Direct3D11MapSubpicture(vd, &subpicture_region_count, &subpicture_regions, subpicture);
1224         Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
1225         sys->d3dregion_count = subpicture_region_count;
1226         sys->d3dregions      = subpicture_regions;
1227     }
1228
1229     ID3D11DeviceContext_Flush(sys->d3dcontext);
1230 #if defined(HAVE_ID3D11VIDEODECODER)
1231     if ( sys->context_lock != INVALID_HANDLE_VALUE)
1232         ReleaseMutex( sys->context_lock );
1233 #endif
1234 }
1235
1236 static void DisplayD3DPicture(vout_display_sys_t *sys, d3d_quad_t *quad, ID3D11ShaderResourceView *resourceView[D3D11_MAX_SHADER_VIEW])
1237 {
1238     UINT stride = sizeof(d3d_vertex_t);
1239     UINT offset = 0;
1240
1241     /* Render the quad */
1242     /* vertex shader */
1243     ID3D11DeviceContext_IASetVertexBuffers(sys->d3dcontext, 0, 1, &quad->pVertexBuffer, &stride, &offset);
1244     ID3D11DeviceContext_IASetIndexBuffer(sys->d3dcontext, quad->pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
1245     if ( quad->pVertexShaderConstants )
1246         ID3D11DeviceContext_VSSetConstantBuffers(sys->d3dcontext, 0, 1, &quad->pVertexShaderConstants);
1247
1248     ID3D11DeviceContext_VSSetShader(sys->d3dcontext, quad->d3dvertexShader, NULL, 0);
1249
1250     /* pixel shader */
1251     ID3D11DeviceContext_PSSetShader(sys->d3dcontext, quad->d3dpixelShader, NULL, 0);
1252
1253     ID3D11DeviceContext_PSSetConstantBuffers(sys->d3dcontext, 0, quad->PSConstantsCount, quad->pPixelShaderConstants);
1254     ID3D11DeviceContext_PSSetShaderResources(sys->d3dcontext, 0, D3D11_MAX_SHADER_VIEW, resourceView);
1255
1256     ID3D11DeviceContext_RSSetViewports(sys->d3dcontext, 1, &quad->cropViewport);
1257
1258     ID3D11DeviceContext_DrawIndexed(sys->d3dcontext, quad->indexCount, 0, 0);
1259 }
1260
1261 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
1262 {
1263     vout_display_sys_t *sys = vd->sys;
1264 #ifdef HAVE_ID3D11VIDEODECODER
1265     if (sys->context_lock != INVALID_HANDLE_VALUE && is_d3d11_opaque(picture->format.i_chroma))
1266     {
1267         WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1268     }
1269 #endif
1270
1271     FLOAT blackRGBA[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1272     ID3D11DeviceContext_ClearRenderTargetView(sys->d3dcontext, sys->d3drenderTargetView, blackRGBA);
1273
1274     /* no ID3D11Device operations should come here */
1275
1276     ID3D11DeviceContext_OMSetRenderTargets(sys->d3dcontext, 1, &sys->d3drenderTargetView, sys->d3ddepthStencilView);
1277
1278     ID3D11DeviceContext_ClearDepthStencilView(sys->d3dcontext, sys->d3ddepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
1279
1280     /* Render the quad */
1281     if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader)
1282         DisplayD3DPicture(sys, &sys->picQuad, sys->stagingSys.resourceView);
1283     else
1284         DisplayD3DPicture(sys, &sys->picQuad, picture->p_sys->resourceView);
1285
1286     if (subpicture) {
1287         // draw the additional vertices
1288         for (int i = 0; i < sys->d3dregion_count; ++i) {
1289             if (sys->d3dregions[i])
1290             {
1291                 d3d_quad_t *quad = (d3d_quad_t *) sys->d3dregions[i]->p_sys;
1292                 DisplayD3DPicture(sys, quad, quad->picSys.resourceView);
1293             }
1294         }
1295     }
1296
1297 #if defined(HAVE_ID3D11VIDEODECODER)
1298     if (sys->context_lock != INVALID_HANDLE_VALUE && is_d3d11_opaque(picture->format.i_chroma))
1299     {
1300         ReleaseMutex( sys->context_lock );
1301     }
1302 #endif
1303
1304     if (sys->dxgiswapChain4 && picture->format.mastering.max_luminance)
1305     {
1306         DXGI_HDR_METADATA_HDR10 hdr10 = {0};
1307         hdr10.GreenPrimary[0] = picture->format.mastering.primaries[0];
1308         hdr10.GreenPrimary[1] = picture->format.mastering.primaries[1];
1309         hdr10.BluePrimary[0]  = picture->format.mastering.primaries[2];
1310         hdr10.BluePrimary[1]  = picture->format.mastering.primaries[3];
1311         hdr10.RedPrimary[0]   = picture->format.mastering.primaries[4];
1312         hdr10.RedPrimary[1]   = picture->format.mastering.primaries[5];
1313         hdr10.WhitePoint[0] = picture->format.mastering.white_point[0];
1314         hdr10.WhitePoint[1] = picture->format.mastering.white_point[1];
1315         hdr10.MinMasteringLuminance = picture->format.mastering.min_luminance;
1316         hdr10.MaxMasteringLuminance = picture->format.mastering.max_luminance;
1317         hdr10.MaxContentLightLevel = picture->format.lighting.MaxCLL;
1318         hdr10.MaxFrameAverageLightLevel = picture->format.lighting.MaxFALL;
1319         IDXGISwapChain4_SetHDRMetaData(sys->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10), &hdr10);
1320     }
1321
1322
1323     DXGI_PRESENT_PARAMETERS presentParams;
1324     memset(&presentParams, 0, sizeof(presentParams));
1325     HRESULT hr = IDXGISwapChain1_Present1(sys->dxgiswapChain, 0, 0, &presentParams);
1326     if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
1327     {
1328         /* TODO device lost */
1329         msg_Dbg(vd, "SwapChain Present failed. (hr=0x%lX)", hr);
1330     }
1331
1332     picture_Release(picture);
1333     if (subpicture)
1334         subpicture_Delete(subpicture);
1335
1336     CommonDisplay(vd);
1337 }
1338
1339 static void Direct3D11Destroy(vout_display_t *vd)
1340 {
1341 #if !VLC_WINSTORE_APP
1342     vout_display_sys_t *sys = vd->sys;
1343
1344     if (sys->hd3d11_dll)
1345         FreeLibrary(sys->hd3d11_dll);
1346     if (sys->hd3dcompiler_dll)
1347         FreeLibrary(sys->hd3dcompiler_dll);
1348
1349     sys->OurD3D11CreateDevice = NULL;
1350     sys->OurD3D11CreateDeviceAndSwapChain = NULL;
1351     sys->OurD3DCompile = NULL;
1352     sys->hdxgi_dll = NULL;
1353     sys->hd3d11_dll = NULL;
1354     sys->hd3dcompiler_dll = NULL;
1355 #else
1356     VLC_UNUSED(vd);
1357 #endif
1358 }
1359
1360 #if !VLC_WINSTORE_APP
1361 static HINSTANCE Direct3D11LoadShaderLibrary(void)
1362 {
1363     HINSTANCE instance = NULL;
1364     /* d3dcompiler_47 is the latest on windows 8.1 */
1365     for (int i = 47; i > 41; --i) {
1366         TCHAR filename[19];
1367         _sntprintf(filename, 19, TEXT("D3DCOMPILER_%d.dll"), i);
1368         instance = LoadLibrary(filename);
1369         if (instance) break;
1370     }
1371     return instance;
1372 }
1373 #endif
1374
1375 #define COLOR_RANGE_FULL   1 /* 0-255 */
1376 #define COLOR_RANGE_STUDIO 0 /* 16-235 */
1377
1378 #define TRANSFER_FUNC_10    TRANSFER_FUNC_LINEAR
1379 #define TRANSFER_FUNC_22    TRANSFER_FUNC_SRGB
1380 #define TRANSFER_FUNC_2084  TRANSFER_FUNC_SMPTE_ST2084
1381
1382 #define COLOR_PRIMARIES_BT601  COLOR_PRIMARIES_BT601_525
1383
1384 static const dxgi_color_space color_spaces[] = {
1385 #define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
1386     { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
1387       #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
1388       COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
1389       COLOR_SPACE_BT##PRIMARIES, COLOR_RANGE_##RANGE},
1390
1391     DXGIMAP(RGB,   FULL,     22,    NONE,   709)
1392     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   601)
1393     DXGIMAP(YCBCR, FULL,     22,    LEFT,   601)
1394     DXGIMAP(RGB,   FULL,     10,    NONE,   709)
1395     DXGIMAP(RGB,   STUDIO,   22,    NONE,   709)
1396     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,   709)
1397     DXGIMAP(YCBCR, FULL,     22,    LEFT,   709)
1398     DXGIMAP(RGB,   STUDIO,   22,    NONE,  2020)
1399     DXGIMAP(YCBCR, STUDIO,   22,    LEFT,  2020)
1400     DXGIMAP(YCBCR, FULL,     22,    LEFT,  2020)
1401     DXGIMAP(YCBCR, STUDIO,   22, TOPLEFT,  2020)
1402     DXGIMAP(RGB,   FULL,     22,    NONE,  2020)
1403     DXGIMAP(RGB,   FULL,   2084,    NONE,  2020)
1404     DXGIMAP(YCBCR, STUDIO, 2084,    LEFT,  2020)
1405     DXGIMAP(RGB,   STUDIO, 2084,    NONE,  2020)
1406     DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT,  2020)
1407     /*DXGIMAP(YCBCR, FULL,     22,    NONE,  2020, 601)*/
1408     {DXGI_COLOR_SPACE_RESERVED, NULL, 0, 0, 0, 0, 0},
1409 #undef DXGIMAP
1410 };
1411
1412 static void D3D11SetColorSpace(vout_display_t *vd)
1413 {
1414     vout_display_sys_t *sys = vd->sys;
1415     HRESULT hr;
1416     int best = -1;
1417     int score, best_score = 0;
1418     UINT support;
1419     IDXGISwapChain3 *dxgiswapChain3 = NULL;
1420     sys->display.colorspace = &color_spaces[0];
1421
1422     hr = ID3D11Device_QueryInterface( sys->dxgiswapChain, &IID_IDXGISwapChain3, (void **)&dxgiswapChain3);
1423     if (FAILED(hr)) {
1424         msg_Warn(vd, "could not get a IDXGISwapChain3");
1425         goto done;
1426     }
1427
1428     bool src_full_range = vd->source.b_color_range_full ||
1429                           /* the YUV->RGB conversion already output full range */
1430                           is_d3d11_opaque(vd->source.i_chroma) ||
1431                           vlc_fourcc_IsYUV(vd->source.i_chroma);
1432 #if VLC_WINSTORE_APP
1433     if (!src_full_range && isXboxHardware(sys->d3ddevice)) {
1434         /* even in full range the Xbox outputs with less range so use the max we can*/
1435         src_full_range = true;
1436     }
1437 #endif
1438
1439     /* pick the best output based on color support and transfer */
1440     /* TODO support YUV output later */
1441     for (int i=0; color_spaces[i].name; ++i)
1442     {
1443         hr = IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3, color_spaces[i].dxgi, &support);
1444         if (SUCCEEDED(hr) && support) {
1445             msg_Dbg(vd, "supports colorspace %s", color_spaces[i].name);
1446             score = 0;
1447             if (color_spaces[i].primaries == vd->source.primaries)
1448                 score++;
1449             if (color_spaces[i].color == vd->source.space)
1450                 score += 2; /* we don't want to translate color spaces */
1451             if (color_spaces[i].transfer == vd->source.transfer ||
1452                 /* favor 2084 output for HLG source */
1453                 (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && vd->source.transfer == TRANSFER_FUNC_HLG))
1454                 score++;
1455             if (color_spaces[i].b_full_range == src_full_range)
1456                 score++;
1457             if (score > best_score || (score && best == -1)) {
1458                 best = i;
1459                 best_score = score;
1460             }
1461         }
1462     }
1463
1464     if (best == -1)
1465     {
1466         best = 0;
1467         msg_Warn(vd, "no matching colorspace found force %s", color_spaces[best].name);
1468     }
1469     hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
1470     if (SUCCEEDED(hr))
1471     {
1472         sys->display.colorspace = &color_spaces[best];
1473         msg_Dbg(vd, "using colorspace %s", sys->display.colorspace->name);
1474     }
1475     else
1476         msg_Err(vd, "Failed to set colorspace %s. (hr=0x%lX)", sys->display.colorspace->name, hr);
1477 done:
1478     /* guestimate the display peak luminance */
1479     switch (sys->display.colorspace->transfer)
1480     {
1481     case TRANSFER_FUNC_LINEAR:
1482     case TRANSFER_FUNC_SRGB:
1483         sys->display.luminance_peak = DEFAULT_BRIGHTNESS;
1484         break;
1485     case TRANSFER_FUNC_SMPTE_ST2084:
1486         sys->display.luminance_peak = 10000;
1487         break;
1488     /* there is no other output transfer on Windows */
1489     default:
1490         vlc_assert_unreachable();
1491     }
1492
1493     if (dxgiswapChain3)
1494         IDXGISwapChain3_Release(dxgiswapChain3);
1495 }
1496
1497 static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
1498 {
1499     vout_display_sys_t *sys = vd->sys;
1500     IDXGIFactory2 *dxgifactory;
1501
1502     *fmt = vd->source;
1503
1504 #if !VLC_WINSTORE_APP
1505
1506     UINT creationFlags = 0;
1507     HRESULT hr = S_OK;
1508
1509 # if !defined(NDEBUG)
1510     HINSTANCE sdklayer_dll = LoadLibrary(TEXT("d3d11_1sdklayers.dll"));
1511     if (sdklayer_dll) {
1512         creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
1513         FreeLibrary(sdklayer_dll);
1514     }
1515 # endif
1516
1517     if (is_d3d11_opaque(fmt->i_chroma))
1518         creationFlags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
1519
1520     DXGI_SWAP_CHAIN_DESC1 scd;
1521     memset(&scd, 0, sizeof(scd));
1522     scd.BufferCount = 2;
1523     scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1524     scd.SampleDesc.Count = 1;
1525     scd.SampleDesc.Quality = 0;
1526     scd.Width = fmt->i_visible_width;
1527     scd.Height = fmt->i_visible_height;
1528     switch(fmt->i_chroma)
1529     {
1530     case VLC_CODEC_D3D11_OPAQUE_10B:
1531         scd.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
1532         break;
1533     default:
1534         scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; /* TODO: use DXGI_FORMAT_NV12 */
1535         break;
1536     }
1537     //scd.Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1538     scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1539
1540     static const D3D_DRIVER_TYPE driverAttempts[] = {
1541         D3D_DRIVER_TYPE_HARDWARE,
1542         D3D_DRIVER_TYPE_WARP,
1543 #ifndef NDEBUG
1544         D3D_DRIVER_TYPE_REFERENCE,
1545 #endif
1546     };
1547
1548     for (UINT driver = 0; driver < ARRAYSIZE(driverAttempts); driver++) {
1549         D3D_FEATURE_LEVEL i_feature_level;
1550         hr = D3D11CreateDevice(NULL, driverAttempts[driver], NULL, creationFlags,
1551                     NULL, 0, D3D11_SDK_VERSION,
1552                     &sys->d3ddevice, &i_feature_level, &sys->d3dcontext);
1553         if (SUCCEEDED(hr)) {
1554 #ifndef NDEBUG
1555             msg_Dbg(vd, "Created the D3D11 device 0x%p ctx 0x%p type %d level %x.",
1556                     (void *)sys->d3ddevice, (void *)sys->d3dcontext,
1557                     driverAttempts[driver], i_feature_level);
1558 #endif
1559             break;
1560         }
1561     }
1562
1563     if (FAILED(hr)) {
1564        msg_Err(vd, "Could not Create the D3D11 device. (hr=0x%lX)", hr);
1565        return VLC_EGENERIC;
1566     }
1567
1568     IDXGIAdapter *dxgiadapter = D3D11DeviceAdapter(sys->d3ddevice);
1569     if (FAILED(hr)) {
1570        msg_Err(vd, "Could not get the DXGI Adapter");
1571        return VLC_EGENERIC;
1572     }
1573
1574     hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
1575     IDXGIAdapter_Release(dxgiadapter);
1576     if (FAILED(hr)) {
1577        msg_Err(vd, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
1578        return VLC_EGENERIC;
1579     }
1580
1581     hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)sys->d3ddevice,
1582                                               sys->sys.hvideownd, &scd, NULL, NULL, &sys->dxgiswapChain);
1583     IDXGIFactory2_Release(dxgifactory);
1584     if (FAILED(hr)) {
1585        msg_Err(vd, "Could not create the SwapChain. (hr=0x%lX)", hr);
1586        return VLC_EGENERIC;
1587     }
1588 #endif
1589
1590     ID3D11Device_QueryInterface( sys->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&sys->dxgiswapChain4);
1591
1592     D3D11SetColorSpace(vd);
1593
1594     // look for the requested pixel format first
1595     sys->picQuadConfig = GetOutputFormat(vd, fmt->i_chroma, 0, true, false);
1596
1597     // look for any pixel format that we can handle with enough pixels per channel
1598     if ( !sys->picQuadConfig )
1599     {
1600         uint8_t bits_per_channel;
1601         switch (fmt->i_chroma)
1602         {
1603         case VLC_CODEC_D3D11_OPAQUE:
1604             bits_per_channel = 8;
1605             break;
1606         case VLC_CODEC_D3D11_OPAQUE_10B:
1607             bits_per_channel = 10;
1608             break;
1609         default:
1610             {
1611                 const vlc_chroma_description_t *p_format = vlc_fourcc_GetChromaDescription(fmt->i_chroma);
1612                 bits_per_channel = p_format == NULL || p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits;
1613             }
1614             break;
1615         }
1616
1617         sys->picQuadConfig = GetOutputFormat(vd, 0, bits_per_channel, false, false);
1618     }
1619
1620     // look for any pixel format that we can handle
1621     if ( !sys->picQuadConfig )
1622         sys->picQuadConfig = GetOutputFormat(vd, 0, 0, false, false);
1623
1624     if ( !sys->picQuadConfig )
1625     {
1626        msg_Err(vd, "Could not get a suitable texture pixel format");
1627        return VLC_EGENERIC;
1628     }
1629     msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", sys->picQuadConfig->name,
1630                  (char *)&fmt->i_chroma );
1631     fmt->i_chroma = sys->picQuadConfig->fourcc;
1632     DxgiFormatMask( sys->picQuadConfig->formatTexture, fmt );
1633
1634     /* check the region pixel format */
1635     sys->d3dregion_format = GetOutputFormat(vd, VLC_CODEC_RGBA, 0, false, true);
1636     if (!sys->d3dregion_format)
1637         sys->d3dregion_format = GetOutputFormat(vd, VLC_CODEC_BGRA, 0, false, true);
1638
1639     UpdateRects(vd, NULL, NULL, true);
1640
1641 #if defined(HAVE_ID3D11VIDEODECODER)
1642     if( sys->context_lock != INVALID_HANDLE_VALUE )
1643     {
1644         WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1645     }
1646 #endif
1647     if (Direct3D11CreateResources(vd, fmt)) {
1648 #if defined(HAVE_ID3D11VIDEODECODER)
1649         if( sys->context_lock != INVALID_HANDLE_VALUE )
1650         {
1651             ReleaseMutex( sys->context_lock );
1652         }
1653 #endif
1654         msg_Err(vd, "Failed to allocate resources");
1655         Direct3D11DestroyResources(vd);
1656         return VLC_EGENERIC;
1657     }
1658 #if defined(HAVE_ID3D11VIDEODECODER)
1659     if( sys->context_lock != INVALID_HANDLE_VALUE )
1660     {
1661         ReleaseMutex( sys->context_lock );
1662     }
1663 #endif
1664
1665 #if !VLC_WINSTORE_APP
1666     EventThreadUpdateTitle(sys->sys.event, VOUT_TITLE " (Direct3D11 output)");
1667 #endif
1668
1669     msg_Dbg(vd, "Direct3D11 device adapter successfully initialized");
1670     return VLC_SUCCESS;
1671 }
1672
1673 static void Direct3D11Close(vout_display_t *vd)
1674 {
1675     vout_display_sys_t *sys = vd->sys;
1676
1677     Direct3D11DestroyResources(vd);
1678     if (sys->d3dcontext)
1679     {
1680         ID3D11DeviceContext_Flush(sys->d3dcontext);
1681         ID3D11DeviceContext_Release(sys->d3dcontext);
1682         sys->d3dcontext = NULL;
1683     }
1684     if (sys->d3ddevice)
1685     {
1686         ID3D11Device_Release(sys->d3ddevice);
1687         sys->d3ddevice = NULL;
1688     }
1689     if (sys->dxgiswapChain4)
1690     {
1691         IDXGISwapChain_Release(sys->dxgiswapChain4);
1692         sys->dxgiswapChain4 = NULL;
1693     }
1694     if (sys->dxgiswapChain)
1695     {
1696         IDXGISwapChain_Release(sys->dxgiswapChain);
1697         sys->dxgiswapChain = NULL;
1698     }
1699
1700     msg_Dbg(vd, "Direct3D11 device adapter closed");
1701 }
1702
1703 static void UpdatePicQuadPosition(vout_display_t *vd)
1704 {
1705     vout_display_sys_t *sys = vd->sys;
1706
1707     sys->picQuad.cropViewport.Width    = RECTWidth(sys->sys.rect_dest_clipped);
1708     sys->picQuad.cropViewport.Height   = RECTHeight(sys->sys.rect_dest_clipped);
1709     sys->picQuad.cropViewport.TopLeftX = sys->sys.rect_dest_clipped.left;
1710     sys->picQuad.cropViewport.TopLeftY = sys->sys.rect_dest_clipped.top;
1711
1712     sys->picQuad.cropViewport.MinDepth = 0.0f;
1713     sys->picQuad.cropViewport.MaxDepth = 1.0f;
1714
1715     SetQuadVSProjection(vd, &sys->picQuad, &vd->cfg->viewpoint);
1716
1717 #ifndef NDEBUG
1718     msg_Dbg(vd, "picQuad position (%.02f,%.02f) %.02fx%.02f", sys->picQuad.cropViewport.TopLeftX, sys->picQuad.cropViewport.TopLeftY, sys->picQuad.cropViewport.Width, sys->picQuad.cropViewport.Height );
1719 #endif
1720 }
1721
1722 static ID3DBlob* CompileShader(vout_display_t *vd, const char *psz_shader, bool pixel)
1723 {
1724     vout_display_sys_t *sys = vd->sys;
1725     ID3DBlob* pShaderBlob = NULL, *pErrBlob;
1726
1727     /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1728     HRESULT hr = D3DCompile(psz_shader, strlen(psz_shader),
1729                             NULL, NULL, NULL, "main",
1730                             pixel ? (sys->legacy_shader ? "ps_4_0_level_9_1" : "ps_4_0") :
1731                                     (sys->legacy_shader ? "vs_4_0_level_9_1" : "vs_4_0"),
1732                             0, 0, &pShaderBlob, &pErrBlob);
1733
1734     if (FAILED(hr)) {
1735         char *err = pErrBlob ? ID3D10Blob_GetBufferPointer(pErrBlob) : NULL;
1736         msg_Err(vd, "invalid %s Shader (hr=0x%lX): %s", pixel?"Pixel":"Vertex", hr, err );
1737         if (pErrBlob)
1738             ID3D10Blob_Release(pErrBlob);
1739         return NULL;
1740     }
1741     return pShaderBlob;
1742 }
1743
1744 static bool IsRGBShader(const d3d_format_t *cfg)
1745 {
1746     return cfg->resourceFormat[0] != DXGI_FORMAT_R8_UNORM &&
1747            cfg->resourceFormat[0] != DXGI_FORMAT_R16_UNORM &&
1748            cfg->formatTexture != DXGI_FORMAT_YUY2;
1749 }
1750
1751 static HRESULT CompilePixelShader(vout_display_t *vd, const d3d_format_t *format,
1752                                   video_transfer_func_t transfer, bool src_full_range,
1753                                   ID3D11PixelShader **output)
1754 {
1755     vout_display_sys_t *sys = vd->sys;
1756
1757     static const char *DEFAULT_NOOP = "return rgb";
1758     const char *psz_sampler;
1759     const char *psz_src_transform     = DEFAULT_NOOP;
1760     const char *psz_display_transform = DEFAULT_NOOP;
1761     const char *psz_tone_mapping      = DEFAULT_NOOP;
1762     const char *psz_peak_luminance    = DEFAULT_NOOP;
1763     const char *psz_adjust_range      = DEFAULT_NOOP;
1764     char *psz_luminance = NULL;
1765     char *psz_range = NULL;
1766
1767     switch (format->formatTexture)
1768     {
1769     case DXGI_FORMAT_NV12:
1770     case DXGI_FORMAT_P010:
1771         psz_sampler =
1772                 "sample.x  = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1773                 sample.yz = shaderTexture[1].Sample(SampleType, In.Texture).xy;\
1774                 sample.a  = 1;";
1775         break;
1776     case DXGI_FORMAT_YUY2:
1777         psz_sampler =
1778                 "sample.x  = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1779                 sample.y  = shaderTexture[0].Sample(SampleType, In.Texture).y;\
1780                 sample.z  = shaderTexture[0].Sample(SampleType, In.Texture).a;\
1781                 sample.a  = 1;";
1782         break;
1783     case DXGI_FORMAT_R8G8B8A8_UNORM:
1784     case DXGI_FORMAT_B8G8R8A8_UNORM:
1785     case DXGI_FORMAT_B5G6R5_UNORM:
1786         psz_sampler =
1787                 "sample = shaderTexture[0].Sample(SampleType, In.Texture);";
1788         break;
1789     default:
1790         vlc_assert_unreachable();
1791     }
1792
1793     video_transfer_func_t src_transfer;
1794     unsigned src_luminance_peak;
1795     switch (transfer)
1796     {
1797         case TRANSFER_FUNC_SMPTE_ST2084:
1798             /* TODO ajust this value using HDR metadata ? */
1799             src_luminance_peak = 7500;
1800             break;
1801         case TRANSFER_FUNC_HLG:
1802             src_luminance_peak = 1000;
1803             break;
1804         case TRANSFER_FUNC_BT709:
1805         case TRANSFER_FUNC_SRGB:
1806             src_luminance_peak = DEFAULT_BRIGHTNESS;
1807             break;
1808         default:
1809             msg_Dbg(vd, "unhandled source transfer %d", transfer);
1810             src_luminance_peak = DEFAULT_BRIGHTNESS;
1811             break;
1812     }
1813
1814     if (transfer != sys->display.colorspace->transfer)
1815     {
1816         /* we need to go in linear mode */
1817         switch (transfer)
1818         {
1819             case TRANSFER_FUNC_SMPTE_ST2084:
1820                 /* ST2084 to Linear */
1821                 psz_src_transform =
1822                        ST2084_PQ_CONSTANTS
1823                        "rgb = pow(rgb, 1.0/ST2084_m2);\
1824                         rgb = max(rgb - ST2084_c1, 0.0) / (ST2084_c2 - ST2084_c3 * rgb);\
1825                         rgb = pow(rgb, 1.0/ST2084_m1);\
1826                         return rgb";
1827                 src_transfer = TRANSFER_FUNC_LINEAR;
1828                 break;
1829             case TRANSFER_FUNC_HLG:
1830                 /* HLG to Linear */
1831                 psz_src_transform =
1832                        "rgb.r = inverse_HLG(rgb.r);\
1833                         rgb.g = inverse_HLG(rgb.g);\
1834                         rgb.b = inverse_HLG(rgb.b);\
1835                         return rgb / 20.0";
1836                 src_transfer = TRANSFER_FUNC_LINEAR;
1837                 break;
1838             case TRANSFER_FUNC_BT709:
1839                 psz_src_transform = "return pow(rgb, 1.0 / 0.45)";
1840                 src_transfer = TRANSFER_FUNC_LINEAR;
1841                 break;
1842             case TRANSFER_FUNC_SRGB:
1843                 psz_src_transform = "return pow(rgb, 2.2)";
1844                 src_transfer = TRANSFER_FUNC_LINEAR;
1845                 break;
1846             default:
1847                 msg_Dbg(vd, "unhandled source transfer %d", transfer);
1848                 src_transfer = transfer;
1849                 break;
1850         }
1851
1852         switch (sys->display.colorspace->transfer)
1853         {
1854             case TRANSFER_FUNC_SRGB:
1855                 if (src_transfer == TRANSFER_FUNC_LINEAR)
1856                 {
1857                     /* Linear to sRGB */
1858                     psz_display_transform = "return pow(rgb, 1.0 / 2.2)";
1859
1860                     if (transfer == TRANSFER_FUNC_SMPTE_ST2084 || transfer == TRANSFER_FUNC_HLG)
1861                     {
1862                         /* HDR tone mapping */
1863                         psz_tone_mapping =
1864                             "static const float3 HABLE_DIV = hable(11.2);\
1865                             rgb = hable(rgb) / HABLE_DIV;\
1866                             return rgb";
1867                     }
1868                 }
1869                 else
1870                     msg_Warn(vd, "don't know how to transfer from %d to sRGB", src_transfer);
1871                 break;
1872
1873             case TRANSFER_FUNC_SMPTE_ST2084:
1874                 if (src_transfer == TRANSFER_FUNC_LINEAR)
1875                 {
1876                     /* Linear to ST2084 */
1877                     psz_display_transform =
1878                            ST2084_PQ_CONSTANTS
1879                            "rgb = pow(rgb, ST2084_m1);\
1880                             rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
1881                             rgb = pow(rgb, ST2084_m2);\
1882                             return rgb";
1883                 }
1884                 else
1885                     msg_Warn(vd, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer);
1886                 break;
1887             default:
1888                 msg_Warn(vd, "don't know how to transfer from %d to %d", src_transfer, sys->display.colorspace->transfer);
1889                 break;
1890         }
1891     }
1892
1893     if (src_luminance_peak != sys->display.luminance_peak)
1894     {
1895         psz_luminance = malloc(64);
1896         if (likely(psz_luminance))
1897         {
1898             sprintf(psz_luminance, "return rgb * %f", (float)src_luminance_peak / (float)sys->display.luminance_peak);
1899             psz_peak_luminance = psz_luminance;
1900         }
1901     }
1902
1903     int range_adjust = sys->display.colorspace->b_full_range;
1904 #if VLC_WINSTORE_APP
1905     if (isXboxHardware(sys->d3ddevice)) {
1906         /* the Xbox lies, when it says it outputs full range it's less than full range */
1907         range_adjust--;
1908     }
1909 #endif
1910     if (!IsRGBShader(format))
1911         range_adjust--; /* the YUV->RGB conversion already output full range */
1912     if (src_full_range)
1913         range_adjust--;
1914
1915     if (range_adjust != 0)
1916     {
1917         psz_range = malloc(256);
1918         if (likely(psz_range))
1919         {
1920             FLOAT itu_black_level;
1921             FLOAT itu_range_factor;
1922             FLOAT itu_white_level;
1923             switch (format->bitsPerChannel)
1924             {
1925             case 8:
1926                 /* Rec. ITU-R BT.709-6 §4.6 */
1927                 itu_black_level  =              16.f / 255.f;
1928                 itu_white_level  =             235.f / 255.f;
1929                 itu_range_factor = (float)(235 - 16) / 255.f;
1930                 break;
1931             case 10:
1932                 /* Rec. ITU-R BT.709-6 §4.6 */
1933                 itu_black_level  =              64.f / 1023.f;
1934                 itu_white_level  =             940.f / 1023.f;
1935                 itu_range_factor = (float)(940 - 64) / 1023.f;
1936                 break;
1937             case 12:
1938                 /* Rec. ITU-R BT.2020-2 Table 5 */
1939                 itu_black_level  =               256.f / 4095.f;
1940                 itu_white_level  =              3760.f / 4095.f;
1941                 itu_range_factor = (float)(3760 - 256) / 4095.f;
1942                 break;
1943             default:
1944                 /* unknown bitdepth, use approximation for infinite bit depth */
1945                 itu_black_level  =              16.f / 256.f;
1946                 itu_white_level  =             235.f / 256.f;
1947                 itu_range_factor = (float)(235 - 16) / 256.f;
1948                 break;
1949             }
1950
1951             FLOAT black_level = 0;
1952             FLOAT range_factor = 1.0f;
1953             if (range_adjust > 0)
1954             {
1955                 /* expand the range from studio to full range */
1956                 while (range_adjust--)
1957                 {
1958                     black_level -= itu_black_level;
1959                     range_factor /= itu_range_factor;
1960                 }
1961                 sprintf(psz_range, "return max(0,min(1,(rgb + %f) * %f))",
1962                         black_level, range_factor);
1963             }
1964             else
1965             {
1966                 /* shrink the range to studio range */
1967                 while (range_adjust++)
1968                 {
1969                     black_level += itu_black_level;
1970                     range_factor *= itu_range_factor;
1971                 }
1972                 sprintf(psz_range, "return clamp(rgb + %f * %f,%f,%f)",
1973                         black_level, range_factor, itu_black_level, itu_white_level);
1974             }
1975             psz_adjust_range = psz_range;
1976         }
1977     }
1978
1979     char *shader = malloc(strlen(globPixelShaderDefault) + 32 + strlen(psz_sampler) +
1980                           strlen(psz_src_transform) + strlen(psz_display_transform) +
1981                           strlen(psz_tone_mapping) + strlen(psz_peak_luminance) + strlen(psz_adjust_range));
1982     if (!shader)
1983     {
1984         msg_Err(vd, "no room for the Pixel Shader");
1985         free(psz_luminance);
1986         free(psz_range);
1987         return E_OUTOFMEMORY;
1988     }
1989     sprintf(shader, globPixelShaderDefault, sys->legacy_shader ? "" : "Array", psz_src_transform,
1990             psz_display_transform, psz_tone_mapping, psz_peak_luminance, psz_adjust_range, psz_sampler);
1991 #ifndef NDEBUG
1992     if (!IsRGBShader(format)) {
1993         msg_Dbg(vd,"psz_src_transform %s", psz_src_transform);
1994         msg_Dbg(vd,"psz_tone_mapping %s", psz_tone_mapping);
1995         msg_Dbg(vd,"psz_peak_luminance %s", psz_peak_luminance);
1996         msg_Dbg(vd,"psz_display_transform %s", psz_display_transform);
1997         msg_Dbg(vd,"psz_adjust_range %s", psz_adjust_range);
1998     }
1999 #endif
2000     free(psz_luminance);
2001     free(psz_range);
2002
2003     ID3DBlob *pPSBlob = CompileShader(vd, shader, true);
2004     free(shader);
2005     if (!pPSBlob)
2006         return E_INVALIDARG;
2007
2008     HRESULT hr = ID3D11Device_CreatePixelShader(sys->d3ddevice,
2009                                                 (void *)ID3D10Blob_GetBufferPointer(pPSBlob),
2010                                                 ID3D10Blob_GetBufferSize(pPSBlob), NULL, output);
2011
2012     ID3D10Blob_Release(pPSBlob);
2013     return hr;
2014 }
2015
2016 #if (!VLC_WINSTORE_APP) && defined (HAVE_ID3D11VIDEODECODER)
2017
2018 static HKEY GetAdapterRegistry(DXGI_ADAPTER_DESC *adapterDesc)
2019 {
2020     HKEY hKey;
2021     TCHAR key[128];
2022     TCHAR szData[256], lookup[256];
2023     DWORD len = 256;
2024
2025     _sntprintf(lookup, 256, TEXT("pci\\ven_%04x&dev_%04x&rev_cf"), adapterDesc->VendorId, adapterDesc->DeviceId);
2026     for (int i=0;;i++)
2027     {
2028         _sntprintf(key, 128, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\%04d"), i);
2029         if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
2030             return NULL;
2031
2032         if( RegQueryValueEx( hKey, TEXT("MatchingDeviceId"), NULL, NULL, (LPBYTE) &szData, &len ) == ERROR_SUCCESS ) {
2033             if (_tcscmp(lookup, szData) == 0)
2034                 return hKey;
2035         }
2036
2037         RegCloseKey(hKey);
2038     }
2039     return NULL;
2040 }
2041 #endif
2042
2043 static bool CanUseTextureArray(vout_display_t *vd)
2044 {
2045 #ifndef HAVE_ID3D11VIDEODECODER
2046     (void) vd;
2047     return false;
2048 #else
2049     vout_display_sys_t *sys = vd->sys;
2050     TCHAR szData[256];
2051     DWORD len = 256;
2052     IDXGIAdapter *pAdapter = D3D11DeviceAdapter(sys->d3ddevice);
2053     if (!pAdapter)
2054         return false;
2055
2056     DXGI_ADAPTER_DESC adapterDesc;
2057     HRESULT hr = IDXGIAdapter_GetDesc(pAdapter, &adapterDesc);
2058     IDXGIAdapter_Release(pAdapter);
2059     if (FAILED(hr))
2060         return false;
2061
2062     /* ATI/AMD hardware has issues with pixel shaders with Texture2DArray */
2063     if (adapterDesc.VendorId != 0x1002)
2064         return true;
2065
2066 #if !VLC_WINSTORE_APP
2067     HKEY hKey = GetAdapterRegistry(&adapterDesc);
2068     if (hKey == NULL)
2069         return false;
2070
2071     LONG err = RegQueryValueEx( hKey, TEXT("DriverVersion"), NULL, NULL, (LPBYTE) &szData, &len );
2072     RegCloseKey(hKey);
2073
2074     if (err == ERROR_SUCCESS )
2075     {
2076         int wddm, d3d_features, revision, build;
2077         /* see https://msdn.microsoft.com/windows/hardware/commercialize/design/compatibility/device-graphics */
2078         if (_stscanf(szData, TEXT("%d.%d.%d.%d"), &wddm, &d3d_features, &revision, &build) == 4)
2079             /* it should be OK starting from driver 22.19.128.xxx */
2080             return wddm > 22 || (wddm == 22 && (d3d_features > 19 || (d3d_features == 19 && revision >= 128)));
2081     }
2082 #endif
2083     msg_Dbg(vd, "fallback to legacy shader mode for old AMD drivers");
2084     return false;
2085 #endif
2086 }
2087
2088 /* TODO : handle errors better
2089    TODO : seperate out into smaller functions like createshaders */
2090 static int Direct3D11CreateResources(vout_display_t *vd, video_format_t *fmt)
2091 {
2092     vout_display_sys_t *sys = vd->sys;
2093     HRESULT hr;
2094
2095 #if defined(HAVE_ID3D11VIDEODECODER)
2096     sys->context_lock = CreateMutexEx( NULL, NULL, 0, SYNCHRONIZE );
2097     ID3D11Device_SetPrivateData( sys->d3ddevice, &GUID_CONTEXT_MUTEX, sizeof( sys->context_lock ), &sys->context_lock );
2098 #endif
2099
2100     hr = UpdateBackBuffer(vd);
2101     if (FAILED(hr)) {
2102        msg_Err(vd, "Could not update the backbuffer. (hr=0x%lX)", hr);
2103        return VLC_EGENERIC;
2104     }
2105
2106     ID3D11BlendState *pSpuBlendState;
2107     D3D11_BLEND_DESC spuBlendDesc = { 0 };
2108     spuBlendDesc.RenderTarget[0].BlendEnable = TRUE;
2109     spuBlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
2110     spuBlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
2111     spuBlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
2112
2113     spuBlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
2114     spuBlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
2115     spuBlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
2116
2117     spuBlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
2118
2119     spuBlendDesc.RenderTarget[1].BlendEnable = TRUE;
2120     spuBlendDesc.RenderTarget[1].SrcBlend = D3D11_BLEND_ONE;
2121     spuBlendDesc.RenderTarget[1].DestBlend = D3D11_BLEND_ZERO;
2122     spuBlendDesc.RenderTarget[1].BlendOp = D3D11_BLEND_OP_ADD;
2123
2124     spuBlendDesc.RenderTarget[1].SrcBlendAlpha = D3D11_BLEND_ONE;
2125     spuBlendDesc.RenderTarget[1].DestBlendAlpha = D3D11_BLEND_ZERO;
2126     spuBlendDesc.RenderTarget[1].BlendOpAlpha = D3D11_BLEND_OP_ADD;
2127
2128     spuBlendDesc.RenderTarget[1].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
2129     hr = ID3D11Device_CreateBlendState(sys->d3ddevice, &spuBlendDesc, &pSpuBlendState);
2130     if (FAILED(hr)) {
2131        msg_Err(vd, "Could not create SPU blend state. (hr=0x%lX)", hr);
2132        return VLC_EGENERIC;
2133     }
2134     ID3D11DeviceContext_OMSetBlendState(sys->d3dcontext, pSpuBlendState, NULL, 0xFFFFFFFF);
2135     ID3D11BlendState_Release(pSpuBlendState);
2136
2137     /* disable depth testing as we're only doing 2D
2138      * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
2139      * see http://rastertek.com/dx11tut11.html
2140     */
2141     D3D11_DEPTH_STENCIL_DESC stencilDesc;
2142     ZeroMemory(&stencilDesc, sizeof(stencilDesc));
2143
2144     ID3D11DepthStencilState *pDepthStencilState;
2145     hr = ID3D11Device_CreateDepthStencilState(sys->d3ddevice, &stencilDesc, &pDepthStencilState );
2146     if (SUCCEEDED(hr)) {
2147         ID3D11DeviceContext_OMSetDepthStencilState(sys->d3dcontext, pDepthStencilState, 0);
2148         ID3D11DepthStencilState_Release(pDepthStencilState);
2149     }
2150
2151     sys->legacy_shader = !CanUseTextureArray(vd);
2152     vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma);
2153
2154     hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
2155     if (FAILED(hr))
2156     {
2157 #ifdef HAVE_ID3D11VIDEODECODER
2158         if (!sys->legacy_shader)
2159         {
2160             sys->legacy_shader = true;
2161             msg_Dbg(vd, "fallback to legacy shader mode");
2162             hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
2163         }
2164 #endif
2165         if (FAILED(hr))
2166         {
2167             msg_Err(vd, "Failed to create the pixel shader. (hr=0x%lX)", hr);
2168             return VLC_EGENERIC;
2169         }
2170     }
2171
2172     if (sys->d3dregion_format != NULL)
2173     {
2174         hr = CompilePixelShader(vd, sys->d3dregion_format, TRANSFER_FUNC_SRGB, true, &sys->pSPUPixelShader);
2175         if (FAILED(hr))
2176         {
2177             ID3D11PixelShader_Release(sys->picQuadPixelShader);
2178             sys->picQuadPixelShader = NULL;
2179             msg_Err(vd, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr);
2180             return VLC_EGENERIC;
2181         }
2182     }
2183
2184     ID3DBlob *pVSBlob = CompileShader(vd, globVertexShaderFlat , false);
2185     if (!pVSBlob)
2186         return VLC_EGENERIC;
2187
2188     hr = ID3D11Device_CreateVertexShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2189                                         ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->flatVSShader);
2190
2191     if(FAILED(hr)) {
2192       ID3D11Device_Release(pVSBlob);
2193       msg_Err(vd, "Failed to create the flat vertex shader. (hr=0x%lX)", hr);
2194       return VLC_EGENERIC;
2195     }
2196
2197     D3D11_INPUT_ELEMENT_DESC layout[] = {
2198     { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
2199     { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
2200     };
2201
2202     ID3D11InputLayout* pVertexLayout = NULL;
2203     hr = ID3D11Device_CreateInputLayout(sys->d3ddevice, layout, 2, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2204                                         ID3D10Blob_GetBufferSize(pVSBlob), &pVertexLayout);
2205
2206     ID3D10Blob_Release(pVSBlob);
2207
2208     if(FAILED(hr)) {
2209       msg_Err(vd, "Failed to create the vertex input layout. (hr=0x%lX)", hr);
2210       return VLC_EGENERIC;
2211     }
2212     ID3D11DeviceContext_IASetInputLayout(sys->d3dcontext, pVertexLayout);
2213     ID3D11InputLayout_Release(pVertexLayout);
2214
2215     pVSBlob = CompileShader(vd, globVertexShaderProjection, false);
2216     if (!pVSBlob)
2217         return VLC_EGENERIC;
2218
2219     hr = ID3D11Device_CreateVertexShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2220                                         ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->projectionVSShader);
2221
2222     if(FAILED(hr)) {
2223       ID3D11Device_Release(pVSBlob);
2224       msg_Err(vd, "Failed to create the projection vertex shader. (hr=0x%lX)", hr);
2225       return VLC_EGENERIC;
2226     }
2227     ID3D10Blob_Release(pVSBlob);
2228
2229     ID3D11DeviceContext_IASetPrimitiveTopology(sys->d3dcontext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
2230
2231     UpdatePicQuadPosition(vd);
2232
2233     D3D11_SAMPLER_DESC sampDesc;
2234     memset(&sampDesc, 0, sizeof(sampDesc));
2235     sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
2236     sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
2237     sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
2238     sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
2239     sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
2240     sampDesc.MinLOD = 0;
2241     sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
2242
2243     ID3D11SamplerState *d3dsampState;
2244     hr = ID3D11Device_CreateSamplerState(sys->d3ddevice, &sampDesc, &d3dsampState);
2245
2246     if (FAILED(hr)) {
2247       msg_Err(vd, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr);
2248       return VLC_EGENERIC;
2249     }
2250     ID3D11DeviceContext_PSSetSamplers(sys->d3dcontext, 0, 1, &d3dsampState);
2251     ID3D11SamplerState_Release(d3dsampState);
2252
2253     msg_Dbg(vd, "Direct3D11 resources created");
2254     return VLC_SUCCESS;
2255 }
2256
2257 static void Direct3D11DestroyPool(vout_display_t *vd)
2258 {
2259     vout_display_sys_t *sys = vd->sys;
2260
2261     if (sys->sys.pool)
2262         picture_pool_Release(sys->sys.pool);
2263     sys->sys.pool = NULL;
2264 }
2265
2266 static void SetupQuadFlat(d3d_vertex_t *dst_data, const video_format_t *fmt, d3d_quad_t *quad, WORD *triangle_pos)
2267 {
2268     unsigned int dst_width = fmt->i_visible_width;
2269     unsigned int dst_height = fmt->i_visible_height;
2270     unsigned int src_x = quad->i_x_offset;
2271     unsigned int src_y = quad->i_y_offset;
2272     unsigned int src_width = quad->i_width;
2273     unsigned int src_height = quad->i_height;
2274
2275     /* the clamping will not work properly on the side of the texture as it
2276      * will have decoder pixels not mean to be displayed but used for interpolation
2277      * So we lose the last line that will be partially green */
2278     if (src_width != dst_width)
2279         dst_width -= 1;
2280     if (src_height != dst_height)
2281         dst_height -= 1;
2282
2283     float right  =  (float) (2*src_width - dst_width - 2*src_x) / (float) dst_width;
2284     float left   = -(float) (2*src_x + dst_width) / (float) dst_width;
2285     float top    =  (float) (2*src_x + dst_height) / (float) dst_height;
2286     float bottom = -(float) (2*src_height - dst_height - 2*src_y) / (float) dst_height;
2287
2288     // bottom left
2289     dst_data[0].position.x = left;
2290     dst_data[0].position.y = bottom;
2291     dst_data[0].position.z = 0.0f;
2292     dst_data[0].texture.x = 0.0f;
2293     dst_data[0].texture.y = 1.0f;
2294
2295     // bottom right
2296     dst_data[1].position.x = right;
2297     dst_data[1].position.y = bottom;
2298     dst_data[1].position.z = 0.0f;
2299     dst_data[1].texture.x = 1.0f;
2300     dst_data[1].texture.y = 1.0f;
2301
2302     // top right
2303     dst_data[2].position.x = right;
2304     dst_data[2].position.y = top;
2305     dst_data[2].position.z = 0.0f;
2306     dst_data[2].texture.x = 1.0f;
2307     dst_data[2].texture.y = 0.0f;
2308
2309     // top left
2310     dst_data[3].position.x = left;
2311     dst_data[3].position.y = top;
2312     dst_data[3].position.z = 0.0f;
2313     dst_data[3].texture.x = 0.0f;
2314     dst_data[3].texture.y = 0.0f;
2315
2316     triangle_pos[0] = 3;
2317     triangle_pos[1] = 1;
2318     triangle_pos[2] = 0;
2319
2320     triangle_pos[3] = 2;
2321     triangle_pos[4] = 1;
2322     triangle_pos[5] = 3;
2323 }
2324
2325 #define SPHERE_SLICES 128
2326 #define nbLatBands SPHERE_SLICES
2327 #define nbLonBands SPHERE_SLICES
2328
2329 static void SetupQuadSphere(d3d_vertex_t *dst_data, WORD *triangle_pos)
2330 {
2331     for (unsigned lat = 0; lat <= nbLatBands; lat++) {
2332         float theta = lat * (float) M_PI / nbLatBands;
2333         float sinTheta, cosTheta;
2334
2335         sincosf(theta, &sinTheta, &cosTheta);
2336
2337         for (unsigned lon = 0; lon <= nbLonBands; lon++) {
2338             float phi = lon * 2 * (float) M_PI / nbLonBands;
2339             float sinPhi, cosPhi;
2340
2341             sincosf(phi, &sinPhi, &cosPhi);
2342
2343             float x = cosPhi * sinTheta;
2344             float y = cosTheta;
2345             float z = sinPhi * sinTheta;
2346
2347             unsigned off1 = lat * (nbLonBands + 1) + lon;
2348             dst_data[off1].position.x = SPHERE_RADIUS * x;
2349             dst_data[off1].position.y = SPHERE_RADIUS * y;
2350             dst_data[off1].position.z = SPHERE_RADIUS * z;
2351
2352             dst_data[off1].texture.x = lon / (float) nbLonBands; // 0(left) to 1(right)
2353             dst_data[off1].texture.y = lat / (float) nbLatBands; // 0(top) to 1 (bottom)
2354         }
2355     }
2356
2357     for (unsigned lat = 0; lat < nbLatBands; lat++) {
2358         for (unsigned lon = 0; lon < nbLonBands; lon++) {
2359             unsigned first = (lat * (nbLonBands + 1)) + lon;
2360             unsigned second = first + nbLonBands + 1;
2361
2362             unsigned off = (lat * nbLatBands + lon) * 3 * 2;
2363
2364             triangle_pos[off] = first;
2365             triangle_pos[off + 1] = first + 1;
2366             triangle_pos[off + 2] = second;
2367
2368             triangle_pos[off + 3] = second;
2369             triangle_pos[off + 4] = first + 1;
2370             triangle_pos[off + 5] = second + 1;
2371         }
2372     }
2373 }
2374
2375 static bool AllocQuadVertices(vout_display_t *vd, d3d_quad_t *quad, const video_format_t *fmt, video_projection_mode_t projection)
2376 {
2377     HRESULT hr;
2378     D3D11_MAPPED_SUBRESOURCE mappedResource;
2379     vout_display_sys_t *sys = vd->sys;
2380
2381     if (projection == PROJECTION_MODE_RECTANGULAR)
2382     {
2383         quad->vertexCount = 4;
2384         quad->indexCount = 2 * 3;
2385     }
2386     else if (projection == PROJECTION_MODE_EQUIRECTANGULAR)
2387     {
2388         quad->vertexCount = (SPHERE_SLICES+1) * (SPHERE_SLICES+1);
2389         quad->indexCount = nbLatBands * nbLonBands * 2 * 3;
2390     }
2391     else
2392     {
2393         msg_Warn(vd, "Projection mode %d not handled", projection);
2394         return false;
2395     }
2396
2397     D3D11_BUFFER_DESC bd;
2398     memset(&bd, 0, sizeof(bd));
2399     bd.Usage = D3D11_USAGE_DYNAMIC;
2400     bd.ByteWidth = sizeof(d3d_vertex_t) * quad->vertexCount;
2401     bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
2402     bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
2403
2404     hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &bd, NULL, &quad->pVertexBuffer);
2405     if(FAILED(hr)) {
2406       msg_Err(vd, "Failed to create vertex buffer. (hr=%lX)", hr);
2407       return false;
2408     }
2409
2410     /* create the index of the vertices */
2411     D3D11_BUFFER_DESC quadDesc = {
2412         .Usage = D3D11_USAGE_DYNAMIC,
2413         .ByteWidth = sizeof(WORD) * quad->indexCount,
2414         .BindFlags = D3D11_BIND_INDEX_BUFFER,
2415         .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
2416     };
2417
2418     hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &quadDesc, NULL, &quad->pIndexBuffer);
2419     if(FAILED(hr)) {
2420         msg_Err(vd, "Could not create the quad indices. (hr=0x%lX)", hr);
2421         return false;
2422     }
2423
2424     /* create the vertices */
2425     hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2426     if (FAILED(hr)) {
2427         msg_Err(vd, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
2428         return false;
2429     }
2430     d3d_vertex_t *dst_data = mappedResource.pData;
2431
2432     /* create the vertex indices */
2433     hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2434     if (FAILED(hr)) {
2435         msg_Err(vd, "Failed to lock the index buffer (hr=0x%lX)", hr);
2436         ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
2437         return false;
2438     }
2439     WORD *triangle_pos = mappedResource.pData;
2440
2441     if ( projection == PROJECTION_MODE_RECTANGULAR )
2442         SetupQuadFlat(dst_data, fmt, quad, triangle_pos);
2443     else
2444         SetupQuadSphere(dst_data, triangle_pos);
2445
2446     ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0);
2447     ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
2448
2449     return true;
2450 }
2451
2452 static int SetupQuad(vout_display_t *vd, const video_format_t *fmt, d3d_quad_t *quad,
2453                      const d3d_format_t *cfg, ID3D11PixelShader *d3dpixelShader,
2454                      video_projection_mode_t projection)
2455 {
2456     vout_display_sys_t *sys = vd->sys;
2457     HRESULT hr;
2458     const bool RGB_shader = IsRGBShader(cfg);
2459
2460     /* pixel shader constant buffer */
2461     PS_CONSTANT_BUFFER defaultConstants = {
2462       .Opacity = 1,
2463     };
2464     static_assert((sizeof(PS_CONSTANT_BUFFER)%16)==0,"Constant buffers require 16-byte alignment");
2465     D3D11_BUFFER_DESC constantDesc = {
2466         .Usage = D3D11_USAGE_DYNAMIC,
2467         .ByteWidth = sizeof(PS_CONSTANT_BUFFER),
2468         .BindFlags = D3D11_BIND_CONSTANT_BUFFER,
2469         .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
2470     };
2471     D3D11_SUBRESOURCE_DATA constantInit = { .pSysMem = &defaultConstants };
2472     hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[0]);
2473     if(FAILED(hr)) {
2474         msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2475         goto error;
2476     }
2477
2478     FLOAT itu_black_level = 0.f;
2479     FLOAT itu_achromacy   = 0.f;
2480     if (!RGB_shader)
2481     {
2482         switch (cfg->bitsPerChannel)
2483         {
2484         case 8:
2485             /* Rec. ITU-R BT.709-6 §4.6 */
2486             itu_black_level  =              16.f / 255.f;
2487             itu_achromacy    =             128.f / 255.f;
2488             break;
2489         case 10:
2490             /* Rec. ITU-R BT.709-6 §4.6 */
2491             itu_black_level  =              64.f / 1023.f;
2492             itu_achromacy    =             512.f / 1023.f;
2493             break;
2494         case 12:
2495             /* Rec. ITU-R BT.2020-2 Table 5 */
2496             itu_black_level  =               256.f / 4095.f;
2497             itu_achromacy    =              2048.f / 4095.f;
2498             break;
2499         default:
2500             /* unknown bitdepth, use approximation for infinite bit depth */
2501             itu_black_level  =              16.f / 256.f;
2502             itu_achromacy    =             128.f / 256.f;
2503             break;
2504         }
2505     }
2506
2507     static const FLOAT IDENTITY_4X4[4 * 4] = {
2508         1.f, 0.f, 0.f, 0.f,
2509         0.f, 1.f, 0.f, 0.f,
2510         0.f, 0.f, 1.f, 0.f,
2511         0.f, 0.f, 0.f, 1.f,
2512     };
2513
2514     /* matrices for studio range */
2515     /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion, in studio range */
2516     static const FLOAT COLORSPACE_BT601_TO_FULL[4*4] = {
2517         1.164383561643836f,                 0.f,  1.596026785714286f, 0.f,
2518         1.164383561643836f, -0.391762290094914f, -0.812967647237771f, 0.f,
2519         1.164383561643836f,  2.017232142857142f,                 0.f, 0.f,
2520                        0.f,                 0.f,                 0.f, 1.f,
2521     };
2522     /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, in studio range */
2523     static const FLOAT COLORSPACE_BT709_TO_FULL[4*4] = {
2524         1.164383561643836f,                 0.f,  1.792741071428571f, 0.f,
2525         1.164383561643836f, -0.213248614273730f, -0.532909328559444f, 0.f,
2526         1.164383561643836f,  2.112401785714286f,                 0.f, 0.f,
2527                        0.f,                 0.f,                 0.f, 1.f,
2528     };
2529     /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion, in studio range */
2530     static const FLOAT COLORSPACE_BT2020_TO_FULL[4*4] = {
2531         1.164383561643836f,  0.000000000000f,  1.678674107143f, 0.f,
2532         1.164383561643836f, -0.127007098661f, -0.440987687946f, 0.f,
2533         1.164383561643836f,  2.141772321429f,  0.000000000000f, 0.f,
2534                        0.f,              0.f,              0.f, 1.f,
2535     };
2536
2537     PS_COLOR_TRANSFORM colorspace;
2538
2539     memcpy(colorspace.WhitePoint, IDENTITY_4X4, sizeof(colorspace.WhitePoint));
2540
2541     const FLOAT *ppColorspace;
2542     if (RGB_shader)
2543         ppColorspace = IDENTITY_4X4;
2544     else {
2545         switch (fmt->space){
2546             case COLOR_SPACE_BT709:
2547                 ppColorspace = COLORSPACE_BT709_TO_FULL;
2548                 break;
2549             case COLOR_SPACE_BT2020:
2550                 ppColorspace = COLORSPACE_BT2020_TO_FULL;
2551                 break;
2552             case COLOR_SPACE_BT601:
2553                 ppColorspace = COLORSPACE_BT601_TO_FULL;
2554                 break;
2555             default:
2556             case COLOR_SPACE_UNDEF:
2557                 if( fmt->i_height > 576 )
2558                     ppColorspace = COLORSPACE_BT709_TO_FULL;
2559                 else
2560                     ppColorspace = COLORSPACE_BT601_TO_FULL;
2561                 break;
2562         }
2563         /* all matrices work in studio range and output in full range */
2564         colorspace.WhitePoint[0*4 + 3] = -itu_black_level;
2565         colorspace.WhitePoint[1*4 + 3] = -itu_achromacy;
2566         colorspace.WhitePoint[2*4 + 3] = -itu_achromacy;
2567     }
2568
2569     memcpy(colorspace.Colorspace, ppColorspace, sizeof(colorspace.Colorspace));
2570
2571     constantInit.pSysMem = &colorspace;
2572
2573     static_assert((sizeof(PS_COLOR_TRANSFORM)%16)==0,"Constant buffers require 16-byte alignment");
2574     constantDesc.ByteWidth = sizeof(PS_COLOR_TRANSFORM);
2575     hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[1]);
2576     if(FAILED(hr)) {
2577         msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2578         goto error;
2579     }
2580     quad->PSConstantsCount = 2;
2581
2582     /* vertex shader constant buffer */
2583     if ( projection == PROJECTION_MODE_EQUIRECTANGULAR )
2584     {
2585         constantDesc.ByteWidth = sizeof(VS_PROJECTION_CONST);
2586         static_assert((sizeof(VS_PROJECTION_CONST)%16)==0,"Constant buffers require 16-byte alignment");
2587         hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, NULL, &quad->pVertexShaderConstants);
2588         if(FAILED(hr)) {
2589             msg_Err(vd, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
2590             goto error;
2591         }
2592
2593         SetQuadVSProjection( vd, quad, &vd->cfg->viewpoint );
2594     }
2595
2596     quad->picSys.formatTexture = cfg->formatTexture;
2597     quad->picSys.context = sys->d3dcontext;
2598     ID3D11DeviceContext_AddRef(quad->picSys.context);
2599
2600     if (!AllocQuadVertices(vd, quad, fmt, projection))
2601         goto error;
2602
2603     quad->d3dpixelShader = d3dpixelShader;
2604     if (projection == PROJECTION_MODE_RECTANGULAR)
2605         quad->d3dvertexShader = sys->flatVSShader;
2606     else
2607         quad->d3dvertexShader = sys->projectionVSShader;
2608
2609     return VLC_SUCCESS;
2610
2611 error:
2612     ReleaseQuad(quad);
2613     return VLC_EGENERIC;
2614 }
2615
2616 static void ReleaseQuad(d3d_quad_t *quad)
2617 {
2618     if (quad->pPixelShaderConstants[0])
2619     {
2620         ID3D11Buffer_Release(quad->pPixelShaderConstants[0]);
2621         quad->pPixelShaderConstants[0] = NULL;
2622     }
2623     if (quad->pPixelShaderConstants[1])
2624     {
2625         ID3D11Buffer_Release(quad->pPixelShaderConstants[1]);
2626         quad->pPixelShaderConstants[1] = NULL;
2627     }
2628     if (quad->pVertexBuffer)
2629     {
2630         ID3D11Buffer_Release(quad->pVertexBuffer);
2631         quad->pVertexBuffer = NULL;
2632     }
2633     quad->d3dvertexShader = NULL;
2634     if (quad->pIndexBuffer)
2635     {
2636         ID3D11Buffer_Release(quad->pIndexBuffer);
2637         quad->pIndexBuffer = NULL;
2638     }
2639     if (quad->pVertexShaderConstants)
2640     {
2641         ID3D11Buffer_Release(quad->pVertexShaderConstants);
2642         quad->pVertexShaderConstants = NULL;
2643     }
2644     ReleasePictureResources(&quad->picSys);
2645 }
2646
2647 static void Direct3D11DestroyResources(vout_display_t *vd)
2648 {
2649     vout_display_sys_t *sys = vd->sys;
2650
2651     Direct3D11DestroyPool(vd);
2652
2653     ReleaseQuad(&sys->picQuad);
2654     Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
2655     sys->d3dregion_count = 0;
2656
2657     ReleasePictureResources(&sys->stagingSys);
2658
2659     if (sys->flatVSShader)
2660     {
2661         ID3D11VertexShader_Release(sys->flatVSShader);
2662         sys->flatVSShader = NULL;
2663     }
2664     if (sys->projectionVSShader)
2665     {
2666         ID3D11VertexShader_Release(sys->projectionVSShader);
2667         sys->projectionVSShader = NULL;
2668     }
2669     if (sys->d3drenderTargetView)
2670     {
2671         ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
2672         sys->d3drenderTargetView = NULL;
2673     }
2674     if (sys->d3ddepthStencilView)
2675     {
2676         ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
2677         sys->d3ddepthStencilView = NULL;
2678     }
2679     if (sys->pSPUPixelShader)
2680     {
2681         ID3D11PixelShader_Release(sys->pSPUPixelShader);
2682         sys->pSPUPixelShader = NULL;
2683     }
2684     if (sys->picQuadPixelShader)
2685     {
2686         ID3D11PixelShader_Release(sys->picQuadPixelShader);
2687         sys->picQuadPixelShader = NULL;
2688     }
2689 #if defined(HAVE_ID3D11VIDEODECODER)
2690     if( sys->context_lock != INVALID_HANDLE_VALUE )
2691     {
2692         CloseHandle( sys->context_lock );
2693         sys->context_lock = INVALID_HANDLE_VALUE;
2694     }
2695 #endif
2696
2697     msg_Dbg(vd, "Direct3D11 resources destroyed");
2698 }
2699
2700 static void Direct3D11DeleteRegions(int count, picture_t **region)
2701 {
2702     for (int i = 0; i < count; ++i) {
2703         if (region[i]) {
2704             picture_Release(region[i]);
2705         }
2706     }
2707     free(region);
2708 }
2709
2710 static void DestroyPictureQuad(picture_t *p_picture)
2711 {
2712     ReleaseQuad( (d3d_quad_t *) p_picture->p_sys );
2713     free( p_picture );
2714 }
2715
2716 static void UpdateQuadOpacity(vout_display_t *vd, const d3d_quad_t *quad, float opacity)
2717 {
2718     vout_display_sys_t *sys = vd->sys;
2719     D3D11_MAPPED_SUBRESOURCE mappedResource;
2720
2721     HRESULT hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2722     if (SUCCEEDED(hr)) {
2723         FLOAT *dst_data = mappedResource.pData;
2724         *dst_data = opacity;
2725         ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0);
2726     }
2727     else {
2728         msg_Err(vd, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr);
2729     }
2730 }
2731
2732 static int Direct3D11MapSubpicture(vout_display_t *vd, int *subpicture_region_count,
2733                                    picture_t ***region, subpicture_t *subpicture)
2734 {
2735     vout_display_sys_t *sys = vd->sys;
2736     D3D11_MAPPED_SUBRESOURCE mappedResource;
2737     D3D11_TEXTURE2D_DESC texDesc;
2738     HRESULT hr;
2739     int err;
2740
2741     if (sys->d3dregion_format == NULL)
2742         return VLC_EGENERIC;
2743
2744     int count = 0;
2745     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
2746         count++;
2747
2748     *region = calloc(count, sizeof(picture_t *));
2749     if (unlikely(*region==NULL))
2750         return VLC_ENOMEM;
2751     *subpicture_region_count = count;
2752
2753     int i = 0;
2754     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
2755         if (!r->fmt.i_width || !r->fmt.i_height)
2756             continue; // won't render anything, keep the cache for later
2757
2758         for (int j = 0; j < sys->d3dregion_count; j++) {
2759             picture_t *cache = sys->d3dregions[j];
2760             if (cache != NULL && ((d3d_quad_t *) cache->p_sys)->picSys.texture[KNOWN_DXGI_INDEX]) {
2761                 ID3D11Texture2D_GetDesc( ((d3d_quad_t *) cache->p_sys)->picSys.texture[KNOWN_DXGI_INDEX], &texDesc );
2762                 if (texDesc.Format == sys->d3dregion_format->formatTexture &&
2763                     texDesc.Width  == r->fmt.i_visible_width &&
2764                     texDesc.Height == r->fmt.i_visible_height) {
2765                     (*region)[i] = cache;
2766                     memset(&sys->d3dregions[j], 0, sizeof(cache)); // do not reuse this cached value
2767                     break;
2768                 }
2769             }
2770         }
2771
2772         picture_t *quad_picture = (*region)[i];
2773         if (quad_picture == NULL) {
2774             ID3D11Texture2D *textures[D3D11_MAX_SHADER_VIEW] = {0};
2775             d3d_quad_t *d3dquad = calloc(1, sizeof(*d3dquad));
2776             if (unlikely(d3dquad==NULL)) {
2777                 continue;
2778             }
2779             if (AllocateTextures(vd, sys->d3dregion_format, &r->fmt, 1, textures)) {
2780                 msg_Err(vd, "Failed to allocate %dx%d texture for OSD",
2781                         r->fmt.i_visible_width, r->fmt.i_visible_height);
2782                 for (int i=0; i<D3D11_MAX_SHADER_VIEW; i++)
2783                     if (textures[i])
2784                         ID3D11Texture2D_Release(textures[i]);
2785                 free(d3dquad);
2786                 continue;
2787             }
2788
2789             for (unsigned plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++) {
2790                 d3dquad->picSys.texture[plane] = textures[plane];
2791             }
2792             if (AllocateShaderView(vd, sys->d3dregion_format, 0, &d3dquad->picSys)) {
2793                 msg_Err(vd, "Failed to create %dx%d shader view for OSD",
2794                         r->fmt.i_visible_width, r->fmt.i_visible_height);
2795                 free(d3dquad);
2796                 continue;
2797             }
2798             d3dquad->i_x_offset = r->fmt.i_x_offset;
2799             d3dquad->i_y_offset = r->fmt.i_y_offset;
2800             d3dquad->i_width    = r->fmt.i_width;
2801             d3dquad->i_height   = r->fmt.i_height;
2802             err = SetupQuad( vd, &r->fmt, d3dquad, sys->d3dregion_format, sys->pSPUPixelShader,
2803                              PROJECTION_MODE_RECTANGULAR );
2804             if (err != VLC_SUCCESS) {
2805                 msg_Err(vd, "Failed to create %dx%d quad for OSD",
2806                         r->fmt.i_visible_width, r->fmt.i_visible_height);
2807                 free(d3dquad);
2808                 continue;
2809             }
2810             picture_resource_t picres = {
2811                 .p_sys      = (picture_sys_t *) d3dquad,
2812                 .pf_destroy = DestroyPictureQuad,
2813             };
2814             (*region)[i] = picture_NewFromResource(&r->fmt, &picres);
2815             if ((*region)[i] == NULL) {
2816                 msg_Err(vd, "Failed to create %dx%d picture for OSD",
2817                         r->fmt.i_width, r->fmt.i_height);
2818                 ReleaseQuad(d3dquad);
2819                 continue;
2820             }
2821             quad_picture = (*region)[i];
2822         }
2823
2824         hr = ID3D11DeviceContext_Map(sys->d3dcontext, ((d3d_quad_t *) quad_picture->p_sys)->picSys.resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2825         if( SUCCEEDED(hr) ) {
2826             err = CommonUpdatePicture(quad_picture, NULL, mappedResource.pData, mappedResource.RowPitch);
2827             if (err != VLC_SUCCESS) {
2828                 msg_Err(vd, "Failed to set the buffer on the SPU picture" );
2829                 picture_Release(quad_picture);
2830                 continue;
2831             }
2832
2833             picture_CopyPixels(quad_picture, r->p_picture);
2834
2835             ID3D11DeviceContext_Unmap(sys->d3dcontext, ((d3d_quad_t *) quad_picture->p_sys)->picSys.resource[KNOWN_DXGI_INDEX], 0);
2836         } else {
2837             msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
2838             picture_Release(quad_picture);
2839             continue;
2840         }
2841
2842         d3d_quad_t *quad = (d3d_quad_t *) quad_picture->p_sys;
2843
2844         quad->cropViewport.Width =  (FLOAT) r->fmt.i_visible_width  * RECTWidth(sys->sys.rect_dest)  / subpicture->i_original_picture_width;
2845         quad->cropViewport.Height = (FLOAT) r->fmt.i_visible_height * RECTHeight(sys->sys.rect_dest) / subpicture->i_original_picture_height;
2846         quad->cropViewport.MinDepth = 0.0f;
2847         quad->cropViewport.MaxDepth = 1.0f;
2848         quad->cropViewport.TopLeftX = sys->sys.rect_dest.left + (FLOAT) r->i_x * RECTWidth(sys->sys.rect_dest) / subpicture->i_original_picture_width;
2849         quad->cropViewport.TopLeftY = sys->sys.rect_dest.top  + (FLOAT) r->i_y * RECTHeight(sys->sys.rect_dest) / subpicture->i_original_picture_height;
2850
2851         UpdateQuadOpacity(vd, quad, r->i_alpha / 255.0f );
2852     }
2853     return VLC_SUCCESS;
2854 }
2855