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
26 # define _WIN32_WINNT _WIN32_WINNT_WIN7
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
48 #include <d3dcompiler.h>
50 /* avoided until we can pass ISwapchainPanel without c++/cx mode
51 # include <windows.ui.xaml.media.dxinterop.h> */
53 #include "../../video_chroma/d3d11_fmt.h"
58 # define D3DCompile(args...) sys->OurD3DCompile(args)
61 DEFINE_GUID(GUID_SWAPCHAIN_WIDTH
, 0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
62 DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT
, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
64 static int Open(vlc_object_t
*);
65 static void Close(vlc_object_t
*);
67 #define DEFAULT_BRIGHTNESS 100
68 #define DEFAULT_SRGB_BRIGHTNESS 100
69 #define MAX_PQ_BRIGHTNESS 10000
71 #define D3D11_HELP N_("Recommended video output for Windows 8 and later versions")
72 #define HW_BLENDING_TEXT N_("Use hardware blending support")
73 #define HW_BLENDING_LONGTEXT N_(\
74 "Try to use hardware acceleration for subtitle/OSD blending.")
77 set_shortname("Direct3D11")
78 set_description(N_("Direct3D11 video output"))
80 set_category(CAT_VIDEO
)
81 set_subcategory(SUBCAT_VIDEO_VOUT
)
83 add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT
, HW_BLENDING_LONGTEXT
, true)
86 add_integer("winrt-d3dcontext", 0x0, NULL
, NULL
, true); /* ID3D11DeviceContext* */
87 add_integer("winrt-swapchain", 0x0, NULL
, NULL
, true); /* IDXGISwapChain1* */
90 set_capability("vout display", 300)
91 add_shortcut("direct3d11")
92 set_callbacks(Open
, Close
)
95 /* A Quad is texture that can be displayed in a rectangle */
99 ID3D11Buffer
*pVertexBuffer
;
101 ID3D11VertexShader
*d3dvertexShader
;
102 ID3D11Buffer
*pIndexBuffer
;
104 ID3D11Buffer
*pVertexShaderConstants
;
105 ID3D11Buffer
*pPixelShaderConstants
[2];
106 UINT PSConstantsCount
;
107 ID3D11PixelShader
*d3dpixelShader
;
108 D3D11_VIEWPORT cropViewport
;
109 unsigned int i_width
;
110 unsigned int i_height
;
113 typedef enum video_color_axis
{
119 DXGI_COLOR_SPACE_TYPE dxgi
;
121 video_color_axis axis
;
122 video_color_primaries_t primaries
;
123 video_transfer_func_t transfer
;
124 video_color_space_t color
;
128 struct vout_display_sys_t
130 vout_display_sys_win32_t sys
;
132 struct { /* TODO may go in vout_display_info_t without DXGI */
133 const dxgi_color_space
*colorspace
;
134 unsigned luminance_peak
;
137 #if !VLC_WINSTORE_APP
138 HINSTANCE hdxgi_dll
; /* handle of the opened dxgi dll */
140 HINSTANCE hd3dcompiler_dll
; /* handle of the opened d3dcompiler dll */
141 pD3DCompile OurD3DCompile
;
143 #if defined(HAVE_ID3D11VIDEODECODER)
144 HANDLE context_lock
; /* D3D11 Context lock necessary
147 IDXGISwapChain1
*dxgiswapChain
; /* DXGI 1.2 swap chain */
148 IDXGISwapChain4
*dxgiswapChain4
; /* DXGI 1.5 for HDR */
149 d3d11_device_t d3d_dev
;
151 const d3d_format_t
*picQuadConfig
;
152 ID3D11PixelShader
*picQuadPixelShader
;
154 picture_sys_t stagingSys
;
156 ID3D11RenderTargetView
*d3drenderTargetView
;
157 ID3D11DepthStencilView
*d3ddepthStencilView
;
159 ID3D11VertexShader
*flatVSShader
;
160 ID3D11VertexShader
*projectionVSShader
;
162 /* copy from the decoder pool into picSquad before display
163 * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
167 vlc_fourcc_t pSubpictureChromas
[2];
168 ID3D11PixelShader
*pSPUPixelShader
;
169 const d3d_format_t
*d3dregion_format
;
171 picture_t
**d3dregions
;
174 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
175 typedef struct d3d_vertex_t
{
189 FLOAT opacityPadding
[3];
190 } PS_CONSTANT_BUFFER
;
193 FLOAT WhitePoint
[4*4];
194 FLOAT Colorspace
[4*4];
195 } PS_COLOR_TRANSFORM
;
202 FLOAT Projection
[4*4];
203 } VS_PROJECTION_CONST
;
205 #define SPHERE_RADIUS 1.f
207 #define RECTWidth(r) (int)((r).right - (r).left)
208 #define RECTHeight(r) (int)((r).bottom - (r).top)
210 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
);
212 static void Prepare(vout_display_t
*, picture_t
*, subpicture_t
*subpicture
);
213 static void Display(vout_display_t
*, picture_t
*, subpicture_t
*subpicture
);
215 static HINSTANCE
Direct3D11LoadShaderLibrary(void);
216 static void Direct3D11Destroy(vout_display_t
*);
218 static int Direct3D11Open (vout_display_t
*, video_format_t
*);
219 static void Direct3D11Close(vout_display_t
*);
221 static int Direct3D11CreateResources (vout_display_t
*, video_format_t
*);
222 static void Direct3D11DestroyResources(vout_display_t
*);
224 static void Direct3D11DestroyPool(vout_display_t
*);
226 static void DestroyDisplayPoolPicture(picture_t
*);
227 static void Direct3D11DeleteRegions(int, picture_t
**);
228 static int Direct3D11MapSubpicture(vout_display_t
*, int *, picture_t
***, subpicture_t
*);
230 static int SetupQuad(vout_display_t
*, const video_format_t
*, d3d_quad_t
*, const RECT
*,
231 const d3d_format_t
*, ID3D11PixelShader
*, video_projection_mode_t
,
232 video_orientation_t
);
233 static bool UpdateQuadPosition( vout_display_t
*vd
, d3d_quad_t
*quad
,
235 video_projection_mode_t projection
,
236 video_orientation_t orientation
);
237 static void ReleaseQuad(d3d_quad_t
*);
238 static void UpdatePicQuadPosition(vout_display_t
*);
240 static int Control(vout_display_t
*vd
, int query
, va_list args
);
241 static void Manage(vout_display_t
*vd
);
243 /* TODO: Move to a direct3d11_shaders header */
244 static const char* globVertexShaderFlat
= "\
247 float4 Position : POSITION;\
248 float4 Texture : TEXCOORD0;\
253 float4 Position : SV_POSITION;\
254 float4 Texture : TEXCOORD0;\
257 VS_OUTPUT main( VS_INPUT In )\
263 #define STRINGIZE2(s) #s
264 #define STRINGIZE(s) STRINGIZE2(s)
266 static const char* globVertexShaderProjection
= "\
267 cbuffer VS_PROJECTION_CONST : register(b0)\
273 float4x4 Projection;\
277 float4 Position : POSITION;\
278 float4 Texture : TEXCOORD0;\
283 float4 Position : SV_POSITION;\
284 float4 Texture : TEXCOORD0;\
287 VS_OUTPUT main( VS_INPUT In )\
290 float4 pos = In.Position;\
291 pos = mul(RotY, pos);\
292 pos = mul(RotX, pos);\
293 pos = mul(RotZ, pos);\
294 pos = mul(View, pos);\
295 pos = mul(Projection, pos);\
296 Output.Position = pos;\
297 Output.Texture = In.Texture;\
302 static const char* globPixelShaderDefault
= "\
303 cbuffer PS_CONSTANT_BUFFER : register(b0)\
306 float opacityPadding[3];\
308 cbuffer PS_COLOR_TRANSFORM : register(b1)\
310 float4x4 WhitePoint;\
311 float4x4 Colorspace;\
313 Texture2D%s shaderTexture[" STRINGIZE(D3D11_MAX_SHADER_VIEW
) "];\
314 SamplerState SampleType;\
318 float4 Position : SV_POSITION;\
319 float4 Texture : TEXCOORD0;\
322 /* see http://filmicworlds.com/blog/filmic-tonemapping-operators/ */\
323 inline float hable(float x) {\
324 const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;\
325 return ((x * (A*x + (C*B))+(D*E))/(x * (A*x + B) + (D*F))) - E/F;\
328 inline float3 hable(float3 x) {\
335 /* https://en.wikipedia.org/wiki/Hybrid_Log-Gamma#Technical_details */\
336 inline float inverse_HLG(float x){\
337 const float B67_a = 0.17883277;\
338 const float B67_b = 0.28466892;\
339 const float B67_c = 0.55991073;\
340 const float B67_inv_r2 = 4.0; /* 1/0.5² */\
342 x = x * x * B67_inv_r2;\
344 x = exp((x - B67_c) / B67_a) + B67_b;\
348 inline float3 sourceToLinear(float3 rgb) {\
352 inline float3 linearToDisplay(float3 rgb) {\
356 inline float3 toneMapping(float3 rgb) {\
360 inline float3 adjustPeakLuminance(float3 rgb) {\
364 inline float3 adjustRange(float3 rgb) {\
368 float4 main( PS_INPUT In ) : SV_TARGET\
372 %s /* sampling routine in sample */\
373 float4 rgba = mul(mul(sample, WhitePoint), Colorspace);\
374 float opacity = rgba.a * Opacity;\
375 float3 rgb = (float3)rgba;\
376 rgb = sourceToLinear(rgb);\
377 rgb = toneMapping(rgb);\
378 rgb = adjustPeakLuminance(rgb);\
379 rgb = linearToDisplay(rgb);\
380 rgb = adjustRange(rgb);\
381 return float4(rgb, saturate(opacity));\
385 #define ST2084_PQ_CONSTANTS "const float ST2084_m1 = 2610.0 / (4096.0 * 4);\
386 const float ST2084_m2 = (2523.0 / 4096.0) * 128.0;\
387 const float ST2084_c1 = 3424.0 / 4096.0;\
388 const float ST2084_c2 = (2413.0 / 4096.0) * 32.0;\
389 const float ST2084_c3 = (2392.0 / 4096.0) * 32.0;"
391 static int Direct3D11MapPoolTexture(picture_t
*picture
)
393 picture_sys_t
*p_sys
= picture
->p_sys
;
394 D3D11_MAPPED_SUBRESOURCE mappedResource
;
396 hr
= ID3D11DeviceContext_Map(p_sys
->context
, p_sys
->resource
[KNOWN_DXGI_INDEX
], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
401 return CommonUpdatePicture(picture
, NULL
, mappedResource
.pData
, mappedResource
.RowPitch
);
404 static void Direct3D11UnmapPoolTexture(picture_t
*picture
)
406 picture_sys_t
*p_sys
= picture
->p_sys
;
407 ID3D11DeviceContext_Unmap(p_sys
->context
, p_sys
->resource
[KNOWN_DXGI_INDEX
], 0);
410 #if !VLC_WINSTORE_APP
411 static int OpenHwnd(vout_display_t
*vd
)
413 vout_display_sys_t
*sys
= vd
->sys
= calloc(1, sizeof(vout_display_sys_t
));
417 if (D3D11_Create(vd
, &sys
->hd3d
) != VLC_SUCCESS
)
420 sys
->hd3dcompiler_dll
= Direct3D11LoadShaderLibrary();
421 if (!sys
->hd3dcompiler_dll
) {
422 msg_Err(vd
, "cannot load d3dcompiler.dll, aborting");
423 Direct3D11Destroy(vd
);
427 sys
->OurD3DCompile
= (void *)GetProcAddress(sys
->hd3dcompiler_dll
, "D3DCompile");
428 if (!sys
->OurD3DCompile
) {
429 msg_Err(vd
, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
430 Direct3D11Destroy(vd
);
436 static int OpenCoreW(vout_display_t
*vd
)
438 IDXGISwapChain1
* dxgiswapChain
= var_InheritInteger(vd
, "winrt-swapchain");
441 ID3D11DeviceContext
* d3dcontext
= var_InheritInteger(vd
, "winrt-d3dcontext");
444 ID3D11Device
* d3ddevice
= NULL
;
445 ID3D11DeviceContext_GetDevice(d3dcontext
, &d3ddevice
);
449 vout_display_sys_t
*sys
= vd
->sys
= calloc(1, sizeof(vout_display_sys_t
));
453 sys
->dxgiswapChain
= dxgiswapChain
;
454 sys
->d3d_dev
.d3ddevice
= d3ddevice
;
455 sys
->d3d_dev
.d3dcontext
= d3dcontext
;
456 IDXGISwapChain_AddRef (sys
->dxgiswapChain
);
457 ID3D11Device_AddRef (sys
->d3d_dev
.d3ddevice
);
458 ID3D11DeviceContext_AddRef(sys
->d3d_dev
.d3dcontext
);
465 static bool GetRect(const vout_display_sys_win32_t
*p_sys
, RECT
*out
)
467 const vout_display_sys_t
*sys
= (const vout_display_sys_t
*)p_sys
;
472 UINT dataSize
= sizeof(i_width
);
473 HRESULT hr
= IDXGISwapChain_GetPrivateData(sys
->dxgiswapChain
, &GUID_SWAPCHAIN_WIDTH
, &dataSize
, &i_width
);
477 dataSize
= sizeof(i_height
);
478 hr
= IDXGISwapChain_GetPrivateData(sys
->dxgiswapChain
, &GUID_SWAPCHAIN_HEIGHT
, &dataSize
, &i_height
);
482 out
->right
= i_width
;
483 out
->bottom
= i_height
;
488 static int Open(vlc_object_t
*object
)
490 vout_display_t
*vd
= (vout_display_t
*)object
;
492 #if !VLC_WINSTORE_APP
493 int ret
= OpenHwnd(vd
);
495 int ret
= OpenCoreW(vd
);
498 if (ret
!= VLC_SUCCESS
)
505 vd
->sys
->sys
.pf_GetRect
= GetRect
;
509 video_format_Copy(&fmt
, &vd
->source
);
510 if (Direct3D11Open(vd
, &fmt
)) {
511 msg_Err(vd
, "Direct3D11 could not be opened");
515 video_format_Clean(&vd
->fmt
);
518 #if !VLC_WINSTORE_APP
519 EventThreadUpdateTitle(vd
->sys
->sys
.event
, VOUT_TITLE
" (Direct3D11 output)");
521 msg_Dbg(vd
, "Direct3D11 device adapter successfully initialized");
523 vd
->info
.has_double_click
= true;
524 vd
->info
.has_pictures_invalid
= vd
->info
.is_slow
;
526 if (var_InheritBool(vd
, "direct3d11-hw-blending") &&
527 vd
->sys
->d3dregion_format
!= NULL
)
529 vd
->sys
->pSubpictureChromas
[0] = vd
->sys
->d3dregion_format
->fourcc
;
530 vd
->sys
->pSubpictureChromas
[1] = 0;
531 vd
->info
.subpicture_chromas
= vd
->sys
->pSubpictureChromas
;
534 vd
->info
.subpicture_chromas
= NULL
;
537 vd
->prepare
= Prepare
;
538 vd
->display
= Display
;
539 vd
->control
= Control
;
542 msg_Dbg(vd
, "Direct3D11 Open Succeeded");
551 static void Close(vlc_object_t
*object
)
553 vout_display_t
* vd
= (vout_display_t
*)object
;
557 Direct3D11Destroy(vd
);
561 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned pool_size
)
563 /* compensate for extra hardware decoding pulling extra pictures from our pool */
566 vout_display_sys_t
*sys
= vd
->sys
;
567 ID3D11Texture2D
*textures
[pool_size
* D3D11_MAX_SHADER_VIEW
];
568 picture_t
**pictures
= NULL
;
570 unsigned picture_count
= 0;
573 return sys
->sys
.pool
;
575 if (vd
->info
.is_slow
)
578 video_format_t surface_fmt
= vd
->fmt
;
579 surface_fmt
.i_width
= sys
->picQuad
.i_width
;
580 surface_fmt
.i_height
= sys
->picQuad
.i_height
;
582 if (!vd
->info
.is_slow
) {
584 ID3D10Multithread
*pMultithread
;
585 hr
= ID3D11Device_QueryInterface( sys
->d3d_dev
.d3ddevice
, &IID_ID3D10Multithread
, (void **)&pMultithread
);
587 ID3D10Multithread_SetMultithreadProtected(pMultithread
, TRUE
);
588 ID3D10Multithread_Release(pMultithread
);
592 if (sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_UNKNOWN
)
594 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->picQuadConfig
, &surface_fmt
, pool_size
, textures
))
597 pictures
= calloc(pool_size
, sizeof(*pictures
));
601 for (picture_count
= 0; picture_count
< pool_size
; picture_count
++) {
602 picture_sys_t
*picsys
= calloc(1, sizeof(*picsys
));
603 if (unlikely(picsys
== NULL
))
606 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++)
607 picsys
->texture
[plane
] = textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
];
609 picsys
->slice_index
= picture_count
;
610 picsys
->formatTexture
= sys
->picQuadConfig
->formatTexture
;
611 picsys
->context
= sys
->d3d_dev
.d3dcontext
;
613 picture_resource_t resource
= {
615 .pf_destroy
= DestroyDisplayPoolPicture
,
618 picture
= picture_NewFromResource(&surface_fmt
, &resource
);
619 if (unlikely(picture
== NULL
)) {
621 msg_Err( vd
, "Failed to create picture %d in the pool.", picture_count
);
625 pictures
[picture_count
] = picture
;
626 /* each picture_t holds a ref to the context and release it on Destroy */
627 ID3D11DeviceContext_AddRef(picsys
->context
);
631 #ifdef HAVE_ID3D11VIDEODECODER
632 if (!is_d3d11_opaque(surface_fmt
.i_chroma
) || sys
->legacy_shader
)
634 /* we need a staging texture */
635 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->picQuadConfig
, &surface_fmt
, 1, textures
))
638 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++)
639 sys
->stagingSys
.texture
[plane
] = textures
[plane
];
641 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->picQuadConfig
,
642 textures
, 0, sys
->stagingSys
.resourceView
))
647 for (picture_count
= 0; picture_count
< pool_size
; picture_count
++) {
648 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->picQuadConfig
,
649 pictures
[picture_count
]->p_sys
->texture
, picture_count
,
650 pictures
[picture_count
]->p_sys
->resourceView
))
655 if (SetupQuad( vd
, &surface_fmt
, &sys
->picQuad
, &sys
->sys
.rect_src_clipped
,
656 sys
->picQuadConfig
, sys
->picQuadPixelShader
,
657 surface_fmt
.projection_mode
, vd
->fmt
.orientation
) != VLC_SUCCESS
) {
658 msg_Err(vd
, "Could not Create the main quad picture.");
662 if (sys
->picQuadConfig
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
663 sys
->sys
.pool
= picture_pool_NewFromFormat( &surface_fmt
, pool_size
);
666 picture_pool_configuration_t pool_cfg
= {
668 .picture_count
= pool_size
,
670 if (vd
->info
.is_slow
) {
671 pool_cfg
.lock
= Direct3D11MapPoolTexture
;
672 //pool_cfg.unlock = Direct3D11UnmapPoolTexture;
674 sys
->sys
.pool
= picture_pool_NewExtended( &pool_cfg
);
678 if (sys
->sys
.pool
== NULL
) {
679 picture_pool_configuration_t pool_cfg
= {
683 msg_Dbg(vd
, "Failed to create the picture d3d11 pool");
684 for (unsigned i
=0;i
<picture_count
; ++i
)
685 picture_Release(pictures
[i
]);
689 /* create an empty pool to avoid crashing */
690 sys
->sys
.pool
= picture_pool_NewExtended( &pool_cfg
);
692 msg_Dbg(vd
, "D3D11 pool succeed with %d surfaces (%dx%d) context 0x%p",
693 picture_count
, surface_fmt
.i_width
, surface_fmt
.i_height
, sys
->d3d_dev
.d3dcontext
);
695 return sys
->sys
.pool
;
698 static void DestroyDisplayPoolPicture(picture_t
*picture
)
700 picture_sys_t
*p_sys
= picture
->p_sys
;
701 ReleasePictureSys( p_sys
);
706 static HRESULT
UpdateBackBuffer(vout_display_t
*vd
)
708 vout_display_sys_t
*sys
= vd
->sys
;
710 ID3D11Texture2D
* pDepthStencil
;
711 ID3D11Texture2D
* pBackBuffer
;
714 if (!GetRect(&sys
->sys
, &rect
))
716 rect
= sys
->sys
.rect_dest_clipped
;
717 uint32_t i_width
= RECTWidth(rect
);
718 uint32_t i_height
= RECTHeight(rect
);
720 if (sys
->d3drenderTargetView
) {
721 ID3D11RenderTargetView_Release(sys
->d3drenderTargetView
);
722 sys
->d3drenderTargetView
= NULL
;
724 if (sys
->d3ddepthStencilView
) {
725 ID3D11DepthStencilView_Release(sys
->d3ddepthStencilView
);
726 sys
->d3ddepthStencilView
= NULL
;
729 /* TODO detect is the size is the same as the output and switch to fullscreen mode */
730 hr
= IDXGISwapChain_ResizeBuffers(sys
->dxgiswapChain
, 0, i_width
, i_height
,
731 DXGI_FORMAT_UNKNOWN
, 0);
733 msg_Err(vd
, "Failed to resize the backbuffer. (hr=0x%lX)", hr
);
737 hr
= IDXGISwapChain_GetBuffer(sys
->dxgiswapChain
, 0, &IID_ID3D11Texture2D
, (LPVOID
*)&pBackBuffer
);
739 msg_Err(vd
, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr
);
743 hr
= ID3D11Device_CreateRenderTargetView(sys
->d3d_dev
.d3ddevice
, (ID3D11Resource
*)pBackBuffer
, NULL
, &sys
->d3drenderTargetView
);
744 ID3D11Texture2D_Release(pBackBuffer
);
746 msg_Err(vd
, "Failed to create the target view. (hr=0x%lX)", hr
);
750 D3D11_TEXTURE2D_DESC deptTexDesc
;
751 memset(&deptTexDesc
, 0,sizeof(deptTexDesc
));
752 deptTexDesc
.ArraySize
= 1;
753 deptTexDesc
.BindFlags
= D3D11_BIND_DEPTH_STENCIL
;
754 deptTexDesc
.CPUAccessFlags
= 0;
755 deptTexDesc
.Format
= DXGI_FORMAT_D24_UNORM_S8_UINT
;
756 deptTexDesc
.Width
= i_width
;
757 deptTexDesc
.Height
= i_height
;
758 deptTexDesc
.MipLevels
= 1;
759 deptTexDesc
.MiscFlags
= 0;
760 deptTexDesc
.SampleDesc
.Count
= 1;
761 deptTexDesc
.SampleDesc
.Quality
= 0;
762 deptTexDesc
.Usage
= D3D11_USAGE_DEFAULT
;
764 hr
= ID3D11Device_CreateTexture2D(sys
->d3d_dev
.d3ddevice
, &deptTexDesc
, NULL
, &pDepthStencil
);
766 msg_Err(vd
, "Could not create the depth stencil texture. (hr=0x%lX)", hr
);
770 D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc
;
771 memset(&depthViewDesc
, 0, sizeof(depthViewDesc
));
773 depthViewDesc
.Format
= deptTexDesc
.Format
;
774 depthViewDesc
.ViewDimension
= D3D11_DSV_DIMENSION_TEXTURE2D
;
775 depthViewDesc
.Texture2D
.MipSlice
= 0;
777 hr
= ID3D11Device_CreateDepthStencilView(sys
->d3d_dev
.d3ddevice
, (ID3D11Resource
*)pDepthStencil
, &depthViewDesc
, &sys
->d3ddepthStencilView
);
778 ID3D11Texture2D_Release(pDepthStencil
);
781 msg_Err(vd
, "Could not create the depth stencil view. (hr=0x%lX)", hr
);
788 /* rotation around the Z axis */
789 static void getZRotMatrix(float theta
, FLOAT matrix
[static 16])
793 sincosf(theta
, &st
, &ct
);
803 memcpy(matrix
, m
, sizeof(m
));
806 /* rotation around the Y axis */
807 static void getYRotMatrix(float theta
, FLOAT matrix
[static 16])
811 sincosf(theta
, &st
, &ct
);
821 memcpy(matrix
, m
, sizeof(m
));
824 /* rotation around the X axis */
825 static void getXRotMatrix(float phi
, FLOAT matrix
[static 16])
829 sincosf(phi
, &sp
, &cp
);
839 memcpy(matrix
, m
, sizeof(m
));
842 static void getZoomMatrix(float zoom
, FLOAT matrix
[static 16]) {
846 1.0f
, 0.0f
, 0.0f
, 0.0f
,
847 0.0f
, 1.0f
, 0.0f
, 0.0f
,
848 0.0f
, 0.0f
, 1.0f
, 0.0f
,
849 0.0f
, 0.0f
, zoom
, 1.0f
852 memcpy(matrix
, m
, sizeof(m
));
855 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
856 static void getProjectionMatrix(float sar
, float fovy
, FLOAT matrix
[static 16]) {
861 float f
= 1.f
/ tanf(fovy
/ 2.f
);
864 f
/ sar
, 0.f
, 0.f
, 0.f
,
866 0.f
, 0.f
, (zNear
+ zFar
) / (zNear
- zFar
), -1.f
,
867 0.f
, 0.f
, (2 * zNear
* zFar
) / (zNear
- zFar
), 0.f
};
869 memcpy(matrix
, m
, sizeof(m
));
872 static float UpdateFOVy(float f_fovx
, float f_sar
)
874 return 2 * atanf(tanf(f_fovx
/ 2) / f_sar
);
877 static float UpdateZ(float f_fovx
, float f_fovy
)
879 /* Do trigonometry to calculate the minimal z value
880 * that will allow us to zoom out without seeing the outside of the
881 * sphere (black borders). */
882 float tan_fovx_2
= tanf(f_fovx
/ 2);
883 float tan_fovy_2
= tanf(f_fovy
/ 2);
884 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
885 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
887 /* The FOV value above which z is dynamically calculated. */
888 const float z_thresh
= 90.f
;
891 if (f_fovx
<= z_thresh
* M_PI
/ 180)
895 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
896 f_z
= f
* f_fovx
- f
* z_thresh
* M_PI
/ 180;
903 static void SetQuadVSProjection(vout_display_t
*vd
, d3d_quad_t
*quad
, const vlc_viewpoint_t
*p_vp
)
905 if (!quad
->pVertexShaderConstants
)
908 #define RAD(d) ((float) ((d) * M_PI / 180.f))
909 float f_fovx
= RAD(p_vp
->fov
);
910 if ( f_fovx
> FIELD_OF_VIEW_DEGREES_MAX
* M_PI
/ 180 + 0.001f
||
914 float f_sar
= (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
;
915 float f_teta
= RAD(p_vp
->yaw
) - (float) M_PI_2
;
916 float f_phi
= RAD(p_vp
->pitch
);
917 float f_roll
= RAD(p_vp
->roll
);
918 float f_fovy
= UpdateFOVy(f_fovx
, f_sar
);
919 float f_z
= UpdateZ(f_fovx
, f_fovy
);
921 vout_display_sys_t
*sys
= vd
->sys
;
923 D3D11_MAPPED_SUBRESOURCE mapped
;
924 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexShaderConstants
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mapped
);
926 VS_PROJECTION_CONST
*dst_data
= mapped
.pData
;
927 getXRotMatrix(f_phi
, dst_data
->RotX
);
928 getYRotMatrix(f_teta
, dst_data
->RotY
);
929 getZRotMatrix(f_roll
, dst_data
->RotZ
);
930 getZoomMatrix(SPHERE_RADIUS
* f_z
, dst_data
->View
);
931 getProjectionMatrix(f_sar
, f_fovy
, dst_data
->Projection
);
933 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexShaderConstants
, 0);
937 static void UpdateSize(vout_display_t
*vd
)
939 vout_display_sys_t
*sys
= vd
->sys
;
940 msg_Dbg(vd
, "Detected size change %dx%d", RECTWidth(sys
->sys
.rect_dest_clipped
),
941 RECTHeight(sys
->sys
.rect_dest_clipped
));
943 UpdateBackBuffer(vd
);
945 UpdatePicQuadPosition(vd
);
947 #if defined(HAVE_ID3D11VIDEODECODER)
948 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
950 WaitForSingleObjectEx( sys
->context_lock
, INFINITE
, FALSE
);
954 UpdateQuadPosition(vd
, &sys
->picQuad
, &sys
->sys
.rect_src_clipped
,
955 vd
->fmt
.projection_mode
, vd
->fmt
.orientation
);
957 #if defined(HAVE_ID3D11VIDEODECODER)
958 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
960 ReleaseMutex( sys
->context_lock
);
965 static inline bool RectEquals(const RECT
*r1
, const RECT
*r2
)
967 return r1
->bottom
== r2
->bottom
&& r1
->top
== r2
->top
&&
968 r1
->left
== r2
->left
&& r1
->right
== r2
->right
;
971 #define BEFORE_UPDATE_RECTS \
972 unsigned int i_outside_width = vd->fmt.i_width; \
973 unsigned int i_outside_height = vd->fmt.i_height; \
974 vd->fmt.i_width = vd->sys->picQuad.i_width; \
975 vd->fmt.i_height = vd->sys->picQuad.i_height;
976 #define AFTER_UPDATE_RECTS \
977 vd->fmt.i_width = i_outside_width; \
978 vd->fmt.i_height = i_outside_height;
980 static int Control(vout_display_t
*vd
, int query
, va_list args
)
982 vout_display_sys_t
*sys
= vd
->sys
;
983 RECT before_src_clipped
= sys
->sys
.rect_src_clipped
;
984 RECT before_dest_clipped
= sys
->sys
.rect_dest_clipped
;
985 RECT before_dest
= sys
->sys
.rect_dest
;
988 int res
= CommonControl( vd
, query
, args
);
991 if (query
== VOUT_DISPLAY_CHANGE_VIEWPOINT
)
993 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
994 if ( sys
->picQuad
.pVertexShaderConstants
)
996 SetQuadVSProjection( vd
, &sys
->picQuad
, &cfg
->viewpoint
);
1001 if (!RectEquals(&before_src_clipped
, &sys
->sys
.rect_src_clipped
) ||
1002 !RectEquals(&before_dest_clipped
, &sys
->sys
.rect_dest_clipped
) ||
1003 !RectEquals(&before_dest
, &sys
->sys
.rect_dest
) )
1011 static void Manage(vout_display_t
*vd
)
1013 vout_display_sys_t
*sys
= vd
->sys
;
1014 RECT before_src_clipped
= sys
->sys
.rect_src_clipped
;
1015 RECT before_dest_clipped
= sys
->sys
.rect_dest_clipped
;
1016 RECT before_dest
= sys
->sys
.rect_dest
;
1018 BEFORE_UPDATE_RECTS
;
1022 if (!RectEquals(&before_src_clipped
, &sys
->sys
.rect_src_clipped
) ||
1023 !RectEquals(&before_dest_clipped
, &sys
->sys
.rect_dest_clipped
) ||
1024 !RectEquals(&before_dest
, &sys
->sys
.rect_dest
))
1030 static void DisplayD3DPicture(vout_display_sys_t
*sys
, d3d_quad_t
*quad
, ID3D11ShaderResourceView
*resourceView
[D3D11_MAX_SHADER_VIEW
])
1032 UINT stride
= sizeof(d3d_vertex_t
);
1035 /* Render the quad */
1037 ID3D11DeviceContext_IASetVertexBuffers(sys
->d3d_dev
.d3dcontext
, 0, 1, &quad
->pVertexBuffer
, &stride
, &offset
);
1038 ID3D11DeviceContext_IASetIndexBuffer(sys
->d3d_dev
.d3dcontext
, quad
->pIndexBuffer
, DXGI_FORMAT_R16_UINT
, 0);
1039 if ( quad
->pVertexShaderConstants
)
1040 ID3D11DeviceContext_VSSetConstantBuffers(sys
->d3d_dev
.d3dcontext
, 0, 1, &quad
->pVertexShaderConstants
);
1042 ID3D11DeviceContext_VSSetShader(sys
->d3d_dev
.d3dcontext
, quad
->d3dvertexShader
, NULL
, 0);
1045 ID3D11DeviceContext_PSSetShader(sys
->d3d_dev
.d3dcontext
, quad
->d3dpixelShader
, NULL
, 0);
1047 ID3D11DeviceContext_PSSetConstantBuffers(sys
->d3d_dev
.d3dcontext
, 0, quad
->PSConstantsCount
, quad
->pPixelShaderConstants
);
1048 ID3D11DeviceContext_PSSetShaderResources(sys
->d3d_dev
.d3dcontext
, 0, D3D11_MAX_SHADER_VIEW
, resourceView
);
1050 ID3D11DeviceContext_RSSetViewports(sys
->d3d_dev
.d3dcontext
, 1, &quad
->cropViewport
);
1052 ID3D11DeviceContext_DrawIndexed(sys
->d3d_dev
.d3dcontext
, quad
->indexCount
, 0, 0);
1055 static void Prepare(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
)
1057 vout_display_sys_t
*sys
= vd
->sys
;
1059 if (sys
->picQuadConfig
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
1061 D3D11_MAPPED_SUBRESOURCE mappedResource
;
1062 D3D11_TEXTURE2D_DESC texDesc
;
1066 for (i
= 0; i
< picture
->i_planes
; i
++) {
1067 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, sys
->stagingSys
.resource
[i
],
1068 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
1071 ID3D11Texture2D_GetDesc(sys
->stagingSys
.texture
[i
], &texDesc
);
1072 plane_t texture_plane
;
1073 texture_plane
.i_lines
= texDesc
.Height
;
1074 texture_plane
.i_pitch
= mappedResource
.RowPitch
;
1075 texture_plane
.p_pixels
= mappedResource
.pData
;
1076 texture_plane
.i_visible_lines
= picture
->p
[i
].i_visible_lines
;
1077 texture_plane
.i_visible_pitch
= picture
->p
[i
].i_visible_pitch
;
1078 plane_CopyPixels(&texture_plane
, &picture
->p
[i
]);
1079 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, sys
->stagingSys
.resource
[i
], 0);
1084 picture_sys_t
*p_sys
= ActivePictureSys(picture
);
1086 #if defined(HAVE_ID3D11VIDEODECODER)
1087 if (sys
->context_lock
!= INVALID_HANDLE_VALUE
&& is_d3d11_opaque(picture
->format
.i_chroma
))
1088 WaitForSingleObjectEx( sys
->context_lock
, INFINITE
, FALSE
);
1090 if (!is_d3d11_opaque(picture
->format
.i_chroma
) || sys
->legacy_shader
) {
1091 D3D11_TEXTURE2D_DESC texDesc
;
1092 if (!is_d3d11_opaque(picture
->format
.i_chroma
))
1093 Direct3D11UnmapPoolTexture(picture
);
1094 ID3D11Texture2D_GetDesc(sys
->stagingSys
.texture
[0], &texDesc
);
1097 .bottom
= picture
->format
.i_y_offset
+ picture
->format
.i_visible_height
,
1099 .right
= picture
->format
.i_x_offset
+ picture
->format
.i_visible_width
,
1102 if ( sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_R8G8B8A8_UNORM
&&
1103 sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_B5G6R5_UNORM
)
1105 box
.bottom
= (box
.bottom
+ 0x01) & ~0x01;
1106 box
.right
= (box
.right
+ 0x01) & ~0x01;
1108 assert(box
.right
<= texDesc
.Width
);
1109 assert(box
.bottom
<= texDesc
.Height
);
1110 ID3D11DeviceContext_CopySubresourceRegion(sys
->d3d_dev
.d3dcontext
,
1111 sys
->stagingSys
.resource
[KNOWN_DXGI_INDEX
],
1113 p_sys
->resource
[KNOWN_DXGI_INDEX
],
1114 p_sys
->slice_index
, &box
);
1118 D3D11_TEXTURE2D_DESC texDesc
;
1119 ID3D11Texture2D_GetDesc(p_sys
->texture
[0], &texDesc
);
1120 if (texDesc
.BindFlags
& D3D11_BIND_SHADER_RESOURCE
)
1122 /* for performance reason we don't want to allocate this during
1123 * display, do it preferrably when creating the texture */
1124 assert(p_sys
->resourceView
[0]!=NULL
);
1126 if ( sys
->picQuad
.i_height
!= texDesc
.Height
||
1127 sys
->picQuad
.i_width
!= texDesc
.Width
)
1129 /* the decoder produced different sizes than the vout, we need to
1130 * adjust the vertex */
1131 sys
->picQuad
.i_height
= texDesc
.Height
;
1132 sys
->picQuad
.i_width
= texDesc
.Width
;
1134 BEFORE_UPDATE_RECTS
;
1135 UpdateRects(vd
, NULL
, true);
1143 int subpicture_region_count
= 0;
1144 picture_t
**subpicture_regions
= NULL
;
1145 Direct3D11MapSubpicture(vd
, &subpicture_region_count
, &subpicture_regions
, subpicture
);
1146 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
1147 sys
->d3dregion_count
= subpicture_region_count
;
1148 sys
->d3dregions
= subpicture_regions
;
1151 FLOAT blackRGBA
[4] = {0.0f
, 0.0f
, 0.0f
, 1.0f
};
1152 ID3D11DeviceContext_ClearRenderTargetView(sys
->d3d_dev
.d3dcontext
, sys
->d3drenderTargetView
, blackRGBA
);
1154 /* no ID3D11Device operations should come here */
1156 ID3D11DeviceContext_OMSetRenderTargets(sys
->d3d_dev
.d3dcontext
, 1, &sys
->d3drenderTargetView
, sys
->d3ddepthStencilView
);
1158 ID3D11DeviceContext_ClearDepthStencilView(sys
->d3d_dev
.d3dcontext
, sys
->d3ddepthStencilView
, D3D11_CLEAR_DEPTH
| D3D11_CLEAR_STENCIL
, 1.0f
, 0);
1160 /* Render the quad */
1161 if (!is_d3d11_opaque(picture
->format
.i_chroma
) || sys
->legacy_shader
)
1162 DisplayD3DPicture(sys
, &sys
->picQuad
, sys
->stagingSys
.resourceView
);
1164 picture_sys_t
*p_sys
= ActivePictureSys(picture
);
1165 DisplayD3DPicture(sys
, &sys
->picQuad
, p_sys
->resourceView
);
1169 // draw the additional vertices
1170 for (int i
= 0; i
< sys
->d3dregion_count
; ++i
) {
1171 if (sys
->d3dregions
[i
])
1173 d3d_quad_t
*quad
= (d3d_quad_t
*) sys
->d3dregions
[i
]->p_sys
;
1174 DisplayD3DPicture(sys
, quad
, quad
->picSys
.resourceView
);
1179 #if defined(HAVE_ID3D11VIDEODECODER)
1180 if (sys
->context_lock
!= INVALID_HANDLE_VALUE
&& is_d3d11_opaque(picture
->format
.i_chroma
))
1182 ReleaseMutex( sys
->context_lock
);
1186 if (sys
->dxgiswapChain4
&& picture
->format
.mastering
.max_luminance
)
1188 DXGI_HDR_METADATA_HDR10 hdr10
= {0};
1189 hdr10
.GreenPrimary
[0] = picture
->format
.mastering
.primaries
[0];
1190 hdr10
.GreenPrimary
[1] = picture
->format
.mastering
.primaries
[1];
1191 hdr10
.BluePrimary
[0] = picture
->format
.mastering
.primaries
[2];
1192 hdr10
.BluePrimary
[1] = picture
->format
.mastering
.primaries
[3];
1193 hdr10
.RedPrimary
[0] = picture
->format
.mastering
.primaries
[4];
1194 hdr10
.RedPrimary
[1] = picture
->format
.mastering
.primaries
[5];
1195 hdr10
.WhitePoint
[0] = picture
->format
.mastering
.white_point
[0];
1196 hdr10
.WhitePoint
[1] = picture
->format
.mastering
.white_point
[1];
1197 hdr10
.MinMasteringLuminance
= picture
->format
.mastering
.min_luminance
;
1198 hdr10
.MaxMasteringLuminance
= picture
->format
.mastering
.max_luminance
;
1199 hdr10
.MaxContentLightLevel
= picture
->format
.lighting
.MaxCLL
;
1200 hdr10
.MaxFrameAverageLightLevel
= picture
->format
.lighting
.MaxFALL
;
1201 IDXGISwapChain4_SetHDRMetaData(sys
->dxgiswapChain4
, DXGI_HDR_METADATA_TYPE_HDR10
, sizeof(hdr10
), &hdr10
);
1204 ID3D11DeviceContext_Flush(sys
->d3d_dev
.d3dcontext
);
1207 static void Display(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
)
1209 vout_display_sys_t
*sys
= vd
->sys
;
1211 DXGI_PRESENT_PARAMETERS presentParams
;
1212 memset(&presentParams
, 0, sizeof(presentParams
));
1213 HRESULT hr
= IDXGISwapChain1_Present1(sys
->dxgiswapChain
, 0, 0, &presentParams
);
1214 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
1216 /* TODO device lost */
1217 msg_Dbg(vd
, "SwapChain Present failed. (hr=0x%lX)", hr
);
1220 picture_Release(picture
);
1222 subpicture_Delete(subpicture
);
1227 static void Direct3D11Destroy(vout_display_t
*vd
)
1229 #if !VLC_WINSTORE_APP
1230 vout_display_sys_t
*sys
= vd
->sys
;
1232 if (sys
->hd3dcompiler_dll
)
1233 FreeLibrary(sys
->hd3dcompiler_dll
);
1235 sys
->OurD3DCompile
= NULL
;
1236 sys
->hdxgi_dll
= NULL
;
1237 sys
->hd3dcompiler_dll
= NULL
;
1238 D3D11_Destroy( &vd
->sys
->hd3d
);
1242 #if !VLC_WINSTORE_APP
1243 static HINSTANCE
Direct3D11LoadShaderLibrary(void)
1245 HINSTANCE instance
= NULL
;
1246 /* d3dcompiler_47 is the latest on windows 8.1 */
1247 for (int i
= 47; i
> 41; --i
) {
1249 _sntprintf(filename
, 19, TEXT("D3DCOMPILER_%d.dll"), i
);
1250 instance
= LoadLibrary(filename
);
1251 if (instance
) break;
1257 #define COLOR_RANGE_FULL 1 /* 0-255 */
1258 #define COLOR_RANGE_STUDIO 0 /* 16-235 */
1260 #define TRANSFER_FUNC_10 TRANSFER_FUNC_LINEAR
1261 #define TRANSFER_FUNC_22 TRANSFER_FUNC_SRGB
1262 #define TRANSFER_FUNC_2084 TRANSFER_FUNC_SMPTE_ST2084
1264 #define COLOR_PRIMARIES_BT601 COLOR_PRIMARIES_BT601_525
1266 static const dxgi_color_space color_spaces
[] = {
1267 #define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
1268 { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
1269 #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
1270 COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
1271 COLOR_SPACE_BT##PRIMARIES, COLOR_RANGE_##RANGE},
1273 DXGIMAP(RGB
, FULL
, 22, NONE
, 709)
1274 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 601)
1275 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 601)
1276 DXGIMAP(RGB
, FULL
, 10, NONE
, 709)
1277 DXGIMAP(RGB
, STUDIO
, 22, NONE
, 709)
1278 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 709)
1279 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 709)
1280 DXGIMAP(RGB
, STUDIO
, 22, NONE
, 2020)
1281 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 2020)
1282 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 2020)
1283 DXGIMAP(YCBCR
, STUDIO
, 22, TOPLEFT
, 2020)
1284 DXGIMAP(RGB
, FULL
, 22, NONE
, 2020)
1285 DXGIMAP(RGB
, FULL
, 2084, NONE
, 2020)
1286 DXGIMAP(YCBCR
, STUDIO
, 2084, LEFT
, 2020)
1287 DXGIMAP(RGB
, STUDIO
, 2084, NONE
, 2020)
1288 DXGIMAP(YCBCR
, STUDIO
, 2084, TOPLEFT
, 2020)
1289 /*DXGIMAP(YCBCR, FULL, 22, NONE, 2020, 601)*/
1290 {DXGI_COLOR_SPACE_RESERVED
, NULL
, 0, 0, 0, 0, 0},
1294 static void D3D11SetColorSpace(vout_display_t
*vd
)
1296 vout_display_sys_t
*sys
= vd
->sys
;
1299 int score
, best_score
= 0;
1301 IDXGIOutput
*dxgiOutput
= NULL
;
1302 IDXGISwapChain3
*dxgiswapChain3
= NULL
;
1303 sys
->display
.colorspace
= &color_spaces
[0];
1305 hr
= IDXGISwapChain_QueryInterface( sys
->dxgiswapChain
, &IID_IDXGISwapChain3
, (void **)&dxgiswapChain3
);
1307 msg_Warn(vd
, "could not get a IDXGISwapChain3");
1311 bool src_full_range
= vd
->source
.b_color_range_full
||
1312 /* the YUV->RGB conversion already output full range */
1313 is_d3d11_opaque(vd
->source
.i_chroma
) ||
1314 vlc_fourcc_IsYUV(vd
->source
.i_chroma
);
1316 /* pick the best output based on color support and transfer */
1317 /* TODO support YUV output later */
1318 for (int i
=0; color_spaces
[i
].name
; ++i
)
1320 hr
= IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3
, color_spaces
[i
].dxgi
, &support
);
1321 if (SUCCEEDED(hr
) && support
) {
1322 msg_Dbg(vd
, "supports colorspace %s", color_spaces
[i
].name
);
1324 if (color_spaces
[i
].primaries
== vd
->source
.primaries
)
1326 if (color_spaces
[i
].color
== vd
->source
.space
)
1327 score
+= 2; /* we don't want to translate color spaces */
1328 if (color_spaces
[i
].transfer
== vd
->source
.transfer
||
1329 /* favor 2084 output for HLG source */
1330 (color_spaces
[i
].transfer
== TRANSFER_FUNC_SMPTE_ST2084
&& vd
->source
.transfer
== TRANSFER_FUNC_HLG
))
1332 if (color_spaces
[i
].b_full_range
== src_full_range
)
1334 if (score
> best_score
|| (score
&& best
== -1)) {
1344 msg_Warn(vd
, "no matching colorspace found force %s", color_spaces
[best
].name
);
1347 #ifdef HAVE_DXGI1_6_H
1348 if (SUCCEEDED(IDXGISwapChain_GetContainingOutput( sys
->dxgiswapChain
, &dxgiOutput
)))
1350 IDXGIOutput6
*dxgiOutput6
= NULL
;
1351 if (SUCCEEDED(IDXGIOutput_QueryInterface( dxgiOutput
, &IID_IDXGIOutput6
, (void **)&dxgiOutput6
)))
1353 DXGI_OUTPUT_DESC1 desc1
;
1354 if (SUCCEEDED(IDXGIOutput6_GetDesc1( dxgiOutput6
, &desc1
)))
1356 const dxgi_color_space
*csp
= NULL
;
1357 for (int i
=0; color_spaces
[i
].name
; ++i
)
1359 if (color_spaces
[i
].dxgi
== desc1
.ColorSpace
)
1362 csp
= &color_spaces
[i
];
1367 msg_Dbg(vd
, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1
.MaxFullFrameLuminance
, csp
?csp
->name
:"unknown", desc1
.BitsPerColor
);
1368 //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
1370 IDXGIOutput6_Release( dxgiOutput6
);
1372 IDXGIOutput_Release( dxgiOutput
);
1376 hr
= IDXGISwapChain3_SetColorSpace1(dxgiswapChain3
, color_spaces
[best
].dxgi
);
1379 sys
->display
.colorspace
= &color_spaces
[best
];
1380 msg_Dbg(vd
, "using colorspace %s", sys
->display
.colorspace
->name
);
1383 msg_Err(vd
, "Failed to set colorspace %s. (hr=0x%lX)", sys
->display
.colorspace
->name
, hr
);
1385 /* guestimate the display peak luminance */
1386 switch (sys
->display
.colorspace
->transfer
)
1388 case TRANSFER_FUNC_LINEAR
:
1389 case TRANSFER_FUNC_SRGB
:
1390 sys
->display
.luminance_peak
= DEFAULT_SRGB_BRIGHTNESS
;
1392 case TRANSFER_FUNC_SMPTE_ST2084
:
1393 sys
->display
.luminance_peak
= MAX_PQ_BRIGHTNESS
;
1395 /* there is no other output transfer on Windows */
1397 vlc_assert_unreachable();
1401 IDXGISwapChain3_Release(dxgiswapChain3
);
1404 static const d3d_format_t
*GetDirectRenderingFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1406 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
1407 if (is_d3d11_opaque(i_src_chroma
))
1408 supportFlags
|= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
1409 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, 0, is_d3d11_opaque(i_src_chroma
), supportFlags
);
1412 static const d3d_format_t
*GetDirectDecoderFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1414 UINT supportFlags
= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
1415 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, 0, is_d3d11_opaque(i_src_chroma
), supportFlags
);
1418 static const d3d_format_t
*GetDisplayFormatByDepth(vout_display_t
*vd
, uint8_t bit_depth
, bool from_processor
)
1420 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
1422 supportFlags
|= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT
;
1423 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, 0, bit_depth
, false, supportFlags
);
1426 static const d3d_format_t
*GetBlendableFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1428 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
| D3D11_FORMAT_SUPPORT_BLENDABLE
;
1429 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, 0, false, supportFlags
);
1432 static int Direct3D11Open(vout_display_t
*vd
, video_format_t
*fmt
)
1434 vout_display_sys_t
*sys
= vd
->sys
;
1435 IDXGIFactory2
*dxgifactory
;
1437 #if !VLC_WINSTORE_APP
1440 DXGI_SWAP_CHAIN_DESC1 scd
;
1441 memset(&scd
, 0, sizeof(scd
));
1442 scd
.BufferCount
= 3;
1443 scd
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1444 scd
.SampleDesc
.Count
= 1;
1445 scd
.SampleDesc
.Quality
= 0;
1446 scd
.Width
= fmt
->i_visible_width
;
1447 scd
.Height
= fmt
->i_visible_height
;
1448 switch(fmt
->i_chroma
)
1450 case VLC_CODEC_D3D11_OPAQUE_10B
:
1451 scd
.Format
= DXGI_FORMAT_R10G10B10A2_UNORM
;
1454 scd
.Format
= DXGI_FORMAT_R8G8B8A8_UNORM
; /* TODO: use DXGI_FORMAT_NV12 */
1457 //scd.Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1458 scd
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1460 hr
= D3D11_CreateDevice(vd
, &sys
->hd3d
,
1461 is_d3d11_opaque(fmt
->i_chroma
),
1464 msg_Err(vd
, "Could not Create the D3D11 device. (hr=0x%lX)", hr
);
1465 return VLC_EGENERIC
;
1468 IDXGIAdapter
*dxgiadapter
= D3D11DeviceAdapter(sys
->d3d_dev
.d3ddevice
);
1469 if (unlikely(dxgiadapter
==NULL
)) {
1470 msg_Err(vd
, "Could not get the DXGI Adapter");
1471 return VLC_EGENERIC
;
1474 hr
= IDXGIAdapter_GetParent(dxgiadapter
, &IID_IDXGIFactory2
, (void **)&dxgifactory
);
1475 IDXGIAdapter_Release(dxgiadapter
);
1477 msg_Err(vd
, "Could not get the DXGI Factory. (hr=0x%lX)", hr
);
1478 return VLC_EGENERIC
;
1481 hr
= IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory
, (IUnknown
*)sys
->d3d_dev
.d3ddevice
,
1482 sys
->sys
.hvideownd
, &scd
, NULL
, NULL
, &sys
->dxgiswapChain
);
1483 IDXGIFactory2_Release(dxgifactory
);
1485 msg_Err(vd
, "Could not create the SwapChain. (hr=0x%lX)", hr
);
1486 return VLC_EGENERIC
;
1490 IDXGISwapChain_QueryInterface( sys
->dxgiswapChain
, &IID_IDXGISwapChain4
, (void **)&sys
->dxgiswapChain4
);
1492 D3D11SetColorSpace(vd
);
1494 // look for the requested pixel format first
1495 sys
->picQuadConfig
= GetDirectRenderingFormat(vd
, fmt
->i_chroma
);
1497 /* look for a decoder format that can be decoded but not used in shaders */
1498 const d3d_format_t
*decoder_format
= NULL
;
1499 if ( !sys
->picQuadConfig
&& is_d3d11_opaque(fmt
->i_chroma
) )
1500 decoder_format
= GetDirectDecoderFormat(vd
, fmt
->i_chroma
);
1502 decoder_format
= sys
->picQuadConfig
;
1504 // look for any pixel format that we can handle with enough pixels per channel
1505 if ( !sys
->picQuadConfig
)
1507 uint8_t bits_per_channel
;
1508 switch (fmt
->i_chroma
)
1510 case VLC_CODEC_D3D11_OPAQUE
:
1511 bits_per_channel
= 8;
1513 case VLC_CODEC_D3D11_OPAQUE_10B
:
1514 bits_per_channel
= 10;
1518 const vlc_chroma_description_t
*p_format
= vlc_fourcc_GetChromaDescription(fmt
->i_chroma
);
1519 bits_per_channel
= p_format
== NULL
|| p_format
->pixel_bits
== 0 ? 8 : p_format
->pixel_bits
;
1524 sys
->picQuadConfig
= GetDisplayFormatByDepth(vd
, bits_per_channel
, decoder_format
!=NULL
);
1527 // look for any pixel format that we can handle
1528 if ( !sys
->picQuadConfig
)
1529 sys
->picQuadConfig
= GetDisplayFormatByDepth(vd
, 0, false);
1531 if ( !sys
->picQuadConfig
)
1533 msg_Err(vd
, "Could not get a suitable texture pixel format");
1534 return VLC_EGENERIC
;
1537 fmt
->i_chroma
= decoder_format
? decoder_format
->fourcc
: sys
->picQuadConfig
->fourcc
;
1539 msg_Dbg( vd
, "Using pixel format %s for chroma %4.4s", sys
->picQuadConfig
->name
,
1540 (char *)&fmt
->i_chroma
);
1541 DxgiFormatMask( sys
->picQuadConfig
->formatTexture
, fmt
);
1543 /* check the region pixel format */
1544 sys
->d3dregion_format
= GetBlendableFormat(vd
, VLC_CODEC_RGBA
);
1545 if (!sys
->d3dregion_format
)
1546 sys
->d3dregion_format
= GetBlendableFormat(vd
, VLC_CODEC_BGRA
);
1548 if (Direct3D11CreateResources(vd
, fmt
)) {
1549 msg_Err(vd
, "Failed to allocate resources");
1550 Direct3D11DestroyResources(vd
);
1551 return VLC_EGENERIC
;
1557 static void Direct3D11Close(vout_display_t
*vd
)
1559 vout_display_sys_t
*sys
= vd
->sys
;
1561 Direct3D11DestroyResources(vd
);
1562 if (sys
->dxgiswapChain4
)
1564 IDXGISwapChain_Release(sys
->dxgiswapChain4
);
1565 sys
->dxgiswapChain4
= NULL
;
1567 if (sys
->dxgiswapChain
)
1569 IDXGISwapChain_Release(sys
->dxgiswapChain
);
1570 sys
->dxgiswapChain
= NULL
;
1573 D3D11_ReleaseDevice( &sys
->d3d_dev
);
1575 msg_Dbg(vd
, "Direct3D11 device adapter closed");
1578 static void UpdatePicQuadPosition(vout_display_t
*vd
)
1580 vout_display_sys_t
*sys
= vd
->sys
;
1582 sys
->picQuad
.cropViewport
.Width
= RECTWidth(sys
->sys
.rect_dest_clipped
);
1583 sys
->picQuad
.cropViewport
.Height
= RECTHeight(sys
->sys
.rect_dest_clipped
);
1584 sys
->picQuad
.cropViewport
.TopLeftX
= sys
->sys
.rect_dest_clipped
.left
;
1585 sys
->picQuad
.cropViewport
.TopLeftY
= sys
->sys
.rect_dest_clipped
.top
;
1587 sys
->picQuad
.cropViewport
.MinDepth
= 0.0f
;
1588 sys
->picQuad
.cropViewport
.MaxDepth
= 1.0f
;
1590 SetQuadVSProjection(vd
, &sys
->picQuad
, &vd
->cfg
->viewpoint
);
1593 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
);
1597 static ID3DBlob
* CompileShader(vout_display_t
*vd
, const char *psz_shader
, bool pixel
)
1599 vout_display_sys_t
*sys
= vd
->sys
;
1600 ID3DBlob
* pShaderBlob
= NULL
, *pErrBlob
;
1602 /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1603 HRESULT hr
= D3DCompile(psz_shader
, strlen(psz_shader
),
1604 NULL
, NULL
, NULL
, "main",
1605 pixel
? (sys
->legacy_shader
? "ps_4_0_level_9_1" : "ps_4_0") :
1606 (sys
->legacy_shader
? "vs_4_0_level_9_1" : "vs_4_0"),
1607 0, 0, &pShaderBlob
, &pErrBlob
);
1610 char *err
= pErrBlob
? ID3D10Blob_GetBufferPointer(pErrBlob
) : NULL
;
1611 msg_Err(vd
, "invalid %s Shader (hr=0x%lX): %s", pixel
?"Pixel":"Vertex", hr
, err
);
1613 ID3D10Blob_Release(pErrBlob
);
1619 static bool IsRGBShader(const d3d_format_t
*cfg
)
1621 return cfg
->resourceFormat
[0] != DXGI_FORMAT_R8_UNORM
&&
1622 cfg
->resourceFormat
[0] != DXGI_FORMAT_R16_UNORM
&&
1623 cfg
->formatTexture
!= DXGI_FORMAT_YUY2
;
1626 static HRESULT
CompilePixelShader(vout_display_t
*vd
, const d3d_format_t
*format
,
1627 video_transfer_func_t transfer
, bool src_full_range
,
1628 ID3D11PixelShader
**output
)
1630 vout_display_sys_t
*sys
= vd
->sys
;
1632 static const char *DEFAULT_NOOP
= "return rgb";
1633 const char *psz_sampler
;
1634 const char *psz_src_transform
= DEFAULT_NOOP
;
1635 const char *psz_display_transform
= DEFAULT_NOOP
;
1636 const char *psz_tone_mapping
= DEFAULT_NOOP
;
1637 const char *psz_peak_luminance
= DEFAULT_NOOP
;
1638 const char *psz_adjust_range
= DEFAULT_NOOP
;
1639 char *psz_luminance
= NULL
;
1640 char *psz_range
= NULL
;
1642 switch (format
->formatTexture
)
1644 case DXGI_FORMAT_NV12
:
1645 case DXGI_FORMAT_P010
:
1647 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1648 sample.yz = shaderTexture[1].Sample(SampleType, In.Texture).xy;\
1651 case DXGI_FORMAT_YUY2
:
1653 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1654 sample.y = shaderTexture[0].Sample(SampleType, In.Texture).y;\
1655 sample.z = shaderTexture[0].Sample(SampleType, In.Texture).a;\
1658 case DXGI_FORMAT_R8G8B8A8_UNORM
:
1659 case DXGI_FORMAT_B8G8R8A8_UNORM
:
1660 case DXGI_FORMAT_B8G8R8X8_UNORM
:
1661 case DXGI_FORMAT_B5G6R5_UNORM
:
1663 "sample = shaderTexture[0].Sample(SampleType, In.Texture);";
1665 case DXGI_FORMAT_UNKNOWN
:
1667 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1668 sample.y = shaderTexture[1].Sample(SampleType, In.Texture).x;\
1669 sample.z = shaderTexture[2].Sample(SampleType, In.Texture).x;\
1673 vlc_assert_unreachable();
1676 video_transfer_func_t src_transfer
;
1677 unsigned src_luminance_peak
;
1680 case TRANSFER_FUNC_SMPTE_ST2084
:
1681 if (sys
->display
.colorspace
->transfer
== TRANSFER_FUNC_SMPTE_ST2084
)
1682 /* the display will take care of the meta data if there are some */
1683 src_luminance_peak
= MAX_PQ_BRIGHTNESS
;
1685 /* TODO adjust this value during playback using HDR metadata ? */
1686 src_luminance_peak
= 5000;
1688 case TRANSFER_FUNC_HLG
:
1689 src_luminance_peak
= 1000;
1691 case TRANSFER_FUNC_BT470_BG
:
1692 case TRANSFER_FUNC_BT470_M
:
1693 case TRANSFER_FUNC_BT709
:
1694 case TRANSFER_FUNC_SRGB
:
1695 src_luminance_peak
= DEFAULT_BRIGHTNESS
;
1698 msg_Dbg(vd
, "unhandled source transfer %d", transfer
);
1699 src_luminance_peak
= DEFAULT_BRIGHTNESS
;
1703 if (transfer
!= sys
->display
.colorspace
->transfer
)
1705 /* we need to go in linear mode */
1708 case TRANSFER_FUNC_SMPTE_ST2084
:
1709 /* ST2084 to Linear */
1712 "rgb = pow(rgb, 1.0/ST2084_m2);\
1713 rgb = max(rgb - ST2084_c1, 0.0) / (ST2084_c2 - ST2084_c3 * rgb);\
1714 rgb = pow(rgb, 1.0/ST2084_m1);\
1716 src_transfer
= TRANSFER_FUNC_LINEAR
;
1718 case TRANSFER_FUNC_HLG
:
1721 "rgb.r = inverse_HLG(rgb.r);\
1722 rgb.g = inverse_HLG(rgb.g);\
1723 rgb.b = inverse_HLG(rgb.b);\
1725 src_transfer
= TRANSFER_FUNC_LINEAR
;
1727 case TRANSFER_FUNC_BT709
:
1728 psz_src_transform
= "return pow(rgb, 1.0 / 0.45)";
1729 src_transfer
= TRANSFER_FUNC_LINEAR
;
1731 case TRANSFER_FUNC_BT470_M
:
1732 case TRANSFER_FUNC_SRGB
:
1733 psz_src_transform
= "return pow(rgb, 2.2)";
1734 src_transfer
= TRANSFER_FUNC_LINEAR
;
1736 case TRANSFER_FUNC_BT470_BG
:
1737 psz_src_transform
= "return pow(rgb, 2.8)";
1738 src_transfer
= TRANSFER_FUNC_LINEAR
;
1741 msg_Dbg(vd
, "unhandled source transfer %d", transfer
);
1742 src_transfer
= transfer
;
1746 switch (sys
->display
.colorspace
->transfer
)
1748 case TRANSFER_FUNC_SRGB
:
1749 if (src_transfer
== TRANSFER_FUNC_LINEAR
)
1751 /* Linear to sRGB */
1752 psz_display_transform
= "return pow(rgb, 1.0 / 2.2)";
1754 if (transfer
== TRANSFER_FUNC_SMPTE_ST2084
|| transfer
== TRANSFER_FUNC_HLG
)
1756 /* HDR tone mapping */
1758 "static const float3 HABLE_DIV = hable(11.2);\
1759 rgb = hable(rgb) / HABLE_DIV;\
1764 msg_Warn(vd
, "don't know how to transfer from %d to sRGB", src_transfer
);
1767 case TRANSFER_FUNC_SMPTE_ST2084
:
1768 if (src_transfer
== TRANSFER_FUNC_LINEAR
)
1770 /* Linear to ST2084 */
1771 psz_display_transform
=
1773 "rgb = pow(rgb, ST2084_m1);\
1774 rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
1775 rgb = pow(rgb, ST2084_m2);\
1779 msg_Warn(vd
, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer
);
1782 msg_Warn(vd
, "don't know how to transfer from %d to %d", src_transfer
, sys
->display
.colorspace
->transfer
);
1787 if (src_luminance_peak
!= sys
->display
.luminance_peak
)
1789 psz_luminance
= malloc(64);
1790 if (likely(psz_luminance
))
1792 sprintf(psz_luminance
, "return rgb * %f", (float)src_luminance_peak
/ (float)sys
->display
.luminance_peak
);
1793 psz_peak_luminance
= psz_luminance
;
1797 int range_adjust
= sys
->display
.colorspace
->b_full_range
;
1798 if (!IsRGBShader(format
))
1799 range_adjust
--; /* the YUV->RGB conversion already output full range */
1803 if (range_adjust
!= 0)
1805 psz_range
= malloc(256);
1806 if (likely(psz_range
))
1808 FLOAT itu_black_level
;
1809 FLOAT itu_range_factor
;
1810 FLOAT itu_white_level
;
1811 switch (format
->bitsPerChannel
)
1814 /* Rec. ITU-R BT.709-6 §4.6 */
1815 itu_black_level
= 16.f
/ 255.f
;
1816 itu_white_level
= 235.f
/ 255.f
;
1817 itu_range_factor
= (float)(235 - 16) / 255.f
;
1820 /* Rec. ITU-R BT.709-6 §4.6 */
1821 itu_black_level
= 64.f
/ 1023.f
;
1822 itu_white_level
= 940.f
/ 1023.f
;
1823 itu_range_factor
= (float)(940 - 64) / 1023.f
;
1826 /* Rec. ITU-R BT.2020-2 Table 5 */
1827 itu_black_level
= 256.f
/ 4095.f
;
1828 itu_white_level
= 3760.f
/ 4095.f
;
1829 itu_range_factor
= (float)(3760 - 256) / 4095.f
;
1832 /* unknown bitdepth, use approximation for infinite bit depth */
1833 itu_black_level
= 16.f
/ 256.f
;
1834 itu_white_level
= 235.f
/ 256.f
;
1835 itu_range_factor
= (float)(235 - 16) / 256.f
;
1839 FLOAT black_level
= 0;
1840 FLOAT range_factor
= 1.0f
;
1841 if (range_adjust
> 0)
1843 /* expand the range from studio to full range */
1844 while (range_adjust
--)
1846 black_level
-= itu_black_level
;
1847 range_factor
/= itu_range_factor
;
1849 sprintf(psz_range
, "return max(0,min(1,(rgb + %f) * %f))",
1850 black_level
, range_factor
);
1854 /* shrink the range to studio range */
1855 while (range_adjust
++)
1857 black_level
+= itu_black_level
;
1858 range_factor
*= itu_range_factor
;
1860 sprintf(psz_range
, "return clamp(rgb + %f * %f,%f,%f)",
1861 black_level
, range_factor
, itu_black_level
, itu_white_level
);
1863 psz_adjust_range
= psz_range
;
1867 char *shader
= malloc(strlen(globPixelShaderDefault
) + 32 + strlen(psz_sampler
) +
1868 strlen(psz_src_transform
) + strlen(psz_display_transform
) +
1869 strlen(psz_tone_mapping
) + strlen(psz_peak_luminance
) + strlen(psz_adjust_range
));
1872 msg_Err(vd
, "no room for the Pixel Shader");
1873 free(psz_luminance
);
1875 return E_OUTOFMEMORY
;
1877 sprintf(shader
, globPixelShaderDefault
, sys
->legacy_shader
? "" : "Array", psz_src_transform
,
1878 psz_display_transform
, psz_tone_mapping
, psz_peak_luminance
, psz_adjust_range
, psz_sampler
);
1880 if (!IsRGBShader(format
)) {
1881 msg_Dbg(vd
,"psz_src_transform %s", psz_src_transform
);
1882 msg_Dbg(vd
,"psz_tone_mapping %s", psz_tone_mapping
);
1883 msg_Dbg(vd
,"psz_peak_luminance %s", psz_peak_luminance
);
1884 msg_Dbg(vd
,"psz_display_transform %s", psz_display_transform
);
1885 msg_Dbg(vd
,"psz_adjust_range %s", psz_adjust_range
);
1888 free(psz_luminance
);
1891 ID3DBlob
*pPSBlob
= CompileShader(vd
, shader
, true);
1894 return E_INVALIDARG
;
1896 HRESULT hr
= ID3D11Device_CreatePixelShader(sys
->d3d_dev
.d3ddevice
,
1897 (void *)ID3D10Blob_GetBufferPointer(pPSBlob
),
1898 ID3D10Blob_GetBufferSize(pPSBlob
), NULL
, output
);
1900 ID3D10Blob_Release(pPSBlob
);
1904 #if (!VLC_WINSTORE_APP) && defined (HAVE_ID3D11VIDEODECODER)
1906 static HKEY
GetAdapterRegistry(DXGI_ADAPTER_DESC
*adapterDesc
)
1910 TCHAR szData
[256], lookup
[256];
1913 _sntprintf(lookup
, 256, TEXT("pci\\ven_%04x&dev_%04x&rev_cf"), adapterDesc
->VendorId
, adapterDesc
->DeviceId
);
1916 _sntprintf(key
, 128, TEXT("SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e968-e325-11ce-bfc1-08002be10318}\\%04d"), i
);
1917 if( RegOpenKeyEx(HKEY_LOCAL_MACHINE
, key
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
1920 len
= sizeof(szData
);
1921 if( RegQueryValueEx( hKey
, TEXT("MatchingDeviceId"), NULL
, NULL
, (LPBYTE
) &szData
, &len
) == ERROR_SUCCESS
) {
1922 if (_tcscmp(lookup
, szData
) == 0)
1932 static bool CanUseTextureArray(vout_display_t
*vd
)
1934 #ifndef HAVE_ID3D11VIDEODECODER
1938 vout_display_sys_t
*sys
= vd
->sys
;
1941 IDXGIAdapter
*pAdapter
= D3D11DeviceAdapter(sys
->d3d_dev
.d3ddevice
);
1945 DXGI_ADAPTER_DESC adapterDesc
;
1946 HRESULT hr
= IDXGIAdapter_GetDesc(pAdapter
, &adapterDesc
);
1947 IDXGIAdapter_Release(pAdapter
);
1951 /* ATI/AMD hardware has issues with pixel shaders with Texture2DArray */
1952 if (adapterDesc
.VendorId
!= 0x1002)
1955 #if !VLC_WINSTORE_APP
1956 HKEY hKey
= GetAdapterRegistry(&adapterDesc
);
1960 LONG err
= RegQueryValueEx( hKey
, TEXT("DriverVersion"), NULL
, NULL
, (LPBYTE
) &szData
, &len
);
1963 if (err
== ERROR_SUCCESS
)
1965 int wddm
, d3d_features
, revision
, build
;
1966 /* see https://msdn.microsoft.com/windows/hardware/commercialize/design/compatibility/device-graphics */
1967 if (_stscanf(szData
, TEXT("%d.%d.%d.%d"), &wddm
, &d3d_features
, &revision
, &build
) == 4)
1968 /* it should be OK starting from driver 22.19.162.xxx */
1969 return wddm
> 22 || (wddm
== 22 && (d3d_features
> 19 || (d3d_features
== 19 && revision
>= 162)));
1972 msg_Dbg(vd
, "fallback to legacy shader mode for old AMD drivers");
1977 /* TODO : handle errors better
1978 TODO : seperate out into smaller functions like createshaders */
1979 static int Direct3D11CreateResources(vout_display_t
*vd
, video_format_t
*fmt
)
1981 vout_display_sys_t
*sys
= vd
->sys
;
1984 #if defined(HAVE_ID3D11VIDEODECODER)
1985 sys
->context_lock
= CreateMutexEx( NULL
, NULL
, 0, SYNCHRONIZE
);
1986 ID3D11Device_SetPrivateData( sys
->d3d_dev
.d3ddevice
, &GUID_CONTEXT_MUTEX
, sizeof( sys
->context_lock
), &sys
->context_lock
);
1989 ID3D11BlendState
*pSpuBlendState
;
1990 D3D11_BLEND_DESC spuBlendDesc
= { 0 };
1991 spuBlendDesc
.RenderTarget
[0].BlendEnable
= TRUE
;
1992 spuBlendDesc
.RenderTarget
[0].SrcBlend
= D3D11_BLEND_SRC_ALPHA
;
1993 spuBlendDesc
.RenderTarget
[0].DestBlend
= D3D11_BLEND_INV_SRC_ALPHA
;
1994 spuBlendDesc
.RenderTarget
[0].BlendOp
= D3D11_BLEND_OP_ADD
;
1996 spuBlendDesc
.RenderTarget
[0].SrcBlendAlpha
= D3D11_BLEND_ONE
;
1997 spuBlendDesc
.RenderTarget
[0].DestBlendAlpha
= D3D11_BLEND_ZERO
;
1998 spuBlendDesc
.RenderTarget
[0].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
2000 spuBlendDesc
.RenderTarget
[0].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
2002 spuBlendDesc
.RenderTarget
[1].BlendEnable
= TRUE
;
2003 spuBlendDesc
.RenderTarget
[1].SrcBlend
= D3D11_BLEND_ONE
;
2004 spuBlendDesc
.RenderTarget
[1].DestBlend
= D3D11_BLEND_ZERO
;
2005 spuBlendDesc
.RenderTarget
[1].BlendOp
= D3D11_BLEND_OP_ADD
;
2007 spuBlendDesc
.RenderTarget
[1].SrcBlendAlpha
= D3D11_BLEND_ONE
;
2008 spuBlendDesc
.RenderTarget
[1].DestBlendAlpha
= D3D11_BLEND_ZERO
;
2009 spuBlendDesc
.RenderTarget
[1].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
2011 spuBlendDesc
.RenderTarget
[1].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
2012 hr
= ID3D11Device_CreateBlendState(sys
->d3d_dev
.d3ddevice
, &spuBlendDesc
, &pSpuBlendState
);
2014 msg_Err(vd
, "Could not create SPU blend state. (hr=0x%lX)", hr
);
2015 return VLC_EGENERIC
;
2017 ID3D11DeviceContext_OMSetBlendState(sys
->d3d_dev
.d3dcontext
, pSpuBlendState
, NULL
, 0xFFFFFFFF);
2018 ID3D11BlendState_Release(pSpuBlendState
);
2020 /* disable depth testing as we're only doing 2D
2021 * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
2022 * see http://rastertek.com/dx11tut11.html
2024 D3D11_DEPTH_STENCIL_DESC stencilDesc
;
2025 ZeroMemory(&stencilDesc
, sizeof(stencilDesc
));
2027 ID3D11DepthStencilState
*pDepthStencilState
;
2028 hr
= ID3D11Device_CreateDepthStencilState(sys
->d3d_dev
.d3ddevice
, &stencilDesc
, &pDepthStencilState
);
2029 if (SUCCEEDED(hr
)) {
2030 ID3D11DeviceContext_OMSetDepthStencilState(sys
->d3d_dev
.d3dcontext
, pDepthStencilState
, 0);
2031 ID3D11DepthStencilState_Release(pDepthStencilState
);
2034 sys
->legacy_shader
= !CanUseTextureArray(vd
);
2035 vd
->info
.is_slow
= !is_d3d11_opaque(fmt
->i_chroma
) && sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_UNKNOWN
;
2037 hr
= CompilePixelShader(vd
, sys
->picQuadConfig
, fmt
->transfer
, fmt
->b_color_range_full
, &sys
->picQuadPixelShader
);
2040 #ifdef HAVE_ID3D11VIDEODECODER
2041 if (!sys
->legacy_shader
)
2043 sys
->legacy_shader
= true;
2044 msg_Dbg(vd
, "fallback to legacy shader mode");
2045 hr
= CompilePixelShader(vd
, sys
->picQuadConfig
, fmt
->transfer
, fmt
->b_color_range_full
, &sys
->picQuadPixelShader
);
2050 msg_Err(vd
, "Failed to create the pixel shader. (hr=0x%lX)", hr
);
2051 return VLC_EGENERIC
;
2055 if (!is_d3d11_opaque(vd
->fmt
.i_chroma
) || sys
->legacy_shader
)
2057 sys
->picQuad
.i_width
= fmt
->i_visible_width
;
2058 sys
->picQuad
.i_height
= fmt
->i_visible_height
;
2062 sys
->picQuad
.i_width
= fmt
->i_width
;
2063 sys
->picQuad
.i_height
= fmt
->i_height
;
2065 if ( sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_R8G8B8A8_UNORM
&&
2066 sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_B5G6R5_UNORM
)
2068 sys
->picQuad
.i_width
= (sys
->picQuad
.i_width
+ 0x01) & ~0x01;
2069 sys
->picQuad
.i_height
= (sys
->picQuad
.i_height
+ 0x01) & ~0x01;
2072 BEFORE_UPDATE_RECTS
;
2073 UpdateRects(vd
, NULL
, true);
2076 hr
= UpdateBackBuffer(vd
);
2078 msg_Err(vd
, "Could not update the backbuffer. (hr=0x%lX)", hr
);
2079 return VLC_EGENERIC
;
2082 if (sys
->d3dregion_format
!= NULL
)
2084 hr
= CompilePixelShader(vd
, sys
->d3dregion_format
, TRANSFER_FUNC_SRGB
, true, &sys
->pSPUPixelShader
);
2087 ID3D11PixelShader_Release(sys
->picQuadPixelShader
);
2088 sys
->picQuadPixelShader
= NULL
;
2089 msg_Err(vd
, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr
);
2090 return VLC_EGENERIC
;
2094 ID3DBlob
*pVSBlob
= CompileShader(vd
, globVertexShaderFlat
, false);
2096 return VLC_EGENERIC
;
2098 hr
= ID3D11Device_CreateVertexShader(sys
->d3d_dev
.d3ddevice
, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2099 ID3D10Blob_GetBufferSize(pVSBlob
), NULL
, &sys
->flatVSShader
);
2102 ID3D11Device_Release(pVSBlob
);
2103 msg_Err(vd
, "Failed to create the flat vertex shader. (hr=0x%lX)", hr
);
2104 return VLC_EGENERIC
;
2107 D3D11_INPUT_ELEMENT_DESC layout
[] = {
2108 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT
, 0, D3D11_APPEND_ALIGNED_ELEMENT
, D3D11_INPUT_PER_VERTEX_DATA
, 0},
2109 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT
, 0, D3D11_APPEND_ALIGNED_ELEMENT
, D3D11_INPUT_PER_VERTEX_DATA
, 0},
2112 ID3D11InputLayout
* pVertexLayout
= NULL
;
2113 hr
= ID3D11Device_CreateInputLayout(sys
->d3d_dev
.d3ddevice
, layout
, 2, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2114 ID3D10Blob_GetBufferSize(pVSBlob
), &pVertexLayout
);
2116 ID3D10Blob_Release(pVSBlob
);
2119 msg_Err(vd
, "Failed to create the vertex input layout. (hr=0x%lX)", hr
);
2120 return VLC_EGENERIC
;
2122 ID3D11DeviceContext_IASetInputLayout(sys
->d3d_dev
.d3dcontext
, pVertexLayout
);
2123 ID3D11InputLayout_Release(pVertexLayout
);
2125 pVSBlob
= CompileShader(vd
, globVertexShaderProjection
, false);
2127 return VLC_EGENERIC
;
2129 hr
= ID3D11Device_CreateVertexShader(sys
->d3d_dev
.d3ddevice
, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2130 ID3D10Blob_GetBufferSize(pVSBlob
), NULL
, &sys
->projectionVSShader
);
2133 ID3D11Device_Release(pVSBlob
);
2134 msg_Err(vd
, "Failed to create the projection vertex shader. (hr=0x%lX)", hr
);
2135 return VLC_EGENERIC
;
2137 ID3D10Blob_Release(pVSBlob
);
2139 ID3D11DeviceContext_IASetPrimitiveTopology(sys
->d3d_dev
.d3dcontext
, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
);
2141 UpdatePicQuadPosition(vd
);
2143 D3D11_SAMPLER_DESC sampDesc
;
2144 memset(&sampDesc
, 0, sizeof(sampDesc
));
2145 sampDesc
.Filter
= D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
;
2146 sampDesc
.AddressU
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2147 sampDesc
.AddressV
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2148 sampDesc
.AddressW
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2149 sampDesc
.ComparisonFunc
= D3D11_COMPARISON_ALWAYS
;
2150 sampDesc
.MinLOD
= 0;
2151 sampDesc
.MaxLOD
= D3D11_FLOAT32_MAX
;
2153 ID3D11SamplerState
*d3dsampState
;
2154 hr
= ID3D11Device_CreateSamplerState(sys
->d3d_dev
.d3ddevice
, &sampDesc
, &d3dsampState
);
2157 msg_Err(vd
, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr
);
2158 return VLC_EGENERIC
;
2160 ID3D11DeviceContext_PSSetSamplers(sys
->d3d_dev
.d3dcontext
, 0, 1, &d3dsampState
);
2161 ID3D11SamplerState_Release(d3dsampState
);
2163 msg_Dbg(vd
, "Direct3D11 resources created");
2167 static void Direct3D11DestroyPool(vout_display_t
*vd
)
2169 vout_display_sys_t
*sys
= vd
->sys
;
2172 picture_pool_Release(sys
->sys
.pool
);
2173 sys
->sys
.pool
= NULL
;
2177 * Compute the vertex ordering needed to rotate the video. Without
2178 * rotation, the vertices of the rectangle are defined in a counterclockwise
2179 * order. This function computes a remapping of the coordinates to
2180 * implement the rotation, given fixed texture coordinates.
2181 * The unrotated order is the following:
2185 * For a 180 degrees rotation it should like this:
2189 * Vertex 0 should be assigned coordinates at index 2 from the
2190 * unrotated order and so on, thus yielding order: 2 3 0 1.
2192 static void orientationVertexOrder(video_orientation_t orientation
, int vertex_order
[static 4])
2194 switch (orientation
) {
2195 case ORIENT_ROTATED_90
:
2196 vertex_order
[0] = 3;
2197 vertex_order
[1] = 0;
2198 vertex_order
[2] = 1;
2199 vertex_order
[3] = 2;
2201 case ORIENT_ROTATED_270
:
2202 vertex_order
[0] = 1;
2203 vertex_order
[1] = 2;
2204 vertex_order
[2] = 3;
2205 vertex_order
[3] = 0;
2207 case ORIENT_ROTATED_180
:
2208 vertex_order
[0] = 2;
2209 vertex_order
[1] = 3;
2210 vertex_order
[2] = 0;
2211 vertex_order
[3] = 1;
2213 case ORIENT_TRANSPOSED
:
2214 vertex_order
[0] = 2;
2215 vertex_order
[1] = 1;
2216 vertex_order
[2] = 0;
2217 vertex_order
[3] = 3;
2219 case ORIENT_HFLIPPED
:
2220 vertex_order
[0] = 1;
2221 vertex_order
[1] = 0;
2222 vertex_order
[2] = 3;
2223 vertex_order
[3] = 2;
2225 case ORIENT_VFLIPPED
:
2226 vertex_order
[0] = 3;
2227 vertex_order
[1] = 2;
2228 vertex_order
[2] = 1;
2229 vertex_order
[3] = 0;
2231 case ORIENT_ANTI_TRANSPOSED
: /* transpose + vflip */
2232 vertex_order
[0] = 0;
2233 vertex_order
[1] = 3;
2234 vertex_order
[2] = 2;
2235 vertex_order
[3] = 1;
2238 vertex_order
[0] = 0;
2239 vertex_order
[1] = 1;
2240 vertex_order
[2] = 2;
2241 vertex_order
[3] = 3;
2246 static void SetupQuadFlat(d3d_vertex_t
*dst_data
, const RECT
*output
,
2247 const d3d_quad_t
*quad
,
2248 WORD
*triangle_pos
, video_orientation_t orientation
)
2250 unsigned int dst_x
= output
->left
;
2251 unsigned int dst_y
= output
->top
;
2252 unsigned int dst_width
= RECTWidth(*output
);
2253 unsigned int dst_height
= RECTHeight(*output
);
2254 unsigned int src_width
= quad
->i_width
;
2255 unsigned int src_height
= quad
->i_height
;
2256 LONG MidY
= output
->top
+ output
->bottom
; // /2
2257 LONG MidX
= output
->left
+ output
->right
; // /2
2259 /* the clamping will not work properly on the side of the texture as it
2260 * will have decoder pixels not mean to be displayed but used for interpolation
2261 * So we lose the last line that will be partially green */
2262 if (src_width
!= dst_width
)
2264 if (src_height
!= dst_height
)
2267 float top
, bottom
, left
, right
;
2268 top
= (float)MidY
/ (float)(MidY
- 2*dst_y
);
2269 bottom
= -(2.f
*src_height
- MidY
) / (float)(MidY
- 2*dst_y
);
2270 right
= (2.f
*src_width
- MidX
) / (float)(MidX
- 2*dst_x
);
2271 left
= -(float)MidX
/ (float)(MidX
- 2*dst_x
);
2273 const float vertices_coords
[4][2] = {
2280 /* Compute index remapping necessary to implement the rotation. */
2281 int vertex_order
[4];
2282 orientationVertexOrder(orientation
, vertex_order
);
2284 for (int i
= 0; i
< 4; ++i
) {
2285 dst_data
[i
].position
.x
= vertices_coords
[vertex_order
[i
]][0];
2286 dst_data
[i
].position
.y
= vertices_coords
[vertex_order
[i
]][1];
2290 dst_data
[0].position
.z
= 0.0f
;
2291 dst_data
[0].texture
.x
= 0.0f
;
2292 dst_data
[0].texture
.y
= 1.0f
;
2295 dst_data
[1].position
.z
= 0.0f
;
2296 dst_data
[1].texture
.x
= 1.0f
;
2297 dst_data
[1].texture
.y
= 1.0f
;
2300 dst_data
[2].position
.z
= 0.0f
;
2301 dst_data
[2].texture
.x
= 1.0f
;
2302 dst_data
[2].texture
.y
= 0.0f
;
2305 dst_data
[3].position
.z
= 0.0f
;
2306 dst_data
[3].texture
.x
= 0.0f
;
2307 dst_data
[3].texture
.y
= 0.0f
;
2309 /* Make sure surfaces are facing the right way */
2310 if( orientation
== ORIENT_TOP_RIGHT
|| orientation
== ORIENT_BOTTOM_LEFT
2311 || orientation
== ORIENT_LEFT_TOP
|| orientation
== ORIENT_RIGHT_BOTTOM
)
2313 triangle_pos
[0] = 0;
2314 triangle_pos
[1] = 1;
2315 triangle_pos
[2] = 3;
2317 triangle_pos
[3] = 3;
2318 triangle_pos
[4] = 1;
2319 triangle_pos
[5] = 2;
2323 triangle_pos
[0] = 3;
2324 triangle_pos
[1] = 1;
2325 triangle_pos
[2] = 0;
2327 triangle_pos
[3] = 2;
2328 triangle_pos
[4] = 1;
2329 triangle_pos
[5] = 3;
2333 #define SPHERE_SLICES 128
2334 #define nbLatBands SPHERE_SLICES
2335 #define nbLonBands SPHERE_SLICES
2337 static void SetupQuadSphere(d3d_vertex_t
*dst_data
, WORD
*triangle_pos
)
2339 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
2340 float theta
= lat
* (float) M_PI
/ nbLatBands
;
2341 float sinTheta
, cosTheta
;
2343 sincosf(theta
, &sinTheta
, &cosTheta
);
2345 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
2346 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
2347 float sinPhi
, cosPhi
;
2349 sincosf(phi
, &sinPhi
, &cosPhi
);
2351 float x
= cosPhi
* sinTheta
;
2353 float z
= sinPhi
* sinTheta
;
2355 unsigned off1
= lat
* (nbLonBands
+ 1) + lon
;
2356 dst_data
[off1
].position
.x
= SPHERE_RADIUS
* x
;
2357 dst_data
[off1
].position
.y
= SPHERE_RADIUS
* y
;
2358 dst_data
[off1
].position
.z
= SPHERE_RADIUS
* z
;
2360 dst_data
[off1
].texture
.x
= lon
/ (float) nbLonBands
; // 0(left) to 1(right)
2361 dst_data
[off1
].texture
.y
= lat
/ (float) nbLatBands
; // 0(top) to 1 (bottom)
2365 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
2366 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
2367 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
2368 unsigned second
= first
+ nbLonBands
+ 1;
2370 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
2372 triangle_pos
[off
] = first
;
2373 triangle_pos
[off
+ 1] = first
+ 1;
2374 triangle_pos
[off
+ 2] = second
;
2376 triangle_pos
[off
+ 3] = second
;
2377 triangle_pos
[off
+ 4] = first
+ 1;
2378 triangle_pos
[off
+ 5] = second
+ 1;
2383 static bool AllocQuadVertices(vout_display_t
*vd
, d3d_quad_t
*quad
,
2384 video_projection_mode_t projection
)
2387 vout_display_sys_t
*sys
= vd
->sys
;
2389 if (projection
== PROJECTION_MODE_RECTANGULAR
)
2391 quad
->vertexCount
= 4;
2392 quad
->indexCount
= 2 * 3;
2394 else if (projection
== PROJECTION_MODE_EQUIRECTANGULAR
)
2396 quad
->vertexCount
= (SPHERE_SLICES
+1) * (SPHERE_SLICES
+1);
2397 quad
->indexCount
= nbLatBands
* nbLonBands
* 2 * 3;
2401 msg_Warn(vd
, "Projection mode %d not handled", projection
);
2405 D3D11_BUFFER_DESC bd
;
2406 memset(&bd
, 0, sizeof(bd
));
2407 bd
.Usage
= D3D11_USAGE_DYNAMIC
;
2408 bd
.ByteWidth
= sizeof(d3d_vertex_t
) * quad
->vertexCount
;
2409 bd
.BindFlags
= D3D11_BIND_VERTEX_BUFFER
;
2410 bd
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
;
2412 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &bd
, NULL
, &quad
->pVertexBuffer
);
2414 msg_Err(vd
, "Failed to create vertex buffer. (hr=%lX)", hr
);
2418 /* create the index of the vertices */
2419 D3D11_BUFFER_DESC quadDesc
= {
2420 .Usage
= D3D11_USAGE_DYNAMIC
,
2421 .ByteWidth
= sizeof(WORD
) * quad
->indexCount
,
2422 .BindFlags
= D3D11_BIND_INDEX_BUFFER
,
2423 .CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
,
2426 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &quadDesc
, NULL
, &quad
->pIndexBuffer
);
2428 msg_Err(vd
, "Could not create the quad indices. (hr=0x%lX)", hr
);
2435 static bool UpdateQuadPosition( vout_display_t
*vd
, d3d_quad_t
*quad
,
2437 video_projection_mode_t projection
,
2438 video_orientation_t orientation
)
2440 vout_display_sys_t
*sys
= vd
->sys
;
2442 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2444 /* create the vertices */
2445 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2447 msg_Err(vd
, "Failed to lock the vertex buffer (hr=0x%lX)", hr
);
2450 d3d_vertex_t
*dst_data
= mappedResource
.pData
;
2452 /* create the vertex indices */
2453 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pIndexBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2455 msg_Err(vd
, "Failed to lock the index buffer (hr=0x%lX)", hr
);
2456 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0);
2459 WORD
*triangle_pos
= mappedResource
.pData
;
2461 if ( projection
== PROJECTION_MODE_RECTANGULAR
)
2462 SetupQuadFlat(dst_data
, output
, quad
, triangle_pos
, orientation
);
2464 SetupQuadSphere(dst_data
, triangle_pos
);
2466 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pIndexBuffer
, 0);
2467 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0);
2472 static int SetupQuad(vout_display_t
*vd
, const video_format_t
*fmt
, d3d_quad_t
*quad
,
2474 const d3d_format_t
*cfg
, ID3D11PixelShader
*d3dpixelShader
,
2475 video_projection_mode_t projection
, video_orientation_t orientation
)
2477 vout_display_sys_t
*sys
= vd
->sys
;
2479 const bool RGB_shader
= IsRGBShader(cfg
);
2481 /* pixel shader constant buffer */
2482 PS_CONSTANT_BUFFER defaultConstants
= {
2485 static_assert((sizeof(PS_CONSTANT_BUFFER
)%16)==0,"Constant buffers require 16-byte alignment");
2486 D3D11_BUFFER_DESC constantDesc
= {
2487 .Usage
= D3D11_USAGE_DYNAMIC
,
2488 .ByteWidth
= sizeof(PS_CONSTANT_BUFFER
),
2489 .BindFlags
= D3D11_BIND_CONSTANT_BUFFER
,
2490 .CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
,
2492 D3D11_SUBRESOURCE_DATA constantInit
= { .pSysMem
= &defaultConstants
};
2493 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, &constantInit
, &quad
->pPixelShaderConstants
[0]);
2495 msg_Err(vd
, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr
);
2499 FLOAT itu_black_level
= 0.f
;
2500 FLOAT itu_achromacy
= 0.f
;
2503 switch (cfg
->bitsPerChannel
)
2506 /* Rec. ITU-R BT.709-6 §4.6 */
2507 itu_black_level
= 16.f
/ 255.f
;
2508 itu_achromacy
= 128.f
/ 255.f
;
2511 /* Rec. ITU-R BT.709-6 §4.6 */
2512 itu_black_level
= 64.f
/ 1023.f
;
2513 itu_achromacy
= 512.f
/ 1023.f
;
2516 /* Rec. ITU-R BT.2020-2 Table 5 */
2517 itu_black_level
= 256.f
/ 4095.f
;
2518 itu_achromacy
= 2048.f
/ 4095.f
;
2521 /* unknown bitdepth, use approximation for infinite bit depth */
2522 itu_black_level
= 16.f
/ 256.f
;
2523 itu_achromacy
= 128.f
/ 256.f
;
2528 static const FLOAT IDENTITY_4X4
[4 * 4] = {
2535 /* matrices for studio range */
2536 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion, in studio range */
2537 static const FLOAT COLORSPACE_BT601_TO_FULL
[4*4] = {
2538 1.164383561643836f
, 0.f
, 1.596026785714286f
, 0.f
,
2539 1.164383561643836f
, -0.391762290094914f
, -0.812967647237771f
, 0.f
,
2540 1.164383561643836f
, 2.017232142857142f
, 0.f
, 0.f
,
2543 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, in studio range */
2544 static const FLOAT COLORSPACE_BT709_TO_FULL
[4*4] = {
2545 1.164383561643836f
, 0.f
, 1.792741071428571f
, 0.f
,
2546 1.164383561643836f
, -0.213248614273730f
, -0.532909328559444f
, 0.f
,
2547 1.164383561643836f
, 2.112401785714286f
, 0.f
, 0.f
,
2550 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion, in studio range */
2551 static const FLOAT COLORSPACE_BT2020_TO_FULL
[4*4] = {
2552 1.164383561643836f
, 0.000000000000f
, 1.678674107143f
, 0.f
,
2553 1.164383561643836f
, -0.127007098661f
, -0.440987687946f
, 0.f
,
2554 1.164383561643836f
, 2.141772321429f
, 0.000000000000f
, 0.f
,
2558 PS_COLOR_TRANSFORM colorspace
;
2560 memcpy(colorspace
.WhitePoint
, IDENTITY_4X4
, sizeof(colorspace
.WhitePoint
));
2562 const FLOAT
*ppColorspace
;
2564 ppColorspace
= IDENTITY_4X4
;
2566 switch (fmt
->space
){
2567 case COLOR_SPACE_BT709
:
2568 ppColorspace
= COLORSPACE_BT709_TO_FULL
;
2570 case COLOR_SPACE_BT2020
:
2571 ppColorspace
= COLORSPACE_BT2020_TO_FULL
;
2573 case COLOR_SPACE_BT601
:
2574 ppColorspace
= COLORSPACE_BT601_TO_FULL
;
2577 case COLOR_SPACE_UNDEF
:
2578 if( fmt
->i_height
> 576 )
2579 ppColorspace
= COLORSPACE_BT709_TO_FULL
;
2581 ppColorspace
= COLORSPACE_BT601_TO_FULL
;
2584 /* all matrices work in studio range and output in full range */
2585 colorspace
.WhitePoint
[0*4 + 3] = -itu_black_level
;
2586 colorspace
.WhitePoint
[1*4 + 3] = -itu_achromacy
;
2587 colorspace
.WhitePoint
[2*4 + 3] = -itu_achromacy
;
2590 memcpy(colorspace
.Colorspace
, ppColorspace
, sizeof(colorspace
.Colorspace
));
2592 constantInit
.pSysMem
= &colorspace
;
2594 static_assert((sizeof(PS_COLOR_TRANSFORM
)%16)==0,"Constant buffers require 16-byte alignment");
2595 constantDesc
.ByteWidth
= sizeof(PS_COLOR_TRANSFORM
);
2596 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, &constantInit
, &quad
->pPixelShaderConstants
[1]);
2598 msg_Err(vd
, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr
);
2601 quad
->PSConstantsCount
= 2;
2603 /* vertex shader constant buffer */
2604 if ( projection
== PROJECTION_MODE_EQUIRECTANGULAR
)
2606 constantDesc
.ByteWidth
= sizeof(VS_PROJECTION_CONST
);
2607 static_assert((sizeof(VS_PROJECTION_CONST
)%16)==0,"Constant buffers require 16-byte alignment");
2608 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, NULL
, &quad
->pVertexShaderConstants
);
2610 msg_Err(vd
, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr
);
2614 SetQuadVSProjection( vd
, quad
, &vd
->cfg
->viewpoint
);
2617 quad
->picSys
.formatTexture
= cfg
->formatTexture
;
2618 quad
->picSys
.context
= sys
->d3d_dev
.d3dcontext
;
2619 ID3D11DeviceContext_AddRef(quad
->picSys
.context
);
2621 if (!AllocQuadVertices(vd
, quad
, projection
))
2623 if (!UpdateQuadPosition(vd
, quad
, output
, projection
, orientation
))
2626 quad
->d3dpixelShader
= d3dpixelShader
;
2627 if (projection
== PROJECTION_MODE_RECTANGULAR
)
2628 quad
->d3dvertexShader
= sys
->flatVSShader
;
2630 quad
->d3dvertexShader
= sys
->projectionVSShader
;
2636 return VLC_EGENERIC
;
2639 static void ReleaseQuad(d3d_quad_t
*quad
)
2641 if (quad
->pPixelShaderConstants
[0])
2643 ID3D11Buffer_Release(quad
->pPixelShaderConstants
[0]);
2644 quad
->pPixelShaderConstants
[0] = NULL
;
2646 if (quad
->pPixelShaderConstants
[1])
2648 ID3D11Buffer_Release(quad
->pPixelShaderConstants
[1]);
2649 quad
->pPixelShaderConstants
[1] = NULL
;
2651 if (quad
->pVertexBuffer
)
2653 ID3D11Buffer_Release(quad
->pVertexBuffer
);
2654 quad
->pVertexBuffer
= NULL
;
2656 quad
->d3dvertexShader
= NULL
;
2657 if (quad
->pIndexBuffer
)
2659 ID3D11Buffer_Release(quad
->pIndexBuffer
);
2660 quad
->pIndexBuffer
= NULL
;
2662 if (quad
->pVertexShaderConstants
)
2664 ID3D11Buffer_Release(quad
->pVertexShaderConstants
);
2665 quad
->pVertexShaderConstants
= NULL
;
2667 ReleasePictureSys(&quad
->picSys
);
2670 static void Direct3D11DestroyResources(vout_display_t
*vd
)
2672 vout_display_sys_t
*sys
= vd
->sys
;
2674 Direct3D11DestroyPool(vd
);
2676 ReleaseQuad(&sys
->picQuad
);
2677 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
2678 sys
->d3dregion_count
= 0;
2680 ReleasePictureSys(&sys
->stagingSys
);
2682 if (sys
->flatVSShader
)
2684 ID3D11VertexShader_Release(sys
->flatVSShader
);
2685 sys
->flatVSShader
= NULL
;
2687 if (sys
->projectionVSShader
)
2689 ID3D11VertexShader_Release(sys
->projectionVSShader
);
2690 sys
->projectionVSShader
= NULL
;
2692 if (sys
->d3drenderTargetView
)
2694 ID3D11RenderTargetView_Release(sys
->d3drenderTargetView
);
2695 sys
->d3drenderTargetView
= NULL
;
2697 if (sys
->d3ddepthStencilView
)
2699 ID3D11DepthStencilView_Release(sys
->d3ddepthStencilView
);
2700 sys
->d3ddepthStencilView
= NULL
;
2702 if (sys
->pSPUPixelShader
)
2704 ID3D11PixelShader_Release(sys
->pSPUPixelShader
);
2705 sys
->pSPUPixelShader
= NULL
;
2707 if (sys
->picQuadPixelShader
)
2709 ID3D11PixelShader_Release(sys
->picQuadPixelShader
);
2710 sys
->picQuadPixelShader
= NULL
;
2712 #if defined(HAVE_ID3D11VIDEODECODER)
2713 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
2715 CloseHandle( sys
->context_lock
);
2716 sys
->context_lock
= INVALID_HANDLE_VALUE
;
2720 msg_Dbg(vd
, "Direct3D11 resources destroyed");
2723 static void Direct3D11DeleteRegions(int count
, picture_t
**region
)
2725 for (int i
= 0; i
< count
; ++i
) {
2727 picture_Release(region
[i
]);
2733 static void DestroyPictureQuad(picture_t
*p_picture
)
2735 ReleaseQuad( (d3d_quad_t
*) p_picture
->p_sys
);
2739 static void UpdateQuadOpacity(vout_display_t
*vd
, const d3d_quad_t
*quad
, float opacity
)
2741 vout_display_sys_t
*sys
= vd
->sys
;
2742 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2744 HRESULT hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2745 if (SUCCEEDED(hr
)) {
2746 FLOAT
*dst_data
= mappedResource
.pData
;
2747 *dst_data
= opacity
;
2748 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0);
2751 msg_Err(vd
, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr
);
2755 static int Direct3D11MapSubpicture(vout_display_t
*vd
, int *subpicture_region_count
,
2756 picture_t
***region
, subpicture_t
*subpicture
)
2758 vout_display_sys_t
*sys
= vd
->sys
;
2759 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2760 D3D11_TEXTURE2D_DESC texDesc
;
2764 if (sys
->d3dregion_format
== NULL
)
2765 return VLC_EGENERIC
;
2768 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
)
2771 *region
= calloc(count
, sizeof(picture_t
*));
2772 if (unlikely(*region
==NULL
))
2774 *subpicture_region_count
= count
;
2777 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
, i
++) {
2778 if (!r
->fmt
.i_width
|| !r
->fmt
.i_height
)
2779 continue; // won't render anything, keep the cache for later
2781 for (int j
= 0; j
< sys
->d3dregion_count
; j
++) {
2782 picture_t
*cache
= sys
->d3dregions
[j
];
2783 if (cache
!= NULL
&& ((d3d_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
]) {
2784 ID3D11Texture2D_GetDesc( ((d3d_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
], &texDesc
);
2785 if (texDesc
.Format
== sys
->d3dregion_format
->formatTexture
&&
2786 texDesc
.Width
== r
->fmt
.i_visible_width
&&
2787 texDesc
.Height
== r
->fmt
.i_visible_height
) {
2788 (*region
)[i
] = cache
;
2789 memset(&sys
->d3dregions
[j
], 0, sizeof(cache
)); // do not reuse this cached value
2795 picture_t
*quad_picture
= (*region
)[i
];
2796 if (quad_picture
== NULL
) {
2797 ID3D11Texture2D
*textures
[D3D11_MAX_SHADER_VIEW
] = {0};
2798 d3d_quad_t
*d3dquad
= calloc(1, sizeof(*d3dquad
));
2799 if (unlikely(d3dquad
==NULL
)) {
2802 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->d3dregion_format
, &r
->fmt
, 1, textures
)) {
2803 msg_Err(vd
, "Failed to allocate %dx%d texture for OSD",
2804 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2805 for (int j
=0; j
<D3D11_MAX_SHADER_VIEW
; j
++)
2807 ID3D11Texture2D_Release(textures
[j
]);
2812 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++) {
2813 d3dquad
->picSys
.texture
[plane
] = textures
[plane
];
2815 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->d3dregion_format
,
2816 d3dquad
->picSys
.texture
, 0,
2817 d3dquad
->picSys
.resourceView
)) {
2818 msg_Err(vd
, "Failed to create %dx%d shader view for OSD",
2819 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2823 d3dquad
->i_width
= r
->fmt
.i_width
;
2824 d3dquad
->i_height
= r
->fmt
.i_height
;
2826 output
.left
= r
->fmt
.i_x_offset
;
2827 output
.right
= r
->fmt
.i_x_offset
+ r
->fmt
.i_width
;
2828 output
.top
= r
->fmt
.i_y_offset
;
2829 output
.bottom
= r
->fmt
.i_y_offset
+ r
->fmt
.i_height
;
2831 err
= SetupQuad( vd
, &r
->fmt
, d3dquad
, &output
,
2832 sys
->d3dregion_format
, sys
->pSPUPixelShader
,
2833 PROJECTION_MODE_RECTANGULAR
, ORIENT_NORMAL
);
2834 if (err
!= VLC_SUCCESS
) {
2835 msg_Err(vd
, "Failed to create %dx%d quad for OSD",
2836 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2840 picture_resource_t picres
= {
2841 .p_sys
= (picture_sys_t
*) d3dquad
,
2842 .pf_destroy
= DestroyPictureQuad
,
2844 (*region
)[i
] = picture_NewFromResource(&r
->fmt
, &picres
);
2845 if ((*region
)[i
] == NULL
) {
2846 msg_Err(vd
, "Failed to create %dx%d picture for OSD",
2847 r
->fmt
.i_width
, r
->fmt
.i_height
);
2848 ReleaseQuad(d3dquad
);
2851 quad_picture
= (*region
)[i
];
2854 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, ((d3d_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2855 if( SUCCEEDED(hr
) ) {
2856 err
= CommonUpdatePicture(quad_picture
, NULL
, mappedResource
.pData
, mappedResource
.RowPitch
);
2857 if (err
!= VLC_SUCCESS
) {
2858 msg_Err(vd
, "Failed to set the buffer on the SPU picture" );
2859 picture_Release(quad_picture
);
2863 picture_CopyPixels(quad_picture
, r
->p_picture
);
2865 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, ((d3d_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0);
2867 msg_Err(vd
, "Failed to map the SPU texture (hr=0x%lX)", hr
);
2868 picture_Release(quad_picture
);
2872 d3d_quad_t
*quad
= (d3d_quad_t
*) quad_picture
->p_sys
;
2874 quad
->cropViewport
.Width
= (FLOAT
) r
->fmt
.i_visible_width
* RECTWidth(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_width
;
2875 quad
->cropViewport
.Height
= (FLOAT
) r
->fmt
.i_visible_height
* RECTHeight(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_height
;
2876 quad
->cropViewport
.MinDepth
= 0.0f
;
2877 quad
->cropViewport
.MaxDepth
= 1.0f
;
2878 quad
->cropViewport
.TopLeftX
= sys
->sys
.rect_dest
.left
+ (FLOAT
) r
->i_x
* RECTWidth(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_width
;
2879 quad
->cropViewport
.TopLeftY
= sys
->sys
.rect_dest
.top
+ (FLOAT
) r
->i_y
* RECTHeight(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_height
;
2881 UpdateQuadOpacity(vd
, quad
, r
->i_alpha
/ 255.0f
);