direct3d11: only do the SwapChain Present() during Display
[vlc.git] / modules / video_output / win32 / direct3d11.c
blob61535c5f07302a966024cf67c23c5c93cacbc529
1 /*****************************************************************************
2 * direct3d11.c: Windows Direct3D11 video output module
3 *****************************************************************************
4 * Copyright (C) 2014-2015 VLC authors and VideoLAN
6 * Authors: Martell Malone <martellmalone@gmail.com>
7 * Steve Lhomme <robux4@gmail.com>
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.
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.
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 *****************************************************************************/
24 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_WIN7
25 # undef _WIN32_WINNT
26 # define _WIN32_WINNT _WIN32_WINNT_WIN7
27 #endif
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
37 #include <assert.h>
38 #include <math.h>
40 #define COBJMACROS
41 #include <initguid.h>
42 #include <d3d11.h>
43 #include <dxgi1_5.h>
44 #include <d3dcompiler.h>
46 /* avoided until we can pass ISwapchainPanel without c++/cx mode
47 # include <windows.ui.xaml.media.dxinterop.h> */
49 #include "../../video_chroma/d3d11_fmt.h"
51 #include "common.h"
53 #if !VLC_WINSTORE_APP
54 # define D3D11CreateDevice(args...) sys->OurD3D11CreateDevice(args)
55 # define D3DCompile(args...) sys->OurD3DCompile(args)
56 #endif
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);
61 static int Open(vlc_object_t *);
62 static void Close(vlc_object_t *);
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.")
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)
76 add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
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
83 set_capability("vout display", 300)
84 add_shortcut("direct3d11")
85 set_callbacks(Open, Close)
86 vlc_module_end ()
88 /* A Quad is texture that can be displayed in a rectangle */
89 typedef struct
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 const vlc_chroma_description_t *p_chroma_sampling;
103 unsigned int i_width;
104 unsigned int i_height;
105 } d3d_quad_t;
107 typedef enum video_color_axis {
108 COLOR_AXIS_RGB,
109 COLOR_AXIS_YCBCR,
110 } video_color_axis;
112 typedef struct {
113 DXGI_COLOR_SPACE_TYPE dxgi;
114 const char *name;
115 video_color_axis axis;
116 video_color_primaries_t primaries;
117 video_transfer_func_t transfer;
118 video_color_space_t color;
119 bool b_full_range;
120 } dxgi_color_space;
122 struct vout_display_sys_t
124 vout_display_sys_win32_t sys;
126 struct { /* TODO may go in vout_display_info_t without DXGI */
127 const dxgi_color_space *colorspace;
128 unsigned luminance_peak;
129 } display;
131 #if !VLC_WINSTORE_APP
132 HINSTANCE hdxgi_dll; /* handle of the opened dxgi dll */
133 HINSTANCE hd3d11_dll; /* handle of the opened d3d11 dll */
134 HINSTANCE hd3dcompiler_dll; /* handle of the opened d3dcompiler dll */
135 /* We should find a better way to store this or atleast a shorter name */
136 PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN OurD3D11CreateDeviceAndSwapChain;
137 PFN_D3D11_CREATE_DEVICE OurD3D11CreateDevice;
138 pD3DCompile OurD3DCompile;
139 #endif
140 #if defined(HAVE_ID3D11VIDEODECODER)
141 HANDLE context_lock; /* D3D11 Context lock necessary
142 for hw decoding */
143 #endif
144 IDXGISwapChain1 *dxgiswapChain; /* DXGI 1.2 swap chain */
145 IDXGISwapChain4 *dxgiswapChain4; /* DXGI 1.5 for HDR */
146 ID3D11Device *d3ddevice; /* D3D device */
147 ID3D11DeviceContext *d3dcontext; /* D3D context */
148 d3d_quad_t picQuad;
149 const d3d_format_t *picQuadConfig;
150 ID3D11PixelShader *picQuadPixelShader;
152 picture_sys_t stagingSys;
154 ID3D11RenderTargetView *d3drenderTargetView;
155 ID3D11DepthStencilView *d3ddepthStencilView;
157 ID3D11VertexShader *flatVSShader;
158 ID3D11VertexShader *projectionVSShader;
160 /* copy from the decoder pool into picSquad before display
161 * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
162 bool legacy_shader;
164 // SPU
165 vlc_fourcc_t pSubpictureChromas[2];
166 ID3D11PixelShader *pSPUPixelShader;
167 const d3d_format_t *d3dregion_format;
168 int d3dregion_count;
169 picture_t **d3dregions;
172 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
173 typedef struct d3d_vertex_t {
174 struct {
175 FLOAT x;
176 FLOAT y;
177 FLOAT z;
178 } position;
179 struct {
180 FLOAT x;
181 FLOAT y;
182 } texture;
183 } d3d_vertex_t;
185 typedef struct {
186 FLOAT Opacity;
187 FLOAT opacityPadding[3];
188 } PS_CONSTANT_BUFFER;
190 typedef struct {
191 FLOAT WhitePoint[4*4];
192 FLOAT Colorspace[4*4];
193 } PS_COLOR_TRANSFORM;
195 typedef struct {
196 FLOAT RotX[4*4];
197 FLOAT RotY[4*4];
198 FLOAT RotZ[4*4];
199 FLOAT View[4*4];
200 FLOAT Projection[4*4];
201 } VS_PROJECTION_CONST;
203 #define SPHERE_RADIUS 1.f
205 #define RECTWidth(r) (int)((r).right - (r).left)
206 #define RECTHeight(r) (int)((r).bottom - (r).top)
208 static picture_pool_t *Pool(vout_display_t *vd, unsigned count);
210 static void Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
211 static void Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
213 static HINSTANCE Direct3D11LoadShaderLibrary(void);
214 static void Direct3D11Destroy(vout_display_t *);
216 static int Direct3D11Open (vout_display_t *, video_format_t *);
217 static void Direct3D11Close(vout_display_t *);
219 static int Direct3D11CreateResources (vout_display_t *, video_format_t *);
220 static void Direct3D11DestroyResources(vout_display_t *);
222 static void Direct3D11DestroyPool(vout_display_t *);
224 static void DestroyDisplayPoolPicture(picture_t *);
225 static void Direct3D11DeleteRegions(int, picture_t **);
226 static int Direct3D11MapSubpicture(vout_display_t *, int *, picture_t ***, subpicture_t *);
228 static int SetupQuad(vout_display_t *, const video_format_t *, d3d_quad_t *, const RECT *,
229 const d3d_format_t *, ID3D11PixelShader *, video_projection_mode_t,
230 video_orientation_t);
231 static bool UpdateQuadPosition( vout_display_t *vd, d3d_quad_t *quad,
232 const RECT *output,
233 video_projection_mode_t projection,
234 video_orientation_t orientation );
235 static void ReleaseQuad(d3d_quad_t *);
236 static void UpdatePicQuadPosition(vout_display_t *);
238 static int Control(vout_display_t *vd, int query, va_list args);
239 static void Manage(vout_display_t *vd);
241 /* TODO: Move to a direct3d11_shaders header */
242 static const char* globVertexShaderFlat = "\
243 struct VS_INPUT\
245 float4 Position : POSITION;\
246 float4 Texture : TEXCOORD0;\
249 struct VS_OUTPUT\
251 float4 Position : SV_POSITION;\
252 float4 Texture : TEXCOORD0;\
255 VS_OUTPUT main( VS_INPUT In )\
257 return In;\
261 #define STRINGIZE2(s) #s
262 #define STRINGIZE(s) STRINGIZE2(s)
264 static const char* globVertexShaderProjection = "\
265 cbuffer VS_PROJECTION_CONST : register(b0)\
267 float4x4 RotX;\
268 float4x4 RotY;\
269 float4x4 RotZ;\
270 float4x4 View;\
271 float4x4 Projection;\
273 struct VS_INPUT\
275 float4 Position : POSITION;\
276 float4 Texture : TEXCOORD0;\
279 struct VS_OUTPUT\
281 float4 Position : SV_POSITION;\
282 float4 Texture : TEXCOORD0;\
285 VS_OUTPUT main( VS_INPUT In )\
287 VS_OUTPUT Output;\
288 float4 pos = In.Position;\
289 pos = mul(RotY, pos);\
290 pos = mul(RotX, pos);\
291 pos = mul(RotZ, pos);\
292 pos = mul(View, pos);\
293 pos = mul(Projection, pos);\
294 Output.Position = pos;\
295 Output.Texture = In.Texture;\
296 return Output;\
300 static const char* globPixelShaderDefault = "\
301 cbuffer PS_CONSTANT_BUFFER : register(b0)\
303 float Opacity;\
304 float opacityPadding[3];\
306 cbuffer PS_COLOR_TRANSFORM : register(b1)\
308 float4x4 WhitePoint;\
309 float4x4 Colorspace;\
311 Texture2D%s shaderTexture[" STRINGIZE(D3D11_MAX_SHADER_VIEW) "];\
312 SamplerState SampleType;\
314 struct PS_INPUT\
316 float4 Position : SV_POSITION;\
317 float4 Texture : TEXCOORD0;\
320 /* see http://filmicworlds.com/blog/filmic-tonemapping-operators/ */\
321 inline float hable(float x) {\
322 const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;\
323 return ((x * (A*x + (C*B))+(D*E))/(x * (A*x + B) + (D*F))) - E/F;\
326 inline float3 hable(float3 x) {\
327 x.r = hable(x.r);\
328 x.g = hable(x.g);\
329 x.b = hable(x.b);\
330 return x;\
333 /* https://en.wikipedia.org/wiki/Hybrid_Log-Gamma#Technical_details */\
334 inline float inverse_HLG(float x){\
335 const float B67_a = 0.17883277;\
336 const float B67_b = 0.28466892;\
337 const float B67_c = 0.55991073;\
338 const float B67_inv_r2 = 4.0; /* 1/0.5² */\
339 if (x <= 0.5)\
340 x = x * x * B67_inv_r2;\
341 else\
342 x = exp((x - B67_c) / B67_a) + B67_b;\
343 return x;\
346 inline float3 sourceToLinear(float3 rgb) {\
347 %s;\
350 inline float3 linearToDisplay(float3 rgb) {\
351 %s;\
354 inline float3 toneMapping(float3 rgb) {\
355 %s;\
358 inline float3 adjustPeakLuminance(float3 rgb) {\
359 %s;\
362 inline float3 adjustRange(float3 rgb) {\
363 %s;\
366 float4 main( PS_INPUT In ) : SV_TARGET\
368 float4 sample;\
370 %s /* sampling routine in sample */\
371 float4 rgba = mul(mul(sample, WhitePoint), Colorspace);\
372 float opacity = rgba.a * Opacity;\
373 float3 rgb = (float3)rgba;\
374 rgb = sourceToLinear(rgb);\
375 rgb = toneMapping(rgb);\
376 rgb = adjustPeakLuminance(rgb);\
377 rgb = linearToDisplay(rgb);\
378 rgb = adjustRange(rgb);\
379 return float4(rgb, saturate(opacity));\
383 #define ST2084_PQ_CONSTANTS "const float ST2084_m1 = 2610.0 / (4096.0 * 4);\
384 const float ST2084_m2 = (2523.0 / 4096.0) * 128.0;\
385 const float ST2084_c1 = 3424.0 / 4096.0;\
386 const float ST2084_c2 = (2413.0 / 4096.0) * 32.0;\
387 const float ST2084_c3 = (2392.0 / 4096.0) * 32.0;"
389 #define DEFAULT_BRIGHTNESS 80
392 static int Direct3D11MapPoolTexture(picture_t *picture)
394 picture_sys_t *p_sys = picture->p_sys;
395 D3D11_MAPPED_SUBRESOURCE mappedResource;
396 HRESULT hr;
397 hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
398 if( FAILED(hr) )
400 return VLC_EGENERIC;
402 return CommonUpdatePicture(picture, NULL, mappedResource.pData, mappedResource.RowPitch);
405 static void Direct3D11UnmapPoolTexture(picture_t *picture)
407 picture_sys_t *p_sys = picture->p_sys;
408 ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0);
411 static int Direct3D11LockDirectTexture(picture_t *picture)
413 picture_sys_t *p_sys = picture->p_sys;
414 D3D11_MAPPED_SUBRESOURCE mappedResource;
415 HRESULT hr;
416 D3D11_TEXTURE2D_DESC texDesc;
417 int i;
419 for (i = 0; i < picture->i_planes; i++) {
420 hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[i], 0, D3D11_MAP_WRITE, 0, &mappedResource);
421 if( FAILED(hr) )
422 break;
423 ID3D11Texture2D_GetDesc(p_sys->texture[i], &texDesc);
424 picture->p[i].p_pixels = mappedResource.pData;
425 picture->p[i].i_pitch = mappedResource.RowPitch;
426 picture->p[i].i_lines = texDesc.Height;
427 assert(picture->p[i].i_visible_pitch <= picture->p[i].i_pitch);
428 assert(picture->p[i].i_visible_lines <= picture->p[i].i_lines);
431 if( FAILED(hr) )
433 while (i-- > 0)
434 ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[i+1], 0);
435 return VLC_EGENERIC;
438 p_sys->mapped = true;
439 return VLC_SUCCESS;
442 static void Direct3D11UnlockDirectTexture(picture_t *picture)
444 picture_sys_t *p_sys = ActivePictureSys(picture);
445 if (p_sys->mapped) {
446 for (int i = 0; i < picture->i_planes; i++)
447 ID3D11DeviceContext_Unmap(p_sys->context, p_sys->resource[i], 0);
448 p_sys->mapped = false;
452 #if !VLC_WINSTORE_APP
453 static int OpenHwnd(vout_display_t *vd)
455 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
456 if (!sys)
457 return VLC_ENOMEM;
459 sys->hd3d11_dll = LoadLibrary(TEXT("D3D11.DLL"));
460 if (!sys->hd3d11_dll) {
461 msg_Warn(vd, "cannot load d3d11.dll, aborting");
462 return VLC_EGENERIC;
465 sys->hd3dcompiler_dll = Direct3D11LoadShaderLibrary();
466 if (!sys->hd3dcompiler_dll) {
467 msg_Err(vd, "cannot load d3dcompiler.dll, aborting");
468 Direct3D11Destroy(vd);
469 return VLC_EGENERIC;
472 sys->OurD3DCompile = (void *)GetProcAddress(sys->hd3dcompiler_dll, "D3DCompile");
473 if (!sys->OurD3DCompile) {
474 msg_Err(vd, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
475 Direct3D11Destroy(vd);
476 return VLC_EGENERIC;
479 sys->OurD3D11CreateDevice =
480 (void *)GetProcAddress(sys->hd3d11_dll, "D3D11CreateDevice");
481 if (!sys->OurD3D11CreateDevice) {
482 msg_Err(vd, "Cannot locate reference to D3D11CreateDevice in d3d11 DLL");
483 Direct3D11Destroy(vd);
484 return VLC_EGENERIC;
486 return VLC_SUCCESS;
488 #else
489 static int OpenCoreW(vout_display_t *vd)
491 IDXGISwapChain1* dxgiswapChain = var_InheritInteger(vd, "winrt-swapchain");
492 if (!dxgiswapChain)
493 return VLC_EGENERIC;
494 ID3D11DeviceContext* d3dcontext = var_InheritInteger(vd, "winrt-d3dcontext");
495 if (!d3dcontext)
496 return VLC_EGENERIC;
497 ID3D11Device* d3ddevice = NULL;
498 ID3D11DeviceContext_GetDevice(d3dcontext, &d3ddevice);
499 if (!d3ddevice)
500 return VLC_EGENERIC;
502 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
503 if (!sys)
504 return VLC_ENOMEM;
506 sys->dxgiswapChain = dxgiswapChain;
507 sys->d3ddevice = d3ddevice;
508 sys->d3dcontext = d3dcontext;
509 IDXGISwapChain_AddRef (sys->dxgiswapChain);
510 ID3D11Device_AddRef (sys->d3ddevice);
511 ID3D11DeviceContext_AddRef(sys->d3dcontext);
513 return VLC_SUCCESS;
515 #endif
517 static bool is_d3d11_opaque(vlc_fourcc_t chroma)
519 switch (chroma)
521 case VLC_CODEC_D3D11_OPAQUE:
522 case VLC_CODEC_D3D11_OPAQUE_10B:
523 return true;
524 default:
525 return false;
529 #if VLC_WINSTORE_APP
530 static bool GetRect(const vout_display_sys_win32_t *p_sys, RECT *out)
532 const vout_display_sys_t *sys = (const vout_display_sys_t *)p_sys;
533 out->left = 0;
534 out->top = 0;
535 uint32_t i_width;
536 uint32_t i_height;
537 UINT dataSize = sizeof(i_width);
538 HRESULT hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
539 if (FAILED(hr)) {
540 return false;
542 dataSize = sizeof(i_height);
543 hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
544 if (FAILED(hr)) {
545 return false;
547 out->right = i_width;
548 out->bottom = i_height;
549 return true;
551 #endif
553 static int Open(vlc_object_t *object)
555 vout_display_t *vd = (vout_display_t *)object;
557 #if !VLC_WINSTORE_APP
558 int ret = OpenHwnd(vd);
559 #else
560 int ret = OpenCoreW(vd);
561 #endif
563 if (ret != VLC_SUCCESS)
564 return ret;
566 if (CommonInit(vd))
567 goto error;
569 #if VLC_WINSTORE_APP
570 vd->sys->sys.pf_GetRect = GetRect;
571 #endif
573 video_format_t fmt;
574 video_format_Copy(&fmt, &vd->source);
575 if (Direct3D11Open(vd, &fmt)) {
576 msg_Err(vd, "Direct3D11 could not be opened");
577 goto error;
580 video_format_Clean(&vd->fmt);
581 vd->fmt = fmt;
583 vd->info.has_double_click = true;
584 vd->info.has_pictures_invalid = vd->info.is_slow;
586 if (var_InheritBool(vd, "direct3d11-hw-blending") &&
587 vd->sys->d3dregion_format != NULL)
589 vd->sys->pSubpictureChromas[0] = vd->sys->d3dregion_format->fourcc;
590 vd->sys->pSubpictureChromas[1] = 0;
591 vd->info.subpicture_chromas = vd->sys->pSubpictureChromas;
593 else
594 vd->info.subpicture_chromas = NULL;
596 vd->pool = Pool;
597 vd->prepare = Prepare;
598 vd->display = Display;
599 vd->control = Control;
600 vd->manage = Manage;
602 msg_Dbg(vd, "Direct3D11 Open Succeeded");
604 return VLC_SUCCESS;
606 error:
607 Close(object);
608 return VLC_EGENERIC;
611 static void Close(vlc_object_t *object)
613 vout_display_t * vd = (vout_display_t *)object;
615 Direct3D11Close(vd);
616 CommonClean(vd);
617 Direct3D11Destroy(vd);
618 free(vd->sys);
621 static int AllocateTextures(vout_display_t *vd, const d3d_format_t *cfg,
622 video_format_t *fmt, unsigned pool_size,
623 ID3D11Texture2D *textures[],
624 bool pool_type_display)
626 vout_display_sys_t *sys = vd->sys;
627 plane_t planes[PICTURE_PLANE_MAX];
628 int plane, plane_count;
629 HRESULT hr;
630 ID3D11Texture2D *slicedTexture = NULL;
631 D3D11_TEXTURE2D_DESC texDesc;
632 ZeroMemory(&texDesc, sizeof(texDesc));
633 texDesc.MipLevels = 1;
634 texDesc.SampleDesc.Count = 1;
635 texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
636 const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
637 if( !p_chroma_desc )
638 return VLC_EGENERIC;
640 if (cfg->formatTexture == DXGI_FORMAT_UNKNOWN) {
641 int i_width_aligned = fmt->i_width;
642 int i_height_aligned = fmt->i_height;
643 if (p_chroma_desc->plane_count == 0)
645 msg_Dbg(vd, "failed to get the pixel format planes for %4.4s", (char *)&fmt->i_chroma);
646 return VLC_EGENERIC;
648 assert(p_chroma_desc->plane_count <= D3D11_MAX_SHADER_VIEW);
649 plane_count = p_chroma_desc->plane_count;
651 texDesc.Format = cfg->resourceFormat[0];
652 assert(cfg->resourceFormat[1] == cfg->resourceFormat[0]);
653 assert(cfg->resourceFormat[2] == cfg->resourceFormat[0]);
655 if (pool_type_display) {
656 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
657 texDesc.Usage = D3D11_USAGE_DEFAULT;
658 } else {
659 texDesc.Usage = D3D11_USAGE_STAGING;
660 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
662 /* align on 16 pixel boundaries */
663 i_width_aligned = ( i_width_aligned + 15 ) & ~15;
664 i_height_aligned = ( i_height_aligned + 15 ) & ~15;
667 for( int i = 0; i < plane_count; i++ )
669 plane_t *p = &planes[i];
671 p->i_lines = i_height_aligned * p_chroma_desc->p[i].h.num / p_chroma_desc->p[i].h.den;
672 p->i_visible_lines = fmt->i_visible_height * p_chroma_desc->p[i].h.num / p_chroma_desc->p[i].h.den;
673 p->i_pitch = i_width_aligned * p_chroma_desc->p[i].w.num / p_chroma_desc->p[i].w.den * p_chroma_desc->pixel_size;
674 p->i_visible_pitch = fmt->i_visible_width * p_chroma_desc->p[i].w.num / p_chroma_desc->p[i].w.den * p_chroma_desc->pixel_size;
675 p->i_pixel_pitch = p_chroma_desc->pixel_size;
678 if (!pool_type_display) {
679 assert( (planes[0].i_pitch % 16) == 0 );
682 texDesc.ArraySize = 1;
683 } else {
684 plane_count = 1;
685 texDesc.Format = cfg->formatTexture;
686 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
687 if (is_d3d11_opaque(fmt->i_chroma)) {
688 texDesc.BindFlags |= D3D11_BIND_DECODER;
689 texDesc.Usage = D3D11_USAGE_DEFAULT;
690 texDesc.CPUAccessFlags = 0;
691 } else {
692 texDesc.Usage = D3D11_USAGE_DYNAMIC;
693 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
695 texDesc.ArraySize = pool_size;
696 texDesc.Height = fmt->i_height;
697 texDesc.Width = fmt->i_width;
699 hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &slicedTexture );
700 if (FAILED(hr)) {
701 msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
702 goto error;
706 for (unsigned picture_count = 0; picture_count < pool_size; picture_count++) {
707 for (plane = 0; plane < plane_count; plane++)
709 if (slicedTexture) {
710 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = slicedTexture;
711 ID3D11Texture2D_AddRef(slicedTexture);
712 } else {
713 texDesc.Height = planes[plane].i_lines;
714 texDesc.Width = planes[plane].i_pitch;
715 hr = ID3D11Device_CreateTexture2D( sys->d3ddevice, &texDesc, NULL, &textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] );
716 if (FAILED(hr)) {
717 msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
718 goto error;
722 for (; plane < D3D11_MAX_SHADER_VIEW; plane++) {
723 if (!cfg->resourceFormat[plane])
724 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = NULL;
725 else
727 textures[picture_count * D3D11_MAX_SHADER_VIEW + plane] = textures[picture_count * D3D11_MAX_SHADER_VIEW];
728 ID3D11Texture2D_AddRef(textures[picture_count * D3D11_MAX_SHADER_VIEW + plane]);
733 if (!is_d3d11_opaque(fmt->i_chroma) && cfg->formatTexture != DXGI_FORMAT_UNKNOWN) {
734 D3D11_MAPPED_SUBRESOURCE mappedResource;
735 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource*)textures[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
736 if( FAILED(hr) ) {
737 msg_Err(vd, "The texture cannot be mapped. (hr=0x%lX)", hr);
738 goto error;
740 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource*)textures[0], 0);
741 if (mappedResource.RowPitch < p_chroma_desc->pixel_size * texDesc.Width) {
742 msg_Err( vd, "The texture row pitch is too small (%d instead of %d)", mappedResource.RowPitch,
743 p_chroma_desc->pixel_size * texDesc.Width );
744 goto error;
748 if (slicedTexture)
749 ID3D11Texture2D_Release(slicedTexture);
750 return VLC_SUCCESS;
751 error:
752 if (slicedTexture)
753 ID3D11Texture2D_Release(slicedTexture);
754 return VLC_EGENERIC;
757 static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
759 /* compensate for extra hardware decoding pulling extra pictures from our pool */
760 pool_size += 2;
762 vout_display_sys_t *sys = vd->sys;
763 ID3D11Texture2D *textures[pool_size * D3D11_MAX_SHADER_VIEW];
764 picture_t **pictures = NULL;
765 picture_t *picture;
766 unsigned plane;
767 unsigned picture_count = 0;
768 picture_pool_configuration_t pool_cfg = {};
770 if (sys->sys.pool)
771 return sys->sys.pool;
773 if (vd->info.is_slow)
774 pool_size = 1;
776 video_format_t surface_fmt = vd->fmt;
777 surface_fmt.i_width = sys->picQuad.i_width;
778 surface_fmt.i_height = sys->picQuad.i_height;
780 if (AllocateTextures(vd, sys->picQuadConfig, &surface_fmt, pool_size, textures,
781 sys->picQuadConfig->formatTexture != DXGI_FORMAT_UNKNOWN))
782 goto error;
784 if (!vd->info.is_slow) {
785 HRESULT hr;
786 ID3D10Multithread *pMultithread;
787 hr = ID3D11Device_QueryInterface( sys->d3ddevice, &IID_ID3D10Multithread, (void **)&pMultithread);
788 if (SUCCEEDED(hr)) {
789 ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
790 ID3D10Multithread_Release(pMultithread);
794 pictures = calloc(pool_size, sizeof(*pictures));
795 if (!pictures)
796 goto error;
798 for (picture_count = 0; picture_count < pool_size; picture_count++) {
799 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
800 if (unlikely(picsys == NULL))
801 goto error;
803 for (plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
804 picsys->texture[plane] = textures[picture_count * D3D11_MAX_SHADER_VIEW + plane];
806 picsys->slice_index = picture_count;
807 picsys->formatTexture = sys->picQuadConfig->formatTexture;
808 picsys->context = sys->d3dcontext;
810 picture_resource_t resource = {
811 .p_sys = picsys,
812 .pf_destroy = DestroyDisplayPoolPicture,
815 picture = picture_NewFromResource(&surface_fmt, &resource);
816 if (unlikely(picture == NULL)) {
817 free(picsys);
818 msg_Err( vd, "Failed to create picture %d in the pool.", picture_count );
819 goto error;
822 pictures[picture_count] = picture;
823 /* each picture_t holds a ref to the context and release it on Destroy */
824 ID3D11DeviceContext_AddRef(picsys->context);
827 #ifdef HAVE_ID3D11VIDEODECODER
828 if (!is_d3d11_opaque(surface_fmt.i_chroma) || sys->legacy_shader)
830 /* we need a staging texture */
831 if (AllocateTextures(vd, sys->picQuadConfig, &surface_fmt, 1, textures, true))
832 goto error;
834 sys->picQuad.p_chroma_sampling = vlc_fourcc_GetChromaDescription( surface_fmt.i_chroma );
836 for (unsigned plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
837 sys->stagingSys.texture[plane] = textures[plane];
839 if (AllocateShaderView(VLC_OBJECT(vd), sys->d3ddevice, sys->picQuadConfig,
840 textures, 0, sys->stagingSys.resourceView))
841 goto error;
842 } else
843 #endif
845 for (picture_count = 0; picture_count < pool_size; picture_count++) {
846 if (AllocateShaderView(VLC_OBJECT(vd), sys->d3ddevice, sys->picQuadConfig,
847 pictures[picture_count]->p_sys->texture, picture_count,
848 pictures[picture_count]->p_sys->resourceView))
849 goto error;
853 if (SetupQuad( vd, &surface_fmt, &sys->picQuad, &sys->sys.rect_src_clipped,
854 sys->picQuadConfig, sys->picQuadPixelShader,
855 surface_fmt.projection_mode, vd->fmt.orientation ) != VLC_SUCCESS) {
856 msg_Err(vd, "Could not Create the main quad picture.");
857 return NULL;
860 if (sys->picQuadConfig->formatTexture == DXGI_FORMAT_UNKNOWN)
862 pool_cfg.lock = Direct3D11LockDirectTexture;
863 pool_cfg.unlock = Direct3D11UnlockDirectTexture;
865 else if (vd->info.is_slow) {
866 pool_cfg.lock = Direct3D11MapPoolTexture;
867 //pool_cfg.unlock = Direct3D11UnmapPoolTexture;
869 pool_cfg.picture = pictures;
870 pool_cfg.picture_count = pool_size;
871 sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
873 error:
874 if (sys->sys.pool == NULL) {
875 if (pictures) {
876 msg_Dbg(vd, "Failed to create the picture d3d11 pool");
877 for (unsigned i=0;i<picture_count; ++i)
878 picture_Release(pictures[i]);
879 free(pictures);
882 /* create an empty pool to avoid crashing */
883 pool_cfg.picture_count = 0;
884 sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
885 } else {
886 msg_Dbg(vd, "D3D11 pool succeed with %d surfaces (%dx%d) context 0x%p",
887 picture_count, surface_fmt.i_width, surface_fmt.i_height, sys->d3dcontext);
889 return sys->sys.pool;
892 static void DestroyDisplayPoolPicture(picture_t *picture)
894 picture_sys_t *p_sys = picture->p_sys;
895 ReleasePictureSys( p_sys );
896 free(p_sys);
897 free(picture);
900 static HRESULT UpdateBackBuffer(vout_display_t *vd)
902 vout_display_sys_t *sys = vd->sys;
903 HRESULT hr;
904 ID3D11Texture2D* pDepthStencil;
905 ID3D11Texture2D* pBackBuffer;
906 RECT rect;
907 #if VLC_WINSTORE_APP
908 if (!GetRect(&sys->sys, &rect))
909 #endif
910 rect = sys->sys.rect_dest_clipped;
911 uint32_t i_width = RECTWidth(rect);
912 uint32_t i_height = RECTHeight(rect);
914 if (sys->d3drenderTargetView) {
915 ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
916 sys->d3drenderTargetView = NULL;
918 if (sys->d3ddepthStencilView) {
919 ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
920 sys->d3ddepthStencilView = NULL;
923 /* TODO detect is the size is the same as the output and switch to fullscreen mode */
924 hr = IDXGISwapChain_ResizeBuffers(sys->dxgiswapChain, 0, i_width, i_height,
925 DXGI_FORMAT_UNKNOWN, 0);
926 if (FAILED(hr)) {
927 msg_Err(vd, "Failed to resize the backbuffer. (hr=0x%lX)", hr);
928 return hr;
931 hr = IDXGISwapChain_GetBuffer(sys->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *)&pBackBuffer);
932 if (FAILED(hr)) {
933 msg_Err(vd, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr);
934 return hr;
937 hr = ID3D11Device_CreateRenderTargetView(sys->d3ddevice, (ID3D11Resource *)pBackBuffer, NULL, &sys->d3drenderTargetView);
938 ID3D11Texture2D_Release(pBackBuffer);
939 if (FAILED(hr)) {
940 msg_Err(vd, "Failed to create the target view. (hr=0x%lX)", hr);
941 return hr;
944 D3D11_TEXTURE2D_DESC deptTexDesc;
945 memset(&deptTexDesc, 0,sizeof(deptTexDesc));
946 deptTexDesc.ArraySize = 1;
947 deptTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
948 deptTexDesc.CPUAccessFlags = 0;
949 deptTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
950 deptTexDesc.Width = i_width;
951 deptTexDesc.Height = i_height;
952 deptTexDesc.MipLevels = 1;
953 deptTexDesc.MiscFlags = 0;
954 deptTexDesc.SampleDesc.Count = 1;
955 deptTexDesc.SampleDesc.Quality = 0;
956 deptTexDesc.Usage = D3D11_USAGE_DEFAULT;
958 hr = ID3D11Device_CreateTexture2D(sys->d3ddevice, &deptTexDesc, NULL, &pDepthStencil);
959 if (FAILED(hr)) {
960 msg_Err(vd, "Could not create the depth stencil texture. (hr=0x%lX)", hr);
961 return hr;
964 D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc;
965 memset(&depthViewDesc, 0, sizeof(depthViewDesc));
967 depthViewDesc.Format = deptTexDesc.Format;
968 depthViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
969 depthViewDesc.Texture2D.MipSlice = 0;
971 hr = ID3D11Device_CreateDepthStencilView(sys->d3ddevice, (ID3D11Resource *)pDepthStencil, &depthViewDesc, &sys->d3ddepthStencilView);
972 ID3D11Texture2D_Release(pDepthStencil);
974 if (FAILED(hr)) {
975 msg_Err(vd, "Could not create the depth stencil view. (hr=0x%lX)", hr);
976 return hr;
979 return S_OK;
982 /* rotation around the Z axis */
983 static void getZRotMatrix(float theta, FLOAT matrix[static 16])
985 float st, ct;
987 sincosf(theta, &st, &ct);
989 const FLOAT m[] = {
990 /* x y z w */
991 ct, -st, 0.f, 0.f,
992 st, ct, 0.f, 0.f,
993 0.f, 0.f, 1.f, 0.f,
994 0.f, 0.f, 0.f, 1.f
997 memcpy(matrix, m, sizeof(m));
1000 /* rotation around the Y axis */
1001 static void getYRotMatrix(float theta, FLOAT matrix[static 16])
1003 float st, ct;
1005 sincosf(theta, &st, &ct);
1007 const FLOAT m[] = {
1008 /* x y z w */
1009 ct, 0.f, -st, 0.f,
1010 0.f, 1.f, 0.f, 0.f,
1011 st, 0.f, ct, 0.f,
1012 0.f, 0.f, 0.f, 1.f
1015 memcpy(matrix, m, sizeof(m));
1018 /* rotation around the X axis */
1019 static void getXRotMatrix(float phi, FLOAT matrix[static 16])
1021 float sp, cp;
1023 sincosf(phi, &sp, &cp);
1025 const FLOAT m[] = {
1026 /* x y z w */
1027 1.f, 0.f, 0.f, 0.f,
1028 0.f, cp, sp, 0.f,
1029 0.f, -sp, cp, 0.f,
1030 0.f, 0.f, 0.f, 1.f
1033 memcpy(matrix, m, sizeof(m));
1036 static void getZoomMatrix(float zoom, FLOAT matrix[static 16]) {
1038 const FLOAT m[] = {
1039 /* x y z w */
1040 1.0f, 0.0f, 0.0f, 0.0f,
1041 0.0f, 1.0f, 0.0f, 0.0f,
1042 0.0f, 0.0f, 1.0f, 0.0f,
1043 0.0f, 0.0f, zoom, 1.0f
1046 memcpy(matrix, m, sizeof(m));
1049 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
1050 static void getProjectionMatrix(float sar, float fovy, FLOAT matrix[static 16]) {
1052 float zFar = 1000;
1053 float zNear = 0.01;
1055 float f = 1.f / tanf(fovy / 2.f);
1057 const FLOAT m[] = {
1058 f / sar, 0.f, 0.f, 0.f,
1059 0.f, f, 0.f, 0.f,
1060 0.f, 0.f, (zNear + zFar) / (zNear - zFar), -1.f,
1061 0.f, 0.f, (2 * zNear * zFar) / (zNear - zFar), 0.f};
1063 memcpy(matrix, m, sizeof(m));
1066 static float UpdateFOVy(float f_fovx, float f_sar)
1068 return 2 * atanf(tanf(f_fovx / 2) / f_sar);
1071 static float UpdateZ(float f_fovx, float f_fovy)
1073 /* Do trigonometry to calculate the minimal z value
1074 * that will allow us to zoom out without seeing the outside of the
1075 * sphere (black borders). */
1076 float tan_fovx_2 = tanf(f_fovx / 2);
1077 float tan_fovy_2 = tanf(f_fovy / 2);
1078 float z_min = - SPHERE_RADIUS / sinf(atanf(sqrtf(
1079 tan_fovx_2 * tan_fovx_2 + tan_fovy_2 * tan_fovy_2)));
1081 /* The FOV value above which z is dynamically calculated. */
1082 const float z_thresh = 90.f;
1084 float f_z;
1085 if (f_fovx <= z_thresh * M_PI / 180)
1086 f_z = 0;
1087 else
1089 float f = z_min / ((FIELD_OF_VIEW_DEGREES_MAX - z_thresh) * M_PI / 180);
1090 f_z = f * f_fovx - f * z_thresh * M_PI / 180;
1091 if (f_z < z_min)
1092 f_z = z_min;
1094 return f_z;
1097 static void SetQuadVSProjection(vout_display_t *vd, d3d_quad_t *quad, const vlc_viewpoint_t *p_vp)
1099 if (!quad->pVertexShaderConstants)
1100 return;
1102 #define RAD(d) ((float) ((d) * M_PI / 180.f))
1103 float f_fovx = RAD(p_vp->fov);
1104 if ( f_fovx > FIELD_OF_VIEW_DEGREES_MAX * M_PI / 180 + 0.001f ||
1105 f_fovx < -0.001f )
1106 return;
1108 float f_sar = (float) vd->cfg->display.width / vd->cfg->display.height;
1109 float f_teta = RAD(p_vp->yaw) - (float) M_PI_2;
1110 float f_phi = RAD(p_vp->pitch);
1111 float f_roll = RAD(p_vp->roll);
1112 float f_fovy = UpdateFOVy(f_fovx, f_sar);
1113 float f_z = UpdateZ(f_fovx, f_fovy);
1115 vout_display_sys_t *sys = vd->sys;
1116 HRESULT hr;
1117 D3D11_MAPPED_SUBRESOURCE mapped;
1118 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
1119 if (SUCCEEDED(hr)) {
1120 VS_PROJECTION_CONST *dst_data = mapped.pData;
1121 getXRotMatrix(f_phi, dst_data->RotX);
1122 getYRotMatrix(f_teta, dst_data->RotY);
1123 getZRotMatrix(f_roll, dst_data->RotZ);
1124 getZoomMatrix(SPHERE_RADIUS * f_z, dst_data->View);
1125 getProjectionMatrix(f_sar, f_fovy, dst_data->Projection);
1127 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0);
1128 #undef RAD
1131 static void UpdateSize(vout_display_t *vd)
1133 vout_display_sys_t *sys = vd->sys;
1134 #if defined(HAVE_ID3D11VIDEODECODER)
1135 if( sys->context_lock != INVALID_HANDLE_VALUE )
1137 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1139 #endif
1140 msg_Dbg(vd, "Detected size change %dx%d", RECTWidth(sys->sys.rect_dest_clipped),
1141 RECTHeight(sys->sys.rect_dest_clipped));
1143 UpdateBackBuffer(vd);
1145 UpdatePicQuadPosition(vd);
1147 UpdateQuadPosition(vd, &sys->picQuad, &sys->sys.rect_src_clipped,
1148 vd->fmt.projection_mode, vd->fmt.orientation);
1150 #if defined(HAVE_ID3D11VIDEODECODER)
1151 if( sys->context_lock != INVALID_HANDLE_VALUE )
1153 ReleaseMutex( sys->context_lock );
1155 #endif
1158 static inline bool RectEquals(const RECT *r1, const RECT *r2)
1160 return r1->bottom == r2->bottom && r1->top == r2->top &&
1161 r1->left == r2->left && r1->right == r2->right;
1164 #define BEFORE_UPDATE_RECTS \
1165 unsigned int i_outside_width = vd->fmt.i_width; \
1166 unsigned int i_outside_height = vd->fmt.i_height; \
1167 vd->fmt.i_width = vd->sys->picQuad.i_width; \
1168 vd->fmt.i_height = vd->sys->picQuad.i_height;
1169 #define AFTER_UPDATE_RECTS \
1170 vd->fmt.i_width = i_outside_width; \
1171 vd->fmt.i_height = i_outside_height;
1173 static int Control(vout_display_t *vd, int query, va_list args)
1175 vout_display_sys_t *sys = vd->sys;
1176 RECT before_src_clipped = sys->sys.rect_src_clipped;
1177 RECT before_dest_clipped = sys->sys.rect_dest_clipped;
1178 RECT before_dest = sys->sys.rect_dest;
1180 BEFORE_UPDATE_RECTS;
1181 int res = CommonControl( vd, query, args );
1182 AFTER_UPDATE_RECTS;
1184 if (query == VOUT_DISPLAY_CHANGE_VIEWPOINT)
1186 const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t*);
1187 if ( sys->picQuad.pVertexShaderConstants )
1189 SetQuadVSProjection( vd, &sys->picQuad, &cfg->viewpoint );
1190 res = VLC_SUCCESS;
1194 if (!RectEquals(&before_src_clipped, &sys->sys.rect_src_clipped) ||
1195 !RectEquals(&before_dest_clipped, &sys->sys.rect_dest_clipped) ||
1196 !RectEquals(&before_dest, &sys->sys.rect_dest) )
1198 UpdateSize(vd);
1201 return res;
1204 static void Manage(vout_display_t *vd)
1206 vout_display_sys_t *sys = vd->sys;
1207 RECT before_src_clipped = sys->sys.rect_src_clipped;
1208 RECT before_dest_clipped = sys->sys.rect_dest_clipped;
1209 RECT before_dest = sys->sys.rect_dest;
1211 BEFORE_UPDATE_RECTS;
1212 CommonManage(vd);
1213 AFTER_UPDATE_RECTS;
1215 if (!RectEquals(&before_src_clipped, &sys->sys.rect_src_clipped) ||
1216 !RectEquals(&before_dest_clipped, &sys->sys.rect_dest_clipped) ||
1217 !RectEquals(&before_dest, &sys->sys.rect_dest))
1219 UpdateSize(vd);
1223 static void DisplayD3DPicture(vout_display_sys_t *sys, d3d_quad_t *quad, ID3D11ShaderResourceView *resourceView[D3D11_MAX_SHADER_VIEW])
1225 UINT stride = sizeof(d3d_vertex_t);
1226 UINT offset = 0;
1228 /* Render the quad */
1229 /* vertex shader */
1230 ID3D11DeviceContext_IASetVertexBuffers(sys->d3dcontext, 0, 1, &quad->pVertexBuffer, &stride, &offset);
1231 ID3D11DeviceContext_IASetIndexBuffer(sys->d3dcontext, quad->pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
1232 if ( quad->pVertexShaderConstants )
1233 ID3D11DeviceContext_VSSetConstantBuffers(sys->d3dcontext, 0, 1, &quad->pVertexShaderConstants);
1235 ID3D11DeviceContext_VSSetShader(sys->d3dcontext, quad->d3dvertexShader, NULL, 0);
1237 /* pixel shader */
1238 ID3D11DeviceContext_PSSetShader(sys->d3dcontext, quad->d3dpixelShader, NULL, 0);
1240 ID3D11DeviceContext_PSSetConstantBuffers(sys->d3dcontext, 0, quad->PSConstantsCount, quad->pPixelShaderConstants);
1241 ID3D11DeviceContext_PSSetShaderResources(sys->d3dcontext, 0, D3D11_MAX_SHADER_VIEW, resourceView);
1243 ID3D11DeviceContext_RSSetViewports(sys->d3dcontext, 1, &quad->cropViewport);
1245 ID3D11DeviceContext_DrawIndexed(sys->d3dcontext, quad->indexCount, 0, 0);
1248 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
1250 vout_display_sys_t *sys = vd->sys;
1252 picture_sys_t *p_sys = ActivePictureSys(picture);
1254 #if defined(HAVE_ID3D11VIDEODECODER)
1255 if (sys->context_lock != INVALID_HANDLE_VALUE && is_d3d11_opaque(picture->format.i_chroma))
1256 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1257 #endif
1258 if (p_sys->formatTexture == DXGI_FORMAT_UNKNOWN)
1260 Direct3D11UnlockDirectTexture(picture);
1261 for (int plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++)
1263 if (!p_sys->resource[plane])
1264 continue;
1266 unsigned int width = sys->picQuad.i_width;
1267 unsigned int height = sys->picQuad.i_height;
1268 unsigned int x_offset = picture->format.i_x_offset;
1269 unsigned int y_offset = picture->format.i_y_offset;
1270 x_offset = x_offset * sys->picQuad.p_chroma_sampling->p[plane].h.num / sys->picQuad.p_chroma_sampling->p[plane].h.den;
1271 y_offset = y_offset * sys->picQuad.p_chroma_sampling->p[plane].w.num / sys->picQuad.p_chroma_sampling->p[plane].w.den;
1272 width = width * sys->picQuad.p_chroma_sampling->p[plane].h.num / sys->picQuad.p_chroma_sampling->p[plane].h.den;
1273 height = height * sys->picQuad.p_chroma_sampling->p[plane].w.num / sys->picQuad.p_chroma_sampling->p[plane].w.den;
1275 D3D11_BOX box = {
1276 .top = y_offset,
1277 .bottom = y_offset + height,
1278 .left = x_offset,
1279 .right = x_offset + width,
1280 .back = 1,
1282 ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
1283 sys->stagingSys.resource[plane],
1284 0, 0, 0, 0,
1285 p_sys->resource[plane],
1286 0, &box);
1289 else if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader) {
1290 D3D11_TEXTURE2D_DESC texDesc;
1291 if (!is_d3d11_opaque(picture->format.i_chroma))
1292 Direct3D11UnmapPoolTexture(picture);
1293 ID3D11Texture2D_GetDesc(sys->stagingSys.texture[0], &texDesc);
1294 D3D11_BOX box = {
1295 .top = 0,
1296 .bottom = picture->format.i_y_offset + picture->format.i_visible_height,
1297 .left = 0,
1298 .right = picture->format.i_x_offset + picture->format.i_visible_width,
1299 .back = 1,
1301 if ( sys->picQuadConfig->formatTexture != DXGI_FORMAT_R8G8B8A8_UNORM &&
1302 sys->picQuadConfig->formatTexture != DXGI_FORMAT_B5G6R5_UNORM )
1304 box.bottom = (box.bottom + 0x01) & ~0x01;
1305 box.right = (box.right + 0x01) & ~0x01;
1307 assert(box.right <= texDesc.Width);
1308 assert(box.bottom <= texDesc.Height);
1309 ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
1310 sys->stagingSys.resource[KNOWN_DXGI_INDEX],
1311 0, 0, 0, 0,
1312 p_sys->resource[KNOWN_DXGI_INDEX],
1313 p_sys->slice_index, &box);
1315 else
1317 D3D11_TEXTURE2D_DESC texDesc;
1318 ID3D11Texture2D_GetDesc(p_sys->texture[0], &texDesc);
1319 if (texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE)
1321 /* for performance reason we don't want to allocate this during
1322 * display, do it preferrably when creating the texture */
1323 assert(p_sys->resourceView[0]!=NULL);
1326 if ( sys->picQuad.i_height != texDesc.Height ||
1327 sys->picQuad.i_width != texDesc.Width )
1329 /* the decoder produced different sizes than the vout, we need to
1330 * adjust the vertex */
1331 sys->picQuad.i_height = texDesc.Height;
1332 sys->picQuad.i_width = texDesc.Width;
1334 BEFORE_UPDATE_RECTS;
1335 UpdateRects(vd, NULL, true);
1336 AFTER_UPDATE_RECTS;
1337 UpdateSize(vd);
1341 if (subpicture) {
1342 int subpicture_region_count = 0;
1343 picture_t **subpicture_regions = NULL;
1344 Direct3D11MapSubpicture(vd, &subpicture_region_count, &subpicture_regions, subpicture);
1345 Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
1346 sys->d3dregion_count = subpicture_region_count;
1347 sys->d3dregions = subpicture_regions;
1350 FLOAT blackRGBA[4] = {0.0f, 0.0f, 0.0f, 1.0f};
1351 ID3D11DeviceContext_ClearRenderTargetView(sys->d3dcontext, sys->d3drenderTargetView, blackRGBA);
1353 /* no ID3D11Device operations should come here */
1355 ID3D11DeviceContext_OMSetRenderTargets(sys->d3dcontext, 1, &sys->d3drenderTargetView, sys->d3ddepthStencilView);
1357 ID3D11DeviceContext_ClearDepthStencilView(sys->d3dcontext, sys->d3ddepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
1359 /* Render the quad */
1360 if (!is_d3d11_opaque(picture->format.i_chroma) || sys->legacy_shader)
1361 DisplayD3DPicture(sys, &sys->picQuad, sys->stagingSys.resourceView);
1362 else {
1363 picture_sys_t *p_sys = ActivePictureSys(picture);
1364 DisplayD3DPicture(sys, &sys->picQuad, p_sys->resourceView);
1367 if (subpicture) {
1368 // draw the additional vertices
1369 for (int i = 0; i < sys->d3dregion_count; ++i) {
1370 if (sys->d3dregions[i])
1372 d3d_quad_t *quad = (d3d_quad_t *) sys->d3dregions[i]->p_sys;
1373 DisplayD3DPicture(sys, quad, quad->picSys.resourceView);
1378 #if defined(HAVE_ID3D11VIDEODECODER)
1379 if (sys->context_lock != INVALID_HANDLE_VALUE && is_d3d11_opaque(picture->format.i_chroma))
1381 ReleaseMutex( sys->context_lock );
1383 #endif
1385 if (sys->dxgiswapChain4 && picture->format.mastering.max_luminance)
1387 DXGI_HDR_METADATA_HDR10 hdr10 = {0};
1388 hdr10.GreenPrimary[0] = picture->format.mastering.primaries[0];
1389 hdr10.GreenPrimary[1] = picture->format.mastering.primaries[1];
1390 hdr10.BluePrimary[0] = picture->format.mastering.primaries[2];
1391 hdr10.BluePrimary[1] = picture->format.mastering.primaries[3];
1392 hdr10.RedPrimary[0] = picture->format.mastering.primaries[4];
1393 hdr10.RedPrimary[1] = picture->format.mastering.primaries[5];
1394 hdr10.WhitePoint[0] = picture->format.mastering.white_point[0];
1395 hdr10.WhitePoint[1] = picture->format.mastering.white_point[1];
1396 hdr10.MinMasteringLuminance = picture->format.mastering.min_luminance;
1397 hdr10.MaxMasteringLuminance = picture->format.mastering.max_luminance;
1398 hdr10.MaxContentLightLevel = picture->format.lighting.MaxCLL;
1399 hdr10.MaxFrameAverageLightLevel = picture->format.lighting.MaxFALL;
1400 IDXGISwapChain4_SetHDRMetaData(sys->dxgiswapChain4, DXGI_HDR_METADATA_TYPE_HDR10, sizeof(hdr10), &hdr10);
1403 //ID3D11DeviceContext_Flush(sys->d3dcontext);
1406 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
1408 vout_display_sys_t *sys = vd->sys;
1410 DXGI_PRESENT_PARAMETERS presentParams;
1411 memset(&presentParams, 0, sizeof(presentParams));
1412 HRESULT hr = IDXGISwapChain1_Present1(sys->dxgiswapChain, 0, 0, &presentParams);
1413 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
1415 /* TODO device lost */
1416 msg_Dbg(vd, "SwapChain Present failed. (hr=0x%lX)", hr);
1419 picture_Release(picture);
1420 if (subpicture)
1421 subpicture_Delete(subpicture);
1423 CommonDisplay(vd);
1426 static void Direct3D11Destroy(vout_display_t *vd)
1428 #if !VLC_WINSTORE_APP
1429 vout_display_sys_t *sys = vd->sys;
1431 if (sys->hd3d11_dll)
1432 FreeLibrary(sys->hd3d11_dll);
1433 if (sys->hd3dcompiler_dll)
1434 FreeLibrary(sys->hd3dcompiler_dll);
1436 sys->OurD3D11CreateDevice = NULL;
1437 sys->OurD3D11CreateDeviceAndSwapChain = NULL;
1438 sys->OurD3DCompile = NULL;
1439 sys->hdxgi_dll = NULL;
1440 sys->hd3d11_dll = NULL;
1441 sys->hd3dcompiler_dll = NULL;
1442 #else
1443 VLC_UNUSED(vd);
1444 #endif
1447 #if !VLC_WINSTORE_APP
1448 static HINSTANCE Direct3D11LoadShaderLibrary(void)
1450 HINSTANCE instance = NULL;
1451 /* d3dcompiler_47 is the latest on windows 8.1 */
1452 for (int i = 47; i > 41; --i) {
1453 TCHAR filename[19];
1454 _sntprintf(filename, 19, TEXT("D3DCOMPILER_%d.dll"), i);
1455 instance = LoadLibrary(filename);
1456 if (instance) break;
1458 return instance;
1460 #endif
1462 #define COLOR_RANGE_FULL 1 /* 0-255 */
1463 #define COLOR_RANGE_STUDIO 0 /* 16-235 */
1465 #define TRANSFER_FUNC_10 TRANSFER_FUNC_LINEAR
1466 #define TRANSFER_FUNC_22 TRANSFER_FUNC_SRGB
1467 #define TRANSFER_FUNC_2084 TRANSFER_FUNC_SMPTE_ST2084
1469 #define COLOR_PRIMARIES_BT601 COLOR_PRIMARIES_BT601_525
1471 static const dxgi_color_space color_spaces[] = {
1472 #define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
1473 { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
1474 #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
1475 COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
1476 COLOR_SPACE_BT##PRIMARIES, COLOR_RANGE_##RANGE},
1478 DXGIMAP(RGB, FULL, 22, NONE, 709)
1479 DXGIMAP(YCBCR, STUDIO, 22, LEFT, 601)
1480 DXGIMAP(YCBCR, FULL, 22, LEFT, 601)
1481 DXGIMAP(RGB, FULL, 10, NONE, 709)
1482 DXGIMAP(RGB, STUDIO, 22, NONE, 709)
1483 DXGIMAP(YCBCR, STUDIO, 22, LEFT, 709)
1484 DXGIMAP(YCBCR, FULL, 22, LEFT, 709)
1485 DXGIMAP(RGB, STUDIO, 22, NONE, 2020)
1486 DXGIMAP(YCBCR, STUDIO, 22, LEFT, 2020)
1487 DXGIMAP(YCBCR, FULL, 22, LEFT, 2020)
1488 DXGIMAP(YCBCR, STUDIO, 22, TOPLEFT, 2020)
1489 DXGIMAP(RGB, FULL, 22, NONE, 2020)
1490 DXGIMAP(RGB, FULL, 2084, NONE, 2020)
1491 DXGIMAP(YCBCR, STUDIO, 2084, LEFT, 2020)
1492 DXGIMAP(RGB, STUDIO, 2084, NONE, 2020)
1493 DXGIMAP(YCBCR, STUDIO, 2084, TOPLEFT, 2020)
1494 /*DXGIMAP(YCBCR, FULL, 22, NONE, 2020, 601)*/
1495 {DXGI_COLOR_SPACE_RESERVED, NULL, 0, 0, 0, 0, 0},
1496 #undef DXGIMAP
1499 static void D3D11SetColorSpace(vout_display_t *vd)
1501 vout_display_sys_t *sys = vd->sys;
1502 HRESULT hr;
1503 int best = -1;
1504 int score, best_score = 0;
1505 UINT support;
1506 IDXGISwapChain3 *dxgiswapChain3 = NULL;
1507 sys->display.colorspace = &color_spaces[0];
1509 hr = ID3D11Device_QueryInterface( sys->dxgiswapChain, &IID_IDXGISwapChain3, (void **)&dxgiswapChain3);
1510 if (FAILED(hr)) {
1511 msg_Warn(vd, "could not get a IDXGISwapChain3");
1512 goto done;
1515 bool src_full_range = vd->source.b_color_range_full ||
1516 /* the YUV->RGB conversion already output full range */
1517 is_d3d11_opaque(vd->source.i_chroma) ||
1518 vlc_fourcc_IsYUV(vd->source.i_chroma);
1519 #if VLC_WINSTORE_APP
1520 if (!src_full_range && isXboxHardware(sys->d3ddevice)) {
1521 /* even in full range the Xbox outputs with less range so use the max we can*/
1522 src_full_range = true;
1524 #endif
1526 /* pick the best output based on color support and transfer */
1527 /* TODO support YUV output later */
1528 for (int i=0; color_spaces[i].name; ++i)
1530 hr = IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3, color_spaces[i].dxgi, &support);
1531 if (SUCCEEDED(hr) && support) {
1532 msg_Dbg(vd, "supports colorspace %s", color_spaces[i].name);
1533 score = 0;
1534 if (color_spaces[i].primaries == vd->source.primaries)
1535 score++;
1536 if (color_spaces[i].color == vd->source.space)
1537 score += 2; /* we don't want to translate color spaces */
1538 if (color_spaces[i].transfer == vd->source.transfer ||
1539 /* favor 2084 output for HLG source */
1540 (color_spaces[i].transfer == TRANSFER_FUNC_SMPTE_ST2084 && vd->source.transfer == TRANSFER_FUNC_HLG))
1541 score++;
1542 if (color_spaces[i].b_full_range == src_full_range)
1543 score++;
1544 if (score > best_score || (score && best == -1)) {
1545 best = i;
1546 best_score = score;
1551 if (best == -1)
1553 best = 0;
1554 msg_Warn(vd, "no matching colorspace found force %s", color_spaces[best].name);
1556 hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
1557 if (SUCCEEDED(hr))
1559 sys->display.colorspace = &color_spaces[best];
1560 msg_Dbg(vd, "using colorspace %s", sys->display.colorspace->name);
1562 else
1563 msg_Err(vd, "Failed to set colorspace %s. (hr=0x%lX)", sys->display.colorspace->name, hr);
1564 done:
1565 /* guestimate the display peak luminance */
1566 switch (sys->display.colorspace->transfer)
1568 case TRANSFER_FUNC_LINEAR:
1569 case TRANSFER_FUNC_SRGB:
1570 sys->display.luminance_peak = DEFAULT_BRIGHTNESS;
1571 break;
1572 case TRANSFER_FUNC_SMPTE_ST2084:
1573 sys->display.luminance_peak = 10000;
1574 break;
1575 /* there is no other output transfer on Windows */
1576 default:
1577 vlc_assert_unreachable();
1580 if (dxgiswapChain3)
1581 IDXGISwapChain3_Release(dxgiswapChain3);
1584 static const d3d_format_t *GetDirectRenderingFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
1586 UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
1587 if (is_d3d11_opaque(i_src_chroma))
1588 supportFlags |= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT;
1589 return FindD3D11Format( vd->sys->d3ddevice, i_src_chroma, 0, is_d3d11_opaque(i_src_chroma), supportFlags );
1592 static const d3d_format_t *GetDirectDecoderFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
1594 UINT supportFlags = D3D11_FORMAT_SUPPORT_DECODER_OUTPUT;
1595 return FindD3D11Format( vd->sys->d3ddevice, i_src_chroma, 0, is_d3d11_opaque(i_src_chroma), supportFlags );
1598 static const d3d_format_t *GetDisplayFormatByDepth(vout_display_t *vd, uint8_t bit_depth, bool from_processor)
1600 UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
1601 if (from_processor)
1602 supportFlags |= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT;
1603 return FindD3D11Format( vd->sys->d3ddevice, 0, bit_depth, false, supportFlags );
1606 static const d3d_format_t *GetBlendableFormat(vout_display_t *vd, vlc_fourcc_t i_src_chroma)
1608 UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD | D3D11_FORMAT_SUPPORT_BLENDABLE;
1609 return FindD3D11Format( vd->sys->d3ddevice, i_src_chroma, 0, false, supportFlags );
1612 static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
1614 vout_display_sys_t *sys = vd->sys;
1615 IDXGIFactory2 *dxgifactory;
1617 #if !VLC_WINSTORE_APP
1619 UINT creationFlags = 0;
1620 HRESULT hr = S_OK;
1622 # if !defined(NDEBUG)
1623 # if !VLC_WINSTORE_APP
1624 if (IsDebuggerPresent())
1625 # endif
1627 HINSTANCE sdklayer_dll = LoadLibrary(TEXT("d3d11_1sdklayers.dll"));
1628 if (sdklayer_dll) {
1629 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
1630 FreeLibrary(sdklayer_dll);
1633 # endif
1635 if (is_d3d11_opaque(fmt->i_chroma))
1636 creationFlags |= D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
1638 DXGI_SWAP_CHAIN_DESC1 scd;
1639 memset(&scd, 0, sizeof(scd));
1640 scd.BufferCount = 2;
1641 scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1642 scd.SampleDesc.Count = 1;
1643 scd.SampleDesc.Quality = 0;
1644 scd.Width = fmt->i_visible_width;
1645 scd.Height = fmt->i_visible_height;
1646 switch(fmt->i_chroma)
1648 case VLC_CODEC_D3D11_OPAQUE_10B:
1649 scd.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
1650 break;
1651 default:
1652 scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; /* TODO: use DXGI_FORMAT_NV12 */
1653 break;
1655 //scd.Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1656 scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1658 static const D3D_DRIVER_TYPE driverAttempts[] = {
1659 D3D_DRIVER_TYPE_HARDWARE,
1660 D3D_DRIVER_TYPE_WARP,
1661 #if 0 /* ifndef NDEBUG */
1662 D3D_DRIVER_TYPE_REFERENCE,
1663 #endif
1666 D3D_FEATURE_LEVEL features[] = {
1667 D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
1668 D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0,
1669 D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1
1672 for (UINT driver = 0; driver < ARRAYSIZE(driverAttempts); driver++) {
1673 D3D_FEATURE_LEVEL i_feature_level;
1674 hr = D3D11CreateDevice(NULL, driverAttempts[driver], NULL, creationFlags,
1675 features, ARRAY_SIZE(features), D3D11_SDK_VERSION,
1676 &sys->d3ddevice, &i_feature_level, &sys->d3dcontext);
1677 if (SUCCEEDED(hr)) {
1678 #ifndef NDEBUG
1679 msg_Dbg(vd, "Created the D3D11 device 0x%p ctx 0x%p type %d level %x.",
1680 (void *)sys->d3ddevice, (void *)sys->d3dcontext,
1681 driverAttempts[driver], i_feature_level);
1682 #endif
1683 if ( vd->obj.force || i_feature_level >= D3D_FEATURE_LEVEL_11_1 )
1684 break;
1685 ID3D11DeviceContext_Release(sys->d3dcontext);
1686 sys->d3dcontext = NULL;
1687 ID3D11Device_Release(sys->d3ddevice);
1688 sys->d3ddevice = NULL;
1692 if (FAILED(hr)) {
1693 msg_Err(vd, "Could not Create the D3D11 device. (hr=0x%lX)", hr);
1694 return VLC_EGENERIC;
1697 IDXGIAdapter *dxgiadapter = D3D11DeviceAdapter(sys->d3ddevice);
1698 if (unlikely(dxgiadapter==NULL)) {
1699 msg_Err(vd, "Could not get the DXGI Adapter");
1700 return VLC_EGENERIC;
1703 hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&dxgifactory);
1704 IDXGIAdapter_Release(dxgiadapter);
1705 if (FAILED(hr)) {
1706 msg_Err(vd, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
1707 return VLC_EGENERIC;
1710 hr = IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory, (IUnknown *)sys->d3ddevice,
1711 sys->sys.hvideownd, &scd, NULL, NULL, &sys->dxgiswapChain);
1712 IDXGIFactory2_Release(dxgifactory);
1713 if (FAILED(hr)) {
1714 msg_Err(vd, "Could not create the SwapChain. (hr=0x%lX)", hr);
1715 return VLC_EGENERIC;
1717 #endif
1719 ID3D11Device_QueryInterface( sys->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&sys->dxgiswapChain4);
1721 D3D11SetColorSpace(vd);
1723 // look for the requested pixel format first
1724 sys->picQuadConfig = GetDirectRenderingFormat(vd, fmt->i_chroma);
1726 /* look for a decoder format that can be decoded but not used in shaders */
1727 const d3d_format_t *decoder_format = NULL;
1728 if ( !sys->picQuadConfig && is_d3d11_opaque(fmt->i_chroma) )
1729 decoder_format = GetDirectDecoderFormat(vd, fmt->i_chroma);
1731 // look for any pixel format that we can handle with enough pixels per channel
1732 if ( !sys->picQuadConfig )
1734 uint8_t bits_per_channel;
1735 switch (fmt->i_chroma)
1737 case VLC_CODEC_D3D11_OPAQUE:
1738 bits_per_channel = 8;
1739 break;
1740 case VLC_CODEC_D3D11_OPAQUE_10B:
1741 bits_per_channel = 10;
1742 break;
1743 default:
1745 const vlc_chroma_description_t *p_format = vlc_fourcc_GetChromaDescription(fmt->i_chroma);
1746 bits_per_channel = p_format == NULL || p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits;
1748 break;
1751 sys->picQuadConfig = GetDisplayFormatByDepth(vd, bits_per_channel, decoder_format!=NULL);
1754 // look for any pixel format that we can handle
1755 if ( !sys->picQuadConfig )
1756 sys->picQuadConfig = GetDisplayFormatByDepth(vd, 0, false);
1758 if ( !sys->picQuadConfig )
1760 msg_Err(vd, "Could not get a suitable texture pixel format");
1761 return VLC_EGENERIC;
1764 fmt->i_chroma = sys->picQuadConfig->fourcc;
1766 msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", sys->picQuadConfig->name,
1767 (char *)&fmt->i_chroma );
1768 DxgiFormatMask( sys->picQuadConfig->formatTexture, fmt );
1770 /* check the region pixel format */
1771 sys->d3dregion_format = GetBlendableFormat(vd, VLC_CODEC_RGBA);
1772 if (!sys->d3dregion_format)
1773 sys->d3dregion_format = GetBlendableFormat(vd, VLC_CODEC_BGRA);
1775 sys->picQuad.i_width = fmt->i_width;
1776 sys->picQuad.i_height = fmt->i_height;
1777 if (is_d3d11_opaque(fmt->i_chroma))
1779 /* worst case scenario we need 128 alignment for HEVC */
1780 sys->picQuad.i_width = (sys->picQuad.i_width + 0x7F) & ~0x7F;
1781 sys->picQuad.i_height = (sys->picQuad.i_height + 0x7F) & ~0x7F;
1783 else if ( sys->picQuadConfig->formatTexture != DXGI_FORMAT_R8G8B8A8_UNORM &&
1784 sys->picQuadConfig->formatTexture != DXGI_FORMAT_B5G6R5_UNORM )
1786 sys->picQuad.i_width = (sys->picQuad.i_width + 0x01) & ~0x01;
1787 sys->picQuad.i_height = (sys->picQuad.i_height + 0x01) & ~0x01;
1790 BEFORE_UPDATE_RECTS;
1791 UpdateRects(vd, NULL, true);
1792 AFTER_UPDATE_RECTS;
1794 #if defined(HAVE_ID3D11VIDEODECODER)
1795 if( sys->context_lock != INVALID_HANDLE_VALUE )
1797 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1799 #endif
1800 if (Direct3D11CreateResources(vd, fmt)) {
1801 #if defined(HAVE_ID3D11VIDEODECODER)
1802 if( sys->context_lock != INVALID_HANDLE_VALUE )
1804 ReleaseMutex( sys->context_lock );
1806 #endif
1807 msg_Err(vd, "Failed to allocate resources");
1808 Direct3D11DestroyResources(vd);
1809 return VLC_EGENERIC;
1811 #if defined(HAVE_ID3D11VIDEODECODER)
1812 if( sys->context_lock != INVALID_HANDLE_VALUE )
1814 ReleaseMutex( sys->context_lock );
1816 #endif
1818 #if !VLC_WINSTORE_APP
1819 EventThreadUpdateTitle(sys->sys.event, VOUT_TITLE " (Direct3D11 output)");
1820 #endif
1822 msg_Dbg(vd, "Direct3D11 device adapter successfully initialized");
1823 return VLC_SUCCESS;
1826 static void Direct3D11Close(vout_display_t *vd)
1828 vout_display_sys_t *sys = vd->sys;
1830 Direct3D11DestroyResources(vd);
1831 if (sys->d3dcontext)
1833 ID3D11DeviceContext_Flush(sys->d3dcontext);
1834 ID3D11DeviceContext_Release(sys->d3dcontext);
1835 sys->d3dcontext = NULL;
1837 if (sys->d3ddevice)
1839 ID3D11Device_Release(sys->d3ddevice);
1840 sys->d3ddevice = NULL;
1842 if (sys->dxgiswapChain4)
1844 IDXGISwapChain_Release(sys->dxgiswapChain4);
1845 sys->dxgiswapChain4 = NULL;
1847 if (sys->dxgiswapChain)
1849 IDXGISwapChain_Release(sys->dxgiswapChain);
1850 sys->dxgiswapChain = NULL;
1853 msg_Dbg(vd, "Direct3D11 device adapter closed");
1856 static void UpdatePicQuadPosition(vout_display_t *vd)
1858 vout_display_sys_t *sys = vd->sys;
1860 sys->picQuad.cropViewport.Width = RECTWidth(sys->sys.rect_dest_clipped);
1861 sys->picQuad.cropViewport.Height = RECTHeight(sys->sys.rect_dest_clipped);
1862 sys->picQuad.cropViewport.TopLeftX = sys->sys.rect_dest_clipped.left;
1863 sys->picQuad.cropViewport.TopLeftY = sys->sys.rect_dest_clipped.top;
1865 sys->picQuad.cropViewport.MinDepth = 0.0f;
1866 sys->picQuad.cropViewport.MaxDepth = 1.0f;
1868 SetQuadVSProjection(vd, &sys->picQuad, &vd->cfg->viewpoint);
1870 #ifndef NDEBUG
1871 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 );
1872 #endif
1875 static ID3DBlob* CompileShader(vout_display_t *vd, const char *psz_shader, bool pixel)
1877 vout_display_sys_t *sys = vd->sys;
1878 ID3DBlob* pShaderBlob = NULL, *pErrBlob;
1880 /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1881 HRESULT hr = D3DCompile(psz_shader, strlen(psz_shader),
1882 NULL, NULL, NULL, "main",
1883 pixel ? (sys->legacy_shader ? "ps_4_0_level_9_1" : "ps_4_0") :
1884 (sys->legacy_shader ? "vs_4_0_level_9_1" : "vs_4_0"),
1885 0, 0, &pShaderBlob, &pErrBlob);
1887 if (FAILED(hr)) {
1888 char *err = pErrBlob ? ID3D10Blob_GetBufferPointer(pErrBlob) : NULL;
1889 msg_Err(vd, "invalid %s Shader (hr=0x%lX): %s", pixel?"Pixel":"Vertex", hr, err );
1890 if (pErrBlob)
1891 ID3D10Blob_Release(pErrBlob);
1892 return NULL;
1894 return pShaderBlob;
1897 static bool IsRGBShader(const d3d_format_t *cfg)
1899 return cfg->resourceFormat[0] != DXGI_FORMAT_R8_UNORM &&
1900 cfg->resourceFormat[0] != DXGI_FORMAT_R16_UNORM &&
1901 cfg->formatTexture != DXGI_FORMAT_YUY2;
1904 static HRESULT CompilePixelShader(vout_display_t *vd, const d3d_format_t *format,
1905 video_transfer_func_t transfer, bool src_full_range,
1906 ID3D11PixelShader **output)
1908 vout_display_sys_t *sys = vd->sys;
1910 static const char *DEFAULT_NOOP = "return rgb";
1911 const char *psz_sampler;
1912 const char *psz_src_transform = DEFAULT_NOOP;
1913 const char *psz_display_transform = DEFAULT_NOOP;
1914 const char *psz_tone_mapping = DEFAULT_NOOP;
1915 const char *psz_peak_luminance = DEFAULT_NOOP;
1916 const char *psz_adjust_range = DEFAULT_NOOP;
1917 char *psz_luminance = NULL;
1918 char *psz_range = NULL;
1920 switch (format->formatTexture)
1922 case DXGI_FORMAT_NV12:
1923 case DXGI_FORMAT_P010:
1924 psz_sampler =
1925 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1926 sample.yz = shaderTexture[1].Sample(SampleType, In.Texture).xy;\
1927 sample.a = 1;";
1928 break;
1929 case DXGI_FORMAT_YUY2:
1930 psz_sampler =
1931 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1932 sample.y = shaderTexture[0].Sample(SampleType, In.Texture).y;\
1933 sample.z = shaderTexture[0].Sample(SampleType, In.Texture).a;\
1934 sample.a = 1;";
1935 break;
1936 case DXGI_FORMAT_R8G8B8A8_UNORM:
1937 case DXGI_FORMAT_B8G8R8A8_UNORM:
1938 case DXGI_FORMAT_B5G6R5_UNORM:
1939 psz_sampler =
1940 "sample = shaderTexture[0].Sample(SampleType, In.Texture);";
1941 break;
1942 case DXGI_FORMAT_UNKNOWN:
1943 psz_sampler =
1944 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1945 sample.y = shaderTexture[1].Sample(SampleType, In.Texture).x;\
1946 sample.z = shaderTexture[2].Sample(SampleType, In.Texture).x;\
1947 sample.a = 1;";
1948 break;
1949 default:
1950 vlc_assert_unreachable();
1953 video_transfer_func_t src_transfer;
1954 unsigned src_luminance_peak;
1955 switch (transfer)
1957 case TRANSFER_FUNC_SMPTE_ST2084:
1958 /* TODO ajust this value using HDR metadata ? */
1959 src_luminance_peak = 7500;
1960 break;
1961 case TRANSFER_FUNC_HLG:
1962 src_luminance_peak = 1000;
1963 break;
1964 case TRANSFER_FUNC_BT709:
1965 case TRANSFER_FUNC_SRGB:
1966 src_luminance_peak = DEFAULT_BRIGHTNESS;
1967 break;
1968 default:
1969 msg_Dbg(vd, "unhandled source transfer %d", transfer);
1970 src_luminance_peak = DEFAULT_BRIGHTNESS;
1971 break;
1974 if (transfer != sys->display.colorspace->transfer)
1976 /* we need to go in linear mode */
1977 switch (transfer)
1979 case TRANSFER_FUNC_SMPTE_ST2084:
1980 /* ST2084 to Linear */
1981 psz_src_transform =
1982 ST2084_PQ_CONSTANTS
1983 "rgb = pow(rgb, 1.0/ST2084_m2);\
1984 rgb = max(rgb - ST2084_c1, 0.0) / (ST2084_c2 - ST2084_c3 * rgb);\
1985 rgb = pow(rgb, 1.0/ST2084_m1);\
1986 return rgb";
1987 src_transfer = TRANSFER_FUNC_LINEAR;
1988 break;
1989 case TRANSFER_FUNC_HLG:
1990 /* HLG to Linear */
1991 psz_src_transform =
1992 "rgb.r = inverse_HLG(rgb.r);\
1993 rgb.g = inverse_HLG(rgb.g);\
1994 rgb.b = inverse_HLG(rgb.b);\
1995 return rgb / 20.0";
1996 src_transfer = TRANSFER_FUNC_LINEAR;
1997 break;
1998 case TRANSFER_FUNC_BT709:
1999 psz_src_transform = "return pow(rgb, 1.0 / 0.45)";
2000 src_transfer = TRANSFER_FUNC_LINEAR;
2001 break;
2002 case TRANSFER_FUNC_SRGB:
2003 psz_src_transform = "return pow(rgb, 2.2)";
2004 src_transfer = TRANSFER_FUNC_LINEAR;
2005 break;
2006 default:
2007 msg_Dbg(vd, "unhandled source transfer %d", transfer);
2008 src_transfer = transfer;
2009 break;
2012 switch (sys->display.colorspace->transfer)
2014 case TRANSFER_FUNC_SRGB:
2015 if (src_transfer == TRANSFER_FUNC_LINEAR)
2017 /* Linear to sRGB */
2018 psz_display_transform = "return pow(rgb, 1.0 / 2.2)";
2020 if (transfer == TRANSFER_FUNC_SMPTE_ST2084 || transfer == TRANSFER_FUNC_HLG)
2022 /* HDR tone mapping */
2023 psz_tone_mapping =
2024 "static const float3 HABLE_DIV = hable(11.2);\
2025 rgb = hable(rgb) / HABLE_DIV;\
2026 return rgb";
2029 else
2030 msg_Warn(vd, "don't know how to transfer from %d to sRGB", src_transfer);
2031 break;
2033 case TRANSFER_FUNC_SMPTE_ST2084:
2034 if (src_transfer == TRANSFER_FUNC_LINEAR)
2036 /* Linear to ST2084 */
2037 psz_display_transform =
2038 ST2084_PQ_CONSTANTS
2039 "rgb = pow(rgb, ST2084_m1);\
2040 rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
2041 rgb = pow(rgb, ST2084_m2);\
2042 return rgb";
2044 else
2045 msg_Warn(vd, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer);
2046 break;
2047 default:
2048 msg_Warn(vd, "don't know how to transfer from %d to %d", src_transfer, sys->display.colorspace->transfer);
2049 break;
2053 if (src_luminance_peak != sys->display.luminance_peak)
2055 psz_luminance = malloc(64);
2056 if (likely(psz_luminance))
2058 sprintf(psz_luminance, "return rgb * %f", (float)src_luminance_peak / (float)sys->display.luminance_peak);
2059 psz_peak_luminance = psz_luminance;
2063 int range_adjust = sys->display.colorspace->b_full_range;
2064 #if VLC_WINSTORE_APP
2065 if (isXboxHardware(sys->d3ddevice)) {
2066 /* the Xbox lies, when it says it outputs full range it's less than full range */
2067 range_adjust--;
2069 #endif
2070 if (!IsRGBShader(format))
2071 range_adjust--; /* the YUV->RGB conversion already output full range */
2072 if (src_full_range)
2073 range_adjust--;
2075 if (range_adjust != 0)
2077 psz_range = malloc(256);
2078 if (likely(psz_range))
2080 FLOAT itu_black_level;
2081 FLOAT itu_range_factor;
2082 FLOAT itu_white_level;
2083 switch (format->bitsPerChannel)
2085 case 8:
2086 /* Rec. ITU-R BT.709-6 §4.6 */
2087 itu_black_level = 16.f / 255.f;
2088 itu_white_level = 235.f / 255.f;
2089 itu_range_factor = (float)(235 - 16) / 255.f;
2090 break;
2091 case 10:
2092 /* Rec. ITU-R BT.709-6 §4.6 */
2093 itu_black_level = 64.f / 1023.f;
2094 itu_white_level = 940.f / 1023.f;
2095 itu_range_factor = (float)(940 - 64) / 1023.f;
2096 break;
2097 case 12:
2098 /* Rec. ITU-R BT.2020-2 Table 5 */
2099 itu_black_level = 256.f / 4095.f;
2100 itu_white_level = 3760.f / 4095.f;
2101 itu_range_factor = (float)(3760 - 256) / 4095.f;
2102 break;
2103 default:
2104 /* unknown bitdepth, use approximation for infinite bit depth */
2105 itu_black_level = 16.f / 256.f;
2106 itu_white_level = 235.f / 256.f;
2107 itu_range_factor = (float)(235 - 16) / 256.f;
2108 break;
2111 FLOAT black_level = 0;
2112 FLOAT range_factor = 1.0f;
2113 if (range_adjust > 0)
2115 /* expand the range from studio to full range */
2116 while (range_adjust--)
2118 black_level -= itu_black_level;
2119 range_factor /= itu_range_factor;
2121 sprintf(psz_range, "return max(0,min(1,(rgb + %f) * %f))",
2122 black_level, range_factor);
2124 else
2126 /* shrink the range to studio range */
2127 while (range_adjust++)
2129 black_level += itu_black_level;
2130 range_factor *= itu_range_factor;
2132 sprintf(psz_range, "return clamp(rgb + %f * %f,%f,%f)",
2133 black_level, range_factor, itu_black_level, itu_white_level);
2135 psz_adjust_range = psz_range;
2139 char *shader = malloc(strlen(globPixelShaderDefault) + 32 + strlen(psz_sampler) +
2140 strlen(psz_src_transform) + strlen(psz_display_transform) +
2141 strlen(psz_tone_mapping) + strlen(psz_peak_luminance) + strlen(psz_adjust_range));
2142 if (!shader)
2144 msg_Err(vd, "no room for the Pixel Shader");
2145 free(psz_luminance);
2146 free(psz_range);
2147 return E_OUTOFMEMORY;
2149 sprintf(shader, globPixelShaderDefault, sys->legacy_shader ? "" : "Array", psz_src_transform,
2150 psz_display_transform, psz_tone_mapping, psz_peak_luminance, psz_adjust_range, psz_sampler);
2151 #ifndef NDEBUG
2152 if (!IsRGBShader(format)) {
2153 msg_Dbg(vd,"psz_src_transform %s", psz_src_transform);
2154 msg_Dbg(vd,"psz_tone_mapping %s", psz_tone_mapping);
2155 msg_Dbg(vd,"psz_peak_luminance %s", psz_peak_luminance);
2156 msg_Dbg(vd,"psz_display_transform %s", psz_display_transform);
2157 msg_Dbg(vd,"psz_adjust_range %s", psz_adjust_range);
2159 #endif
2160 free(psz_luminance);
2161 free(psz_range);
2163 ID3DBlob *pPSBlob = CompileShader(vd, shader, true);
2164 free(shader);
2165 if (!pPSBlob)
2166 return E_INVALIDARG;
2168 HRESULT hr = ID3D11Device_CreatePixelShader(sys->d3ddevice,
2169 (void *)ID3D10Blob_GetBufferPointer(pPSBlob),
2170 ID3D10Blob_GetBufferSize(pPSBlob), NULL, output);
2172 ID3D10Blob_Release(pPSBlob);
2173 return hr;
2176 #if (!VLC_WINSTORE_APP) && defined (HAVE_ID3D11VIDEODECODER)
2178 static HKEY GetAdapterRegistry(DXGI_ADAPTER_DESC *adapterDesc)
2180 HKEY hKey;
2181 TCHAR key[128];
2182 TCHAR szData[256], lookup[256];
2183 DWORD len = 256;
2185 _sntprintf(lookup, 256, TEXT("pci\\ven_%04x&dev_%04x&rev_cf"), adapterDesc->VendorId, adapterDesc->DeviceId);
2186 for (int i=0;;i++)
2188 _sntprintf(key, 128, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\%04d"), i);
2189 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey) != ERROR_SUCCESS )
2190 return NULL;
2192 len = sizeof(szData);
2193 if( RegQueryValueEx( hKey, TEXT("MatchingDeviceId"), NULL, NULL, (LPBYTE) &szData, &len ) == ERROR_SUCCESS ) {
2194 if (_tcscmp(lookup, szData) == 0)
2195 return hKey;
2198 RegCloseKey(hKey);
2200 return NULL;
2202 #endif
2204 static bool CanUseTextureArray(vout_display_t *vd)
2206 #ifndef HAVE_ID3D11VIDEODECODER
2207 (void) vd;
2208 return false;
2209 #else
2210 vout_display_sys_t *sys = vd->sys;
2211 TCHAR szData[256];
2212 DWORD len = 256;
2213 IDXGIAdapter *pAdapter = D3D11DeviceAdapter(sys->d3ddevice);
2214 if (!pAdapter)
2215 return false;
2217 DXGI_ADAPTER_DESC adapterDesc;
2218 HRESULT hr = IDXGIAdapter_GetDesc(pAdapter, &adapterDesc);
2219 IDXGIAdapter_Release(pAdapter);
2220 if (FAILED(hr))
2221 return false;
2223 /* ATI/AMD hardware has issues with pixel shaders with Texture2DArray */
2224 if (adapterDesc.VendorId != 0x1002)
2225 return true;
2227 #if !VLC_WINSTORE_APP
2228 HKEY hKey = GetAdapterRegistry(&adapterDesc);
2229 if (hKey == NULL)
2230 return false;
2232 LONG err = RegQueryValueEx( hKey, TEXT("DriverVersion"), NULL, NULL, (LPBYTE) &szData, &len );
2233 RegCloseKey(hKey);
2235 if (err == ERROR_SUCCESS )
2237 int wddm, d3d_features, revision, build;
2238 /* see https://msdn.microsoft.com/windows/hardware/commercialize/design/compatibility/device-graphics */
2239 if (_stscanf(szData, TEXT("%d.%d.%d.%d"), &wddm, &d3d_features, &revision, &build) == 4)
2240 /* it should be OK starting from driver 22.19.162.xxx */
2241 return wddm > 22 || (wddm == 22 && (d3d_features > 19 || (d3d_features == 19 && revision >= 162)));
2243 #endif
2244 msg_Dbg(vd, "fallback to legacy shader mode for old AMD drivers");
2245 return false;
2246 #endif
2249 /* TODO : handle errors better
2250 TODO : seperate out into smaller functions like createshaders */
2251 static int Direct3D11CreateResources(vout_display_t *vd, video_format_t *fmt)
2253 vout_display_sys_t *sys = vd->sys;
2254 HRESULT hr;
2256 #if defined(HAVE_ID3D11VIDEODECODER)
2257 sys->context_lock = CreateMutexEx( NULL, NULL, 0, SYNCHRONIZE );
2258 ID3D11Device_SetPrivateData( sys->d3ddevice, &GUID_CONTEXT_MUTEX, sizeof( sys->context_lock ), &sys->context_lock );
2259 #endif
2261 hr = UpdateBackBuffer(vd);
2262 if (FAILED(hr)) {
2263 msg_Err(vd, "Could not update the backbuffer. (hr=0x%lX)", hr);
2264 return VLC_EGENERIC;
2267 ID3D11BlendState *pSpuBlendState;
2268 D3D11_BLEND_DESC spuBlendDesc = { 0 };
2269 spuBlendDesc.RenderTarget[0].BlendEnable = TRUE;
2270 spuBlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
2271 spuBlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
2272 spuBlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
2274 spuBlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
2275 spuBlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
2276 spuBlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
2278 spuBlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
2280 spuBlendDesc.RenderTarget[1].BlendEnable = TRUE;
2281 spuBlendDesc.RenderTarget[1].SrcBlend = D3D11_BLEND_ONE;
2282 spuBlendDesc.RenderTarget[1].DestBlend = D3D11_BLEND_ZERO;
2283 spuBlendDesc.RenderTarget[1].BlendOp = D3D11_BLEND_OP_ADD;
2285 spuBlendDesc.RenderTarget[1].SrcBlendAlpha = D3D11_BLEND_ONE;
2286 spuBlendDesc.RenderTarget[1].DestBlendAlpha = D3D11_BLEND_ZERO;
2287 spuBlendDesc.RenderTarget[1].BlendOpAlpha = D3D11_BLEND_OP_ADD;
2289 spuBlendDesc.RenderTarget[1].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
2290 hr = ID3D11Device_CreateBlendState(sys->d3ddevice, &spuBlendDesc, &pSpuBlendState);
2291 if (FAILED(hr)) {
2292 msg_Err(vd, "Could not create SPU blend state. (hr=0x%lX)", hr);
2293 return VLC_EGENERIC;
2295 ID3D11DeviceContext_OMSetBlendState(sys->d3dcontext, pSpuBlendState, NULL, 0xFFFFFFFF);
2296 ID3D11BlendState_Release(pSpuBlendState);
2298 /* disable depth testing as we're only doing 2D
2299 * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
2300 * see http://rastertek.com/dx11tut11.html
2302 D3D11_DEPTH_STENCIL_DESC stencilDesc;
2303 ZeroMemory(&stencilDesc, sizeof(stencilDesc));
2305 ID3D11DepthStencilState *pDepthStencilState;
2306 hr = ID3D11Device_CreateDepthStencilState(sys->d3ddevice, &stencilDesc, &pDepthStencilState );
2307 if (SUCCEEDED(hr)) {
2308 ID3D11DeviceContext_OMSetDepthStencilState(sys->d3dcontext, pDepthStencilState, 0);
2309 ID3D11DepthStencilState_Release(pDepthStencilState);
2312 sys->legacy_shader = !CanUseTextureArray(vd);
2313 vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma) && sys->picQuadConfig->formatTexture != DXGI_FORMAT_UNKNOWN;
2315 hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
2316 if (FAILED(hr))
2318 #ifdef HAVE_ID3D11VIDEODECODER
2319 if (!sys->legacy_shader)
2321 sys->legacy_shader = true;
2322 msg_Dbg(vd, "fallback to legacy shader mode");
2323 hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
2325 #endif
2326 if (FAILED(hr))
2328 msg_Err(vd, "Failed to create the pixel shader. (hr=0x%lX)", hr);
2329 return VLC_EGENERIC;
2333 if (sys->d3dregion_format != NULL)
2335 hr = CompilePixelShader(vd, sys->d3dregion_format, TRANSFER_FUNC_SRGB, true, &sys->pSPUPixelShader);
2336 if (FAILED(hr))
2338 ID3D11PixelShader_Release(sys->picQuadPixelShader);
2339 sys->picQuadPixelShader = NULL;
2340 msg_Err(vd, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr);
2341 return VLC_EGENERIC;
2345 ID3DBlob *pVSBlob = CompileShader(vd, globVertexShaderFlat , false);
2346 if (!pVSBlob)
2347 return VLC_EGENERIC;
2349 hr = ID3D11Device_CreateVertexShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2350 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->flatVSShader);
2352 if(FAILED(hr)) {
2353 ID3D11Device_Release(pVSBlob);
2354 msg_Err(vd, "Failed to create the flat vertex shader. (hr=0x%lX)", hr);
2355 return VLC_EGENERIC;
2358 D3D11_INPUT_ELEMENT_DESC layout[] = {
2359 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
2360 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
2363 ID3D11InputLayout* pVertexLayout = NULL;
2364 hr = ID3D11Device_CreateInputLayout(sys->d3ddevice, layout, 2, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2365 ID3D10Blob_GetBufferSize(pVSBlob), &pVertexLayout);
2367 ID3D10Blob_Release(pVSBlob);
2369 if(FAILED(hr)) {
2370 msg_Err(vd, "Failed to create the vertex input layout. (hr=0x%lX)", hr);
2371 return VLC_EGENERIC;
2373 ID3D11DeviceContext_IASetInputLayout(sys->d3dcontext, pVertexLayout);
2374 ID3D11InputLayout_Release(pVertexLayout);
2376 pVSBlob = CompileShader(vd, globVertexShaderProjection, false);
2377 if (!pVSBlob)
2378 return VLC_EGENERIC;
2380 hr = ID3D11Device_CreateVertexShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2381 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->projectionVSShader);
2383 if(FAILED(hr)) {
2384 ID3D11Device_Release(pVSBlob);
2385 msg_Err(vd, "Failed to create the projection vertex shader. (hr=0x%lX)", hr);
2386 return VLC_EGENERIC;
2388 ID3D10Blob_Release(pVSBlob);
2390 ID3D11DeviceContext_IASetPrimitiveTopology(sys->d3dcontext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
2392 UpdatePicQuadPosition(vd);
2394 D3D11_SAMPLER_DESC sampDesc;
2395 memset(&sampDesc, 0, sizeof(sampDesc));
2396 sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
2397 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
2398 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
2399 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
2400 sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
2401 sampDesc.MinLOD = 0;
2402 sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
2404 ID3D11SamplerState *d3dsampState;
2405 hr = ID3D11Device_CreateSamplerState(sys->d3ddevice, &sampDesc, &d3dsampState);
2407 if (FAILED(hr)) {
2408 msg_Err(vd, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr);
2409 return VLC_EGENERIC;
2411 ID3D11DeviceContext_PSSetSamplers(sys->d3dcontext, 0, 1, &d3dsampState);
2412 ID3D11SamplerState_Release(d3dsampState);
2414 msg_Dbg(vd, "Direct3D11 resources created");
2415 return VLC_SUCCESS;
2418 static void Direct3D11DestroyPool(vout_display_t *vd)
2420 vout_display_sys_t *sys = vd->sys;
2422 if (sys->sys.pool)
2423 picture_pool_Release(sys->sys.pool);
2424 sys->sys.pool = NULL;
2428 * Compute the vertex ordering needed to rotate the video. Without
2429 * rotation, the vertices of the rectangle are defined in a clockwise
2430 * order. This function computes a remapping of the coordinates to
2431 * implement the rotation, given fixed texture coordinates.
2432 * The unrotated order is the following:
2433 * 0--1
2434 * | |
2435 * 3--2
2436 * For a 180 degrees rotation it should like this:
2437 * 2--3
2438 * | |
2439 * 1--0
2440 * Vertex 0 should be assigned coordinates at index 2 from the
2441 * unrotated order and so on, thus yielding order: 2 3 0 1.
2443 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
2445 switch (orientation) {
2446 case ORIENT_ROTATED_90:
2447 vertex_order[0] = 3;
2448 vertex_order[1] = 0;
2449 vertex_order[2] = 1;
2450 vertex_order[3] = 2;
2451 break;
2452 case ORIENT_ROTATED_270:
2453 vertex_order[0] = 1;
2454 vertex_order[1] = 2;
2455 vertex_order[2] = 3;
2456 vertex_order[3] = 0;
2457 break;
2458 case ORIENT_ROTATED_180:
2459 vertex_order[0] = 2;
2460 vertex_order[1] = 3;
2461 vertex_order[2] = 0;
2462 vertex_order[3] = 1;
2463 break;
2464 case ORIENT_TRANSPOSED:
2465 vertex_order[0] = 2;
2466 vertex_order[1] = 1;
2467 vertex_order[2] = 0;
2468 vertex_order[3] = 3;
2469 break;
2470 case ORIENT_HFLIPPED:
2471 vertex_order[0] = 3;
2472 vertex_order[1] = 2;
2473 vertex_order[2] = 1;
2474 vertex_order[3] = 0;
2475 break;
2476 case ORIENT_VFLIPPED:
2477 vertex_order[0] = 1;
2478 vertex_order[1] = 0;
2479 vertex_order[2] = 3;
2480 vertex_order[3] = 2;
2481 break;
2482 case ORIENT_ANTI_TRANSPOSED: /* transpose + vflip */
2483 vertex_order[0] = 1;
2484 vertex_order[1] = 2;
2485 vertex_order[2] = 3;
2486 vertex_order[3] = 0;
2487 break;
2488 default:
2489 vertex_order[0] = 0;
2490 vertex_order[1] = 1;
2491 vertex_order[2] = 2;
2492 vertex_order[3] = 3;
2493 break;
2497 static void SetupQuadFlat(d3d_vertex_t *dst_data, const RECT *output,
2498 const d3d_quad_t *quad,
2499 WORD *triangle_pos, video_orientation_t orientation)
2501 unsigned int dst_x = output->left;
2502 unsigned int dst_y = output->top;
2503 unsigned int dst_width = RECTWidth(*output);
2504 unsigned int dst_height = RECTHeight(*output);
2505 unsigned int src_width = quad->i_width;
2506 unsigned int src_height = quad->i_height;
2507 LONG MidY = output->top + output->bottom; // /2
2508 LONG MidX = output->left + output->right; // /2
2510 /* the clamping will not work properly on the side of the texture as it
2511 * will have decoder pixels not mean to be displayed but used for interpolation
2512 * So we lose the last line that will be partially green */
2513 if (src_width != dst_width)
2514 src_width += 1;
2515 if (src_height != dst_height)
2516 src_height += 1;
2518 float top, bottom, left, right;
2519 top = (float)MidY / (float)(MidY - 2*dst_y);
2520 bottom = -(2.f*src_height - MidY) / (float)(MidY - 2*dst_y);
2521 right = (2.f*src_width - MidX) / (float)(MidX - 2*dst_x);
2522 left = -(float)MidX / (float)(MidX - 2*dst_x);
2524 const float vertices_coords[4][2] = {
2525 { left, bottom },
2526 { right, bottom },
2527 { right, top },
2528 { left, top },
2531 /* Compute index remapping necessary to implement the rotation. */
2532 int vertex_order[4];
2533 orientationVertexOrder(orientation, vertex_order);
2535 for (int i = 0; i < 4; ++i) {
2536 dst_data[i].position.x = vertices_coords[vertex_order[i]][0];
2537 dst_data[i].position.y = vertices_coords[vertex_order[i]][1];
2540 // bottom left
2541 dst_data[0].position.z = 0.0f;
2542 dst_data[0].texture.x = 0.0f;
2543 dst_data[0].texture.y = 1.0f;
2545 // bottom right
2546 dst_data[1].position.z = 0.0f;
2547 dst_data[1].texture.x = 1.0f;
2548 dst_data[1].texture.y = 1.0f;
2550 // top right
2551 dst_data[2].position.z = 0.0f;
2552 dst_data[2].texture.x = 1.0f;
2553 dst_data[2].texture.y = 0.0f;
2555 // top left
2556 dst_data[3].position.z = 0.0f;
2557 dst_data[3].texture.x = 0.0f;
2558 dst_data[3].texture.y = 0.0f;
2560 triangle_pos[0] = 3;
2561 triangle_pos[1] = 1;
2562 triangle_pos[2] = 0;
2564 triangle_pos[3] = 2;
2565 triangle_pos[4] = 1;
2566 triangle_pos[5] = 3;
2569 #define SPHERE_SLICES 128
2570 #define nbLatBands SPHERE_SLICES
2571 #define nbLonBands SPHERE_SLICES
2573 static void SetupQuadSphere(d3d_vertex_t *dst_data, WORD *triangle_pos)
2575 for (unsigned lat = 0; lat <= nbLatBands; lat++) {
2576 float theta = lat * (float) M_PI / nbLatBands;
2577 float sinTheta, cosTheta;
2579 sincosf(theta, &sinTheta, &cosTheta);
2581 for (unsigned lon = 0; lon <= nbLonBands; lon++) {
2582 float phi = lon * 2 * (float) M_PI / nbLonBands;
2583 float sinPhi, cosPhi;
2585 sincosf(phi, &sinPhi, &cosPhi);
2587 float x = cosPhi * sinTheta;
2588 float y = cosTheta;
2589 float z = sinPhi * sinTheta;
2591 unsigned off1 = lat * (nbLonBands + 1) + lon;
2592 dst_data[off1].position.x = SPHERE_RADIUS * x;
2593 dst_data[off1].position.y = SPHERE_RADIUS * y;
2594 dst_data[off1].position.z = SPHERE_RADIUS * z;
2596 dst_data[off1].texture.x = lon / (float) nbLonBands; // 0(left) to 1(right)
2597 dst_data[off1].texture.y = lat / (float) nbLatBands; // 0(top) to 1 (bottom)
2601 for (unsigned lat = 0; lat < nbLatBands; lat++) {
2602 for (unsigned lon = 0; lon < nbLonBands; lon++) {
2603 unsigned first = (lat * (nbLonBands + 1)) + lon;
2604 unsigned second = first + nbLonBands + 1;
2606 unsigned off = (lat * nbLatBands + lon) * 3 * 2;
2608 triangle_pos[off] = first;
2609 triangle_pos[off + 1] = first + 1;
2610 triangle_pos[off + 2] = second;
2612 triangle_pos[off + 3] = second;
2613 triangle_pos[off + 4] = first + 1;
2614 triangle_pos[off + 5] = second + 1;
2619 static bool AllocQuadVertices(vout_display_t *vd, d3d_quad_t *quad,
2620 video_projection_mode_t projection)
2622 HRESULT hr;
2623 vout_display_sys_t *sys = vd->sys;
2625 if (projection == PROJECTION_MODE_RECTANGULAR)
2627 quad->vertexCount = 4;
2628 quad->indexCount = 2 * 3;
2630 else if (projection == PROJECTION_MODE_EQUIRECTANGULAR)
2632 quad->vertexCount = (SPHERE_SLICES+1) * (SPHERE_SLICES+1);
2633 quad->indexCount = nbLatBands * nbLonBands * 2 * 3;
2635 else
2637 msg_Warn(vd, "Projection mode %d not handled", projection);
2638 return false;
2641 D3D11_BUFFER_DESC bd;
2642 memset(&bd, 0, sizeof(bd));
2643 bd.Usage = D3D11_USAGE_DYNAMIC;
2644 bd.ByteWidth = sizeof(d3d_vertex_t) * quad->vertexCount;
2645 bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
2646 bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
2648 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &bd, NULL, &quad->pVertexBuffer);
2649 if(FAILED(hr)) {
2650 msg_Err(vd, "Failed to create vertex buffer. (hr=%lX)", hr);
2651 return false;
2654 /* create the index of the vertices */
2655 D3D11_BUFFER_DESC quadDesc = {
2656 .Usage = D3D11_USAGE_DYNAMIC,
2657 .ByteWidth = sizeof(WORD) * quad->indexCount,
2658 .BindFlags = D3D11_BIND_INDEX_BUFFER,
2659 .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
2662 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &quadDesc, NULL, &quad->pIndexBuffer);
2663 if(FAILED(hr)) {
2664 msg_Err(vd, "Could not create the quad indices. (hr=0x%lX)", hr);
2665 return false;
2668 return true;
2671 static bool UpdateQuadPosition( vout_display_t *vd, d3d_quad_t *quad,
2672 const RECT *output,
2673 video_projection_mode_t projection,
2674 video_orientation_t orientation )
2676 vout_display_sys_t *sys = vd->sys;
2677 HRESULT hr;
2678 D3D11_MAPPED_SUBRESOURCE mappedResource;
2680 /* create the vertices */
2681 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2682 if (FAILED(hr)) {
2683 msg_Err(vd, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
2684 return false;
2686 d3d_vertex_t *dst_data = mappedResource.pData;
2688 /* create the vertex indices */
2689 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2690 if (FAILED(hr)) {
2691 msg_Err(vd, "Failed to lock the index buffer (hr=0x%lX)", hr);
2692 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
2693 return false;
2695 WORD *triangle_pos = mappedResource.pData;
2697 if ( projection == PROJECTION_MODE_RECTANGULAR )
2698 SetupQuadFlat(dst_data, output, quad, triangle_pos, orientation);
2699 else
2700 SetupQuadSphere(dst_data, triangle_pos);
2702 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pIndexBuffer, 0);
2703 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
2705 return true;
2708 static int SetupQuad(vout_display_t *vd, const video_format_t *fmt, d3d_quad_t *quad,
2709 const RECT *output,
2710 const d3d_format_t *cfg, ID3D11PixelShader *d3dpixelShader,
2711 video_projection_mode_t projection, video_orientation_t orientation)
2713 vout_display_sys_t *sys = vd->sys;
2714 HRESULT hr;
2715 const bool RGB_shader = IsRGBShader(cfg);
2717 /* pixel shader constant buffer */
2718 PS_CONSTANT_BUFFER defaultConstants = {
2719 .Opacity = 1,
2721 static_assert((sizeof(PS_CONSTANT_BUFFER)%16)==0,"Constant buffers require 16-byte alignment");
2722 D3D11_BUFFER_DESC constantDesc = {
2723 .Usage = D3D11_USAGE_DYNAMIC,
2724 .ByteWidth = sizeof(PS_CONSTANT_BUFFER),
2725 .BindFlags = D3D11_BIND_CONSTANT_BUFFER,
2726 .CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
2728 D3D11_SUBRESOURCE_DATA constantInit = { .pSysMem = &defaultConstants };
2729 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[0]);
2730 if(FAILED(hr)) {
2731 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2732 goto error;
2735 FLOAT itu_black_level = 0.f;
2736 FLOAT itu_achromacy = 0.f;
2737 if (!RGB_shader)
2739 switch (cfg->bitsPerChannel)
2741 case 8:
2742 /* Rec. ITU-R BT.709-6 §4.6 */
2743 itu_black_level = 16.f / 255.f;
2744 itu_achromacy = 128.f / 255.f;
2745 break;
2746 case 10:
2747 /* Rec. ITU-R BT.709-6 §4.6 */
2748 itu_black_level = 64.f / 1023.f;
2749 itu_achromacy = 512.f / 1023.f;
2750 break;
2751 case 12:
2752 /* Rec. ITU-R BT.2020-2 Table 5 */
2753 itu_black_level = 256.f / 4095.f;
2754 itu_achromacy = 2048.f / 4095.f;
2755 break;
2756 default:
2757 /* unknown bitdepth, use approximation for infinite bit depth */
2758 itu_black_level = 16.f / 256.f;
2759 itu_achromacy = 128.f / 256.f;
2760 break;
2764 static const FLOAT IDENTITY_4X4[4 * 4] = {
2765 1.f, 0.f, 0.f, 0.f,
2766 0.f, 1.f, 0.f, 0.f,
2767 0.f, 0.f, 1.f, 0.f,
2768 0.f, 0.f, 0.f, 1.f,
2771 /* matrices for studio range */
2772 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion, in studio range */
2773 static const FLOAT COLORSPACE_BT601_TO_FULL[4*4] = {
2774 1.164383561643836f, 0.f, 1.596026785714286f, 0.f,
2775 1.164383561643836f, -0.391762290094914f, -0.812967647237771f, 0.f,
2776 1.164383561643836f, 2.017232142857142f, 0.f, 0.f,
2777 0.f, 0.f, 0.f, 1.f,
2779 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, in studio range */
2780 static const FLOAT COLORSPACE_BT709_TO_FULL[4*4] = {
2781 1.164383561643836f, 0.f, 1.792741071428571f, 0.f,
2782 1.164383561643836f, -0.213248614273730f, -0.532909328559444f, 0.f,
2783 1.164383561643836f, 2.112401785714286f, 0.f, 0.f,
2784 0.f, 0.f, 0.f, 1.f,
2786 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion, in studio range */
2787 static const FLOAT COLORSPACE_BT2020_TO_FULL[4*4] = {
2788 1.164383561643836f, 0.000000000000f, 1.678674107143f, 0.f,
2789 1.164383561643836f, -0.127007098661f, -0.440987687946f, 0.f,
2790 1.164383561643836f, 2.141772321429f, 0.000000000000f, 0.f,
2791 0.f, 0.f, 0.f, 1.f,
2794 PS_COLOR_TRANSFORM colorspace;
2796 memcpy(colorspace.WhitePoint, IDENTITY_4X4, sizeof(colorspace.WhitePoint));
2798 const FLOAT *ppColorspace;
2799 if (RGB_shader)
2800 ppColorspace = IDENTITY_4X4;
2801 else {
2802 switch (fmt->space){
2803 case COLOR_SPACE_BT709:
2804 ppColorspace = COLORSPACE_BT709_TO_FULL;
2805 break;
2806 case COLOR_SPACE_BT2020:
2807 ppColorspace = COLORSPACE_BT2020_TO_FULL;
2808 break;
2809 case COLOR_SPACE_BT601:
2810 ppColorspace = COLORSPACE_BT601_TO_FULL;
2811 break;
2812 default:
2813 case COLOR_SPACE_UNDEF:
2814 if( fmt->i_height > 576 )
2815 ppColorspace = COLORSPACE_BT709_TO_FULL;
2816 else
2817 ppColorspace = COLORSPACE_BT601_TO_FULL;
2818 break;
2820 /* all matrices work in studio range and output in full range */
2821 colorspace.WhitePoint[0*4 + 3] = -itu_black_level;
2822 colorspace.WhitePoint[1*4 + 3] = -itu_achromacy;
2823 colorspace.WhitePoint[2*4 + 3] = -itu_achromacy;
2826 memcpy(colorspace.Colorspace, ppColorspace, sizeof(colorspace.Colorspace));
2828 constantInit.pSysMem = &colorspace;
2830 static_assert((sizeof(PS_COLOR_TRANSFORM)%16)==0,"Constant buffers require 16-byte alignment");
2831 constantDesc.ByteWidth = sizeof(PS_COLOR_TRANSFORM);
2832 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, &constantInit, &quad->pPixelShaderConstants[1]);
2833 if(FAILED(hr)) {
2834 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2835 goto error;
2837 quad->PSConstantsCount = 2;
2839 /* vertex shader constant buffer */
2840 if ( projection == PROJECTION_MODE_EQUIRECTANGULAR )
2842 constantDesc.ByteWidth = sizeof(VS_PROJECTION_CONST);
2843 static_assert((sizeof(VS_PROJECTION_CONST)%16)==0,"Constant buffers require 16-byte alignment");
2844 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &constantDesc, NULL, &quad->pVertexShaderConstants);
2845 if(FAILED(hr)) {
2846 msg_Err(vd, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
2847 goto error;
2850 SetQuadVSProjection( vd, quad, &vd->cfg->viewpoint );
2853 quad->picSys.formatTexture = cfg->formatTexture;
2854 quad->picSys.context = sys->d3dcontext;
2855 ID3D11DeviceContext_AddRef(quad->picSys.context);
2857 if (!AllocQuadVertices(vd, quad, projection))
2858 goto error;
2859 if (!UpdateQuadPosition(vd, quad, output, projection, orientation))
2860 goto error;
2862 quad->d3dpixelShader = d3dpixelShader;
2863 if (projection == PROJECTION_MODE_RECTANGULAR)
2864 quad->d3dvertexShader = sys->flatVSShader;
2865 else
2866 quad->d3dvertexShader = sys->projectionVSShader;
2868 return VLC_SUCCESS;
2870 error:
2871 ReleaseQuad(quad);
2872 return VLC_EGENERIC;
2875 static void ReleaseQuad(d3d_quad_t *quad)
2877 if (quad->pPixelShaderConstants[0])
2879 ID3D11Buffer_Release(quad->pPixelShaderConstants[0]);
2880 quad->pPixelShaderConstants[0] = NULL;
2882 if (quad->pPixelShaderConstants[1])
2884 ID3D11Buffer_Release(quad->pPixelShaderConstants[1]);
2885 quad->pPixelShaderConstants[1] = NULL;
2887 if (quad->pVertexBuffer)
2889 ID3D11Buffer_Release(quad->pVertexBuffer);
2890 quad->pVertexBuffer = NULL;
2892 quad->d3dvertexShader = NULL;
2893 if (quad->pIndexBuffer)
2895 ID3D11Buffer_Release(quad->pIndexBuffer);
2896 quad->pIndexBuffer = NULL;
2898 if (quad->pVertexShaderConstants)
2900 ID3D11Buffer_Release(quad->pVertexShaderConstants);
2901 quad->pVertexShaderConstants = NULL;
2903 ReleasePictureSys(&quad->picSys);
2906 static void Direct3D11DestroyResources(vout_display_t *vd)
2908 vout_display_sys_t *sys = vd->sys;
2910 Direct3D11DestroyPool(vd);
2912 ReleaseQuad(&sys->picQuad);
2913 Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
2914 sys->d3dregion_count = 0;
2916 ReleasePictureSys(&sys->stagingSys);
2918 if (sys->flatVSShader)
2920 ID3D11VertexShader_Release(sys->flatVSShader);
2921 sys->flatVSShader = NULL;
2923 if (sys->projectionVSShader)
2925 ID3D11VertexShader_Release(sys->projectionVSShader);
2926 sys->projectionVSShader = NULL;
2928 if (sys->d3drenderTargetView)
2930 ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
2931 sys->d3drenderTargetView = NULL;
2933 if (sys->d3ddepthStencilView)
2935 ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
2936 sys->d3ddepthStencilView = NULL;
2938 if (sys->pSPUPixelShader)
2940 ID3D11PixelShader_Release(sys->pSPUPixelShader);
2941 sys->pSPUPixelShader = NULL;
2943 if (sys->picQuadPixelShader)
2945 ID3D11PixelShader_Release(sys->picQuadPixelShader);
2946 sys->picQuadPixelShader = NULL;
2948 #if defined(HAVE_ID3D11VIDEODECODER)
2949 if( sys->context_lock != INVALID_HANDLE_VALUE )
2951 CloseHandle( sys->context_lock );
2952 sys->context_lock = INVALID_HANDLE_VALUE;
2954 #endif
2956 msg_Dbg(vd, "Direct3D11 resources destroyed");
2959 static void Direct3D11DeleteRegions(int count, picture_t **region)
2961 for (int i = 0; i < count; ++i) {
2962 if (region[i]) {
2963 picture_Release(region[i]);
2966 free(region);
2969 static void DestroyPictureQuad(picture_t *p_picture)
2971 ReleaseQuad( (d3d_quad_t *) p_picture->p_sys );
2972 free( p_picture );
2975 static void UpdateQuadOpacity(vout_display_t *vd, const d3d_quad_t *quad, float opacity)
2977 vout_display_sys_t *sys = vd->sys;
2978 D3D11_MAPPED_SUBRESOURCE mappedResource;
2980 HRESULT hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2981 if (SUCCEEDED(hr)) {
2982 FLOAT *dst_data = mappedResource.pData;
2983 *dst_data = opacity;
2984 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pPixelShaderConstants[0], 0);
2986 else {
2987 msg_Err(vd, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr);
2991 static int Direct3D11MapSubpicture(vout_display_t *vd, int *subpicture_region_count,
2992 picture_t ***region, subpicture_t *subpicture)
2994 vout_display_sys_t *sys = vd->sys;
2995 D3D11_MAPPED_SUBRESOURCE mappedResource;
2996 D3D11_TEXTURE2D_DESC texDesc;
2997 HRESULT hr;
2998 int err;
3000 if (sys->d3dregion_format == NULL)
3001 return VLC_EGENERIC;
3003 int count = 0;
3004 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
3005 count++;
3007 *region = calloc(count, sizeof(picture_t *));
3008 if (unlikely(*region==NULL))
3009 return VLC_ENOMEM;
3010 *subpicture_region_count = count;
3012 int i = 0;
3013 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
3014 if (!r->fmt.i_width || !r->fmt.i_height)
3015 continue; // won't render anything, keep the cache for later
3017 for (int j = 0; j < sys->d3dregion_count; j++) {
3018 picture_t *cache = sys->d3dregions[j];
3019 if (cache != NULL && ((d3d_quad_t *) cache->p_sys)->picSys.texture[KNOWN_DXGI_INDEX]) {
3020 ID3D11Texture2D_GetDesc( ((d3d_quad_t *) cache->p_sys)->picSys.texture[KNOWN_DXGI_INDEX], &texDesc );
3021 if (texDesc.Format == sys->d3dregion_format->formatTexture &&
3022 texDesc.Width == r->fmt.i_visible_width &&
3023 texDesc.Height == r->fmt.i_visible_height) {
3024 (*region)[i] = cache;
3025 memset(&sys->d3dregions[j], 0, sizeof(cache)); // do not reuse this cached value
3026 break;
3031 picture_t *quad_picture = (*region)[i];
3032 if (quad_picture == NULL) {
3033 ID3D11Texture2D *textures[D3D11_MAX_SHADER_VIEW] = {0};
3034 d3d_quad_t *d3dquad = calloc(1, sizeof(*d3dquad));
3035 if (unlikely(d3dquad==NULL)) {
3036 continue;
3038 if (AllocateTextures(vd, sys->d3dregion_format, &r->fmt, 1, textures, true)) {
3039 msg_Err(vd, "Failed to allocate %dx%d texture for OSD",
3040 r->fmt.i_visible_width, r->fmt.i_visible_height);
3041 for (int i=0; i<D3D11_MAX_SHADER_VIEW; i++)
3042 if (textures[i])
3043 ID3D11Texture2D_Release(textures[i]);
3044 free(d3dquad);
3045 continue;
3048 for (unsigned plane = 0; plane < D3D11_MAX_SHADER_VIEW; plane++) {
3049 d3dquad->picSys.texture[plane] = textures[plane];
3051 if (AllocateShaderView(VLC_OBJECT(vd), sys->d3ddevice, sys->d3dregion_format,
3052 d3dquad->picSys.texture, 0,
3053 d3dquad->picSys.resourceView)) {
3054 msg_Err(vd, "Failed to create %dx%d shader view for OSD",
3055 r->fmt.i_visible_width, r->fmt.i_visible_height);
3056 free(d3dquad);
3057 continue;
3059 d3dquad->i_width = r->fmt.i_width;
3060 d3dquad->i_height = r->fmt.i_height;
3061 RECT output;
3062 output.left = r->fmt.i_x_offset;
3063 output.right = r->fmt.i_x_offset + r->fmt.i_width;
3064 output.top = r->fmt.i_y_offset;
3065 output.bottom = r->fmt.i_y_offset + r->fmt.i_height;
3067 err = SetupQuad( vd, &r->fmt, d3dquad, &output,
3068 sys->d3dregion_format, sys->pSPUPixelShader,
3069 PROJECTION_MODE_RECTANGULAR, ORIENT_NORMAL );
3070 if (err != VLC_SUCCESS) {
3071 msg_Err(vd, "Failed to create %dx%d quad for OSD",
3072 r->fmt.i_visible_width, r->fmt.i_visible_height);
3073 free(d3dquad);
3074 continue;
3076 picture_resource_t picres = {
3077 .p_sys = (picture_sys_t *) d3dquad,
3078 .pf_destroy = DestroyPictureQuad,
3080 (*region)[i] = picture_NewFromResource(&r->fmt, &picres);
3081 if ((*region)[i] == NULL) {
3082 msg_Err(vd, "Failed to create %dx%d picture for OSD",
3083 r->fmt.i_width, r->fmt.i_height);
3084 ReleaseQuad(d3dquad);
3085 continue;
3087 quad_picture = (*region)[i];
3090 hr = ID3D11DeviceContext_Map(sys->d3dcontext, ((d3d_quad_t *) quad_picture->p_sys)->picSys.resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
3091 if( SUCCEEDED(hr) ) {
3092 err = CommonUpdatePicture(quad_picture, NULL, mappedResource.pData, mappedResource.RowPitch);
3093 if (err != VLC_SUCCESS) {
3094 msg_Err(vd, "Failed to set the buffer on the SPU picture" );
3095 picture_Release(quad_picture);
3096 continue;
3099 picture_CopyPixels(quad_picture, r->p_picture);
3101 ID3D11DeviceContext_Unmap(sys->d3dcontext, ((d3d_quad_t *) quad_picture->p_sys)->picSys.resource[KNOWN_DXGI_INDEX], 0);
3102 } else {
3103 msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
3104 picture_Release(quad_picture);
3105 continue;
3108 d3d_quad_t *quad = (d3d_quad_t *) quad_picture->p_sys;
3110 quad->cropViewport.Width = (FLOAT) r->fmt.i_visible_width * RECTWidth(sys->sys.rect_dest) / subpicture->i_original_picture_width;
3111 quad->cropViewport.Height = (FLOAT) r->fmt.i_visible_height * RECTHeight(sys->sys.rect_dest) / subpicture->i_original_picture_height;
3112 quad->cropViewport.MinDepth = 0.0f;
3113 quad->cropViewport.MaxDepth = 1.0f;
3114 quad->cropViewport.TopLeftX = sys->sys.rect_dest.left + (FLOAT) r->i_x * RECTWidth(sys->sys.rect_dest) / subpicture->i_original_picture_width;
3115 quad->cropViewport.TopLeftY = sys->sys.rect_dest.top + (FLOAT) r->i_y * RECTHeight(sys->sys.rect_dest) / subpicture->i_original_picture_height;
3117 UpdateQuadOpacity(vd, quad, r->i_alpha / 255.0f );
3119 return VLC_SUCCESS;