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
;
111 float f_luminance_scale
;
114 typedef enum video_color_axis
{
120 DXGI_COLOR_SPACE_TYPE dxgi
;
122 video_color_axis axis
;
123 video_color_primaries_t primaries
;
124 video_transfer_func_t transfer
;
125 video_color_space_t color
;
129 struct vout_display_sys_t
131 vout_display_sys_win32_t sys
;
133 struct { /* TODO may go in vout_display_info_t without DXGI */
134 const dxgi_color_space
*colorspace
;
135 unsigned luminance_peak
;
138 #if !VLC_WINSTORE_APP
139 HINSTANCE hdxgi_dll
; /* handle of the opened dxgi dll */
141 HINSTANCE hd3dcompiler_dll
; /* handle of the opened d3dcompiler dll */
142 pD3DCompile OurD3DCompile
;
144 #if defined(HAVE_ID3D11VIDEODECODER)
145 HANDLE context_lock
; /* D3D11 Context lock necessary
148 IDXGISwapChain1
*dxgiswapChain
; /* DXGI 1.2 swap chain */
149 IDXGISwapChain4
*dxgiswapChain4
; /* DXGI 1.5 for HDR */
150 d3d11_device_t d3d_dev
;
152 const d3d_format_t
*picQuadConfig
;
153 ID3D11PixelShader
*picQuadPixelShader
;
155 picture_sys_t stagingSys
;
157 ID3D11RenderTargetView
*d3drenderTargetView
;
158 ID3D11DepthStencilView
*d3ddepthStencilView
;
160 ID3D11VertexShader
*flatVSShader
;
161 ID3D11VertexShader
*projectionVSShader
;
163 /* copy from the decoder pool into picSquad before display
164 * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
168 vlc_fourcc_t pSubpictureChromas
[2];
169 ID3D11PixelShader
*pSPUPixelShader
;
170 const d3d_format_t
*d3dregion_format
;
172 picture_t
**d3dregions
;
175 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
176 typedef struct d3d_vertex_t
{
192 FLOAT LuminanceScale
;
193 } PS_CONSTANT_BUFFER
;
196 FLOAT WhitePoint
[4*4];
197 FLOAT Colorspace
[4*4];
198 } PS_COLOR_TRANSFORM
;
205 FLOAT Projection
[4*4];
206 } VS_PROJECTION_CONST
;
208 #define SPHERE_RADIUS 1.f
210 #define RECTWidth(r) (int)((r).right - (r).left)
211 #define RECTHeight(r) (int)((r).bottom - (r).top)
213 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
);
215 static void Prepare(vout_display_t
*, picture_t
*, subpicture_t
*subpicture
);
216 static void Display(vout_display_t
*, picture_t
*, subpicture_t
*subpicture
);
218 static HINSTANCE
Direct3D11LoadShaderLibrary(void);
219 static void Direct3D11Destroy(vout_display_t
*);
221 static int Direct3D11Open (vout_display_t
*);
222 static void Direct3D11Close(vout_display_t
*);
224 static int SetupOutputFormat(vout_display_t
*, video_format_t
*);
225 static int Direct3D11CreateFormatResources (vout_display_t
*, const video_format_t
*);
226 static int Direct3D11CreateGenericResources(vout_display_t
*);
227 static void Direct3D11DestroyResources(vout_display_t
*);
229 static void Direct3D11DestroyPool(vout_display_t
*);
231 static void DestroyDisplayPoolPicture(picture_t
*);
232 static void Direct3D11DeleteRegions(int, picture_t
**);
233 static int Direct3D11MapSubpicture(vout_display_t
*, int *, picture_t
***, subpicture_t
*);
235 static int SetupQuad(vout_display_t
*, const video_format_t
*, d3d_quad_t
*, const RECT
*,
236 const d3d_format_t
*, ID3D11PixelShader
*, video_projection_mode_t
,
237 video_orientation_t
);
238 static bool UpdateQuadPosition( vout_display_t
*vd
, d3d_quad_t
*quad
,
240 video_projection_mode_t projection
,
241 video_orientation_t orientation
);
242 static void ReleaseQuad(d3d_quad_t
*);
243 static void UpdatePicQuadPosition(vout_display_t
*);
245 static int Control(vout_display_t
*vd
, int query
, va_list args
);
246 static void Manage(vout_display_t
*vd
);
248 /* TODO: Move to a direct3d11_shaders header */
249 static const char* globVertexShaderFlat
= "\
252 float4 Position : POSITION;\
253 float4 Texture : TEXCOORD0;\
258 float4 Position : SV_POSITION;\
259 float4 Texture : TEXCOORD0;\
262 VS_OUTPUT main( VS_INPUT In )\
268 #define STRINGIZE2(s) #s
269 #define STRINGIZE(s) STRINGIZE2(s)
271 static const char* globVertexShaderProjection
= "\
272 cbuffer VS_PROJECTION_CONST : register(b0)\
278 float4x4 Projection;\
282 float4 Position : POSITION;\
283 float4 Texture : TEXCOORD0;\
288 float4 Position : SV_POSITION;\
289 float4 Texture : TEXCOORD0;\
292 VS_OUTPUT main( VS_INPUT In )\
295 float4 pos = In.Position;\
296 pos = mul(RotY, pos);\
297 pos = mul(RotX, pos);\
298 pos = mul(RotZ, pos);\
299 pos = mul(View, pos);\
300 pos = mul(Projection, pos);\
301 Output.Position = pos;\
302 Output.Texture = In.Texture;\
307 static const char* globPixelShaderDefault
= "\
308 cbuffer PS_CONSTANT_BUFFER : register(b0)\
313 float LuminanceScale;\
315 cbuffer PS_COLOR_TRANSFORM : register(b1)\
317 float4x4 WhitePoint;\
318 float4x4 Colorspace;\
320 Texture2D%s shaderTexture[" STRINGIZE(D3D11_MAX_SHADER_VIEW
) "];\
321 SamplerState SamplerStates[2];\
325 float4 Position : SV_POSITION;\
326 float4 Texture : TEXCOORD0;\
329 /* see http://filmicworlds.com/blog/filmic-tonemapping-operators/ */\
330 inline float hable(float x) {\
331 const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;\
332 return ((x * (A*x + (C*B))+(D*E))/(x * (A*x + B) + (D*F))) - E/F;\
335 inline float3 hable(float3 x) {\
342 /* https://en.wikipedia.org/wiki/Hybrid_Log-Gamma#Technical_details */\
343 inline float inverse_HLG(float x){\
344 const float B67_a = 0.17883277;\
345 const float B67_b = 0.28466892;\
346 const float B67_c = 0.55991073;\
347 const float B67_inv_r2 = 4.0; /* 1/0.5² */\
349 x = x * x * B67_inv_r2;\
351 x = exp((x - B67_c) / B67_a) + B67_b;\
355 inline float3 sourceToLinear(float3 rgb) {\
359 inline float3 linearToDisplay(float3 rgb) {\
363 inline float3 toneMapping(float3 rgb) {\
367 inline float3 adjustPeakLuminance(float3 rgb) {\
371 inline float3 adjustRange(float3 rgb) {\
375 inline float4 sampleTexture(SamplerState samplerState, float4 coords) {\
377 %s /* sampling routine in sample */\
381 float4 main( PS_INPUT In ) : SV_TARGET\
385 if (In.Texture.x > BoundaryX || In.Texture.y > BoundaryY) \
386 sample = sampleTexture( SamplerStates[1], In.Texture );\
388 sample = sampleTexture( SamplerStates[0], In.Texture );\
389 float4 rgba = mul(mul(sample, WhitePoint), Colorspace);\
390 float opacity = rgba.a * Opacity;\
391 float3 rgb = (float3)rgba;\
392 rgb = sourceToLinear(rgb);\
393 rgb = toneMapping(rgb);\
394 rgb = adjustPeakLuminance(rgb);\
395 rgb = linearToDisplay(rgb);\
396 rgb = adjustRange(rgb);\
397 return float4(rgb, saturate(opacity));\
401 #define ST2084_PQ_CONSTANTS "const float ST2084_m1 = 2610.0 / (4096.0 * 4);\
402 const float ST2084_m2 = (2523.0 / 4096.0) * 128.0;\
403 const float ST2084_c1 = 3424.0 / 4096.0;\
404 const float ST2084_c2 = (2413.0 / 4096.0) * 32.0;\
405 const float ST2084_c3 = (2392.0 / 4096.0) * 32.0;"
407 static int Direct3D11MapPoolTexture(picture_t
*picture
)
409 picture_sys_t
*p_sys
= picture
->p_sys
;
410 D3D11_MAPPED_SUBRESOURCE mappedResource
;
412 hr
= ID3D11DeviceContext_Map(p_sys
->context
, p_sys
->resource
[KNOWN_DXGI_INDEX
], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
417 return CommonUpdatePicture(picture
, NULL
, mappedResource
.pData
, mappedResource
.RowPitch
);
420 static void Direct3D11UnmapPoolTexture(picture_t
*picture
)
422 picture_sys_t
*p_sys
= picture
->p_sys
;
423 ID3D11DeviceContext_Unmap(p_sys
->context
, p_sys
->resource
[KNOWN_DXGI_INDEX
], 0);
426 #if !VLC_WINSTORE_APP
427 static int OpenHwnd(vout_display_t
*vd
)
429 vout_display_sys_t
*sys
= vd
->sys
= calloc(1, sizeof(vout_display_sys_t
));
433 if (D3D11_Create(vd
, &sys
->hd3d
) != VLC_SUCCESS
)
436 sys
->hd3dcompiler_dll
= Direct3D11LoadShaderLibrary();
437 if (!sys
->hd3dcompiler_dll
) {
438 msg_Err(vd
, "cannot load d3dcompiler.dll, aborting");
439 Direct3D11Destroy(vd
);
443 sys
->OurD3DCompile
= (void *)GetProcAddress(sys
->hd3dcompiler_dll
, "D3DCompile");
444 if (!sys
->OurD3DCompile
) {
445 msg_Err(vd
, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
446 Direct3D11Destroy(vd
);
452 static int OpenCoreW(vout_display_t
*vd
)
454 IDXGISwapChain1
* dxgiswapChain
= var_InheritInteger(vd
, "winrt-swapchain");
457 ID3D11DeviceContext
* d3dcontext
= var_InheritInteger(vd
, "winrt-d3dcontext");
460 ID3D11Device
* d3ddevice
= NULL
;
461 ID3D11DeviceContext_GetDevice(d3dcontext
, &d3ddevice
);
465 vout_display_sys_t
*sys
= vd
->sys
= calloc(1, sizeof(vout_display_sys_t
));
469 sys
->dxgiswapChain
= dxgiswapChain
;
470 sys
->d3d_dev
.d3ddevice
= d3ddevice
;
471 sys
->d3d_dev
.d3dcontext
= d3dcontext
;
472 IDXGISwapChain_AddRef (sys
->dxgiswapChain
);
473 ID3D11Device_AddRef (sys
->d3d_dev
.d3ddevice
);
474 ID3D11DeviceContext_AddRef(sys
->d3d_dev
.d3dcontext
);
481 static bool GetRect(const vout_display_sys_win32_t
*p_sys
, RECT
*out
)
483 const vout_display_sys_t
*sys
= (const vout_display_sys_t
*)p_sys
;
488 UINT dataSize
= sizeof(i_width
);
489 HRESULT hr
= IDXGISwapChain_GetPrivateData(sys
->dxgiswapChain
, &GUID_SWAPCHAIN_WIDTH
, &dataSize
, &i_width
);
493 dataSize
= sizeof(i_height
);
494 hr
= IDXGISwapChain_GetPrivateData(sys
->dxgiswapChain
, &GUID_SWAPCHAIN_HEIGHT
, &dataSize
, &i_height
);
498 out
->right
= i_width
;
499 out
->bottom
= i_height
;
504 static int Open(vlc_object_t
*object
)
506 vout_display_t
*vd
= (vout_display_t
*)object
;
508 #if !VLC_WINSTORE_APP
509 int ret
= OpenHwnd(vd
);
511 int ret
= OpenCoreW(vd
);
514 if (ret
!= VLC_SUCCESS
)
521 vd
->sys
->sys
.pf_GetRect
= GetRect
;
524 if (Direct3D11Open(vd
)) {
525 msg_Err(vd
, "Direct3D11 could not be opened");
529 #if !VLC_WINSTORE_APP
530 EventThreadUpdateTitle(vd
->sys
->sys
.event
, VOUT_TITLE
" (Direct3D11 output)");
532 msg_Dbg(vd
, "Direct3D11 device adapter successfully initialized");
534 vd
->info
.has_double_click
= true;
535 vd
->info
.has_pictures_invalid
= vd
->info
.is_slow
;
537 if (var_InheritBool(vd
, "direct3d11-hw-blending") &&
538 vd
->sys
->d3dregion_format
!= NULL
)
540 vd
->sys
->pSubpictureChromas
[0] = vd
->sys
->d3dregion_format
->fourcc
;
541 vd
->sys
->pSubpictureChromas
[1] = 0;
542 vd
->info
.subpicture_chromas
= vd
->sys
->pSubpictureChromas
;
545 vd
->info
.subpicture_chromas
= NULL
;
548 vd
->prepare
= Prepare
;
549 vd
->display
= Display
;
550 vd
->control
= Control
;
553 msg_Dbg(vd
, "Direct3D11 Open Succeeded");
562 static void Close(vlc_object_t
*object
)
564 vout_display_t
* vd
= (vout_display_t
*)object
;
568 Direct3D11Destroy(vd
);
572 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned pool_size
)
574 /* compensate for extra hardware decoding pulling extra pictures from our pool */
577 vout_display_sys_t
*sys
= vd
->sys
;
578 ID3D11Texture2D
*textures
[pool_size
* D3D11_MAX_SHADER_VIEW
];
579 picture_t
**pictures
= NULL
;
581 unsigned picture_count
= 0;
584 return sys
->sys
.pool
;
586 if (vd
->info
.is_slow
)
589 video_format_t surface_fmt
= vd
->fmt
;
590 surface_fmt
.i_width
= sys
->picQuad
.i_width
;
591 surface_fmt
.i_height
= sys
->picQuad
.i_height
;
593 if (!vd
->info
.is_slow
) {
595 ID3D10Multithread
*pMultithread
;
596 hr
= ID3D11Device_QueryInterface( sys
->d3d_dev
.d3ddevice
, &IID_ID3D10Multithread
, (void **)&pMultithread
);
598 ID3D10Multithread_SetMultithreadProtected(pMultithread
, TRUE
);
599 ID3D10Multithread_Release(pMultithread
);
603 if (sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_UNKNOWN
)
605 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->picQuadConfig
, &surface_fmt
, pool_size
, textures
))
608 pictures
= calloc(pool_size
, sizeof(*pictures
));
612 for (picture_count
= 0; picture_count
< pool_size
; picture_count
++) {
613 picture_sys_t
*picsys
= calloc(1, sizeof(*picsys
));
614 if (unlikely(picsys
== NULL
))
617 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++)
618 picsys
->texture
[plane
] = textures
[picture_count
* D3D11_MAX_SHADER_VIEW
+ plane
];
620 picsys
->slice_index
= picture_count
;
621 picsys
->formatTexture
= sys
->picQuadConfig
->formatTexture
;
622 picsys
->context
= sys
->d3d_dev
.d3dcontext
;
624 picture_resource_t resource
= {
626 .pf_destroy
= DestroyDisplayPoolPicture
,
629 picture
= picture_NewFromResource(&surface_fmt
, &resource
);
630 if (unlikely(picture
== NULL
)) {
632 msg_Err( vd
, "Failed to create picture %d in the pool.", picture_count
);
636 pictures
[picture_count
] = picture
;
637 /* each picture_t holds a ref to the context and release it on Destroy */
638 ID3D11DeviceContext_AddRef(picsys
->context
);
642 #ifdef HAVE_ID3D11VIDEODECODER
643 if (is_d3d11_opaque(surface_fmt
.i_chroma
) && !sys
->legacy_shader
)
646 for (picture_count
= 0; picture_count
< pool_size
; picture_count
++) {
647 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->picQuadConfig
,
648 pictures
[picture_count
]->p_sys
->texture
, picture_count
,
649 pictures
[picture_count
]->p_sys
->resourceView
))
654 if (SetupQuad( vd
, &surface_fmt
, &sys
->picQuad
, &sys
->sys
.rect_src_clipped
,
655 sys
->picQuadConfig
, sys
->picQuadPixelShader
,
656 surface_fmt
.projection_mode
, vd
->fmt
.orientation
) != VLC_SUCCESS
) {
657 msg_Err(vd
, "Could not Create the main quad picture.");
661 if (sys
->picQuadConfig
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
662 sys
->sys
.pool
= picture_pool_NewFromFormat( &surface_fmt
, pool_size
);
665 picture_pool_configuration_t pool_cfg
= {
667 .picture_count
= pool_size
,
669 if (vd
->info
.is_slow
) {
670 pool_cfg
.lock
= Direct3D11MapPoolTexture
;
671 //pool_cfg.unlock = Direct3D11UnmapPoolTexture;
673 sys
->sys
.pool
= picture_pool_NewExtended( &pool_cfg
);
677 if (sys
->sys
.pool
== NULL
) {
678 picture_pool_configuration_t pool_cfg
= {
682 msg_Dbg(vd
, "Failed to create the picture d3d11 pool");
683 for (unsigned i
=0;i
<picture_count
; ++i
)
684 picture_Release(pictures
[i
]);
688 /* create an empty pool to avoid crashing */
689 sys
->sys
.pool
= picture_pool_NewExtended( &pool_cfg
);
691 msg_Dbg(vd
, "D3D11 pool succeed with %d surfaces (%dx%d) context 0x%p",
692 pool_size
, surface_fmt
.i_width
, surface_fmt
.i_height
, sys
->d3d_dev
.d3dcontext
);
694 return sys
->sys
.pool
;
697 static void DestroyDisplayPoolPicture(picture_t
*picture
)
699 picture_sys_t
*p_sys
= picture
->p_sys
;
700 ReleasePictureSys( p_sys
);
705 static HRESULT
UpdateBackBuffer(vout_display_t
*vd
)
707 vout_display_sys_t
*sys
= vd
->sys
;
709 ID3D11Texture2D
* pDepthStencil
;
710 ID3D11Texture2D
* pBackBuffer
;
713 if (!GetRect(&sys
->sys
, &rect
))
715 rect
= sys
->sys
.rect_dest_clipped
;
716 uint32_t i_width
= RECTWidth(rect
);
717 uint32_t i_height
= RECTHeight(rect
);
718 D3D11_TEXTURE2D_DESC dsc
= { 0 };
720 if (sys
->d3drenderTargetView
) {
721 ID3D11Resource
*res
= NULL
;
722 ID3D11RenderTargetView_GetResource(sys
->d3drenderTargetView
, &res
);
725 ID3D11Texture2D_GetDesc((ID3D11Texture2D
*) res
, &dsc
);
726 ID3D11Resource_Release(res
);
730 if (dsc
.Width
== i_width
&& dsc
.Height
== i_height
)
731 return S_OK
; /* nothing changed */
733 if (sys
->d3drenderTargetView
) {
734 ID3D11RenderTargetView_Release(sys
->d3drenderTargetView
);
735 sys
->d3drenderTargetView
= NULL
;
737 if (sys
->d3ddepthStencilView
) {
738 ID3D11DepthStencilView_Release(sys
->d3ddepthStencilView
);
739 sys
->d3ddepthStencilView
= NULL
;
742 /* TODO detect is the size is the same as the output and switch to fullscreen mode */
743 hr
= IDXGISwapChain_ResizeBuffers(sys
->dxgiswapChain
, 0, i_width
, i_height
,
744 DXGI_FORMAT_UNKNOWN
, 0);
746 msg_Err(vd
, "Failed to resize the backbuffer. (hr=0x%lX)", hr
);
750 hr
= IDXGISwapChain_GetBuffer(sys
->dxgiswapChain
, 0, &IID_ID3D11Texture2D
, (LPVOID
*)&pBackBuffer
);
752 msg_Err(vd
, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr
);
756 hr
= ID3D11Device_CreateRenderTargetView(sys
->d3d_dev
.d3ddevice
, (ID3D11Resource
*)pBackBuffer
, NULL
, &sys
->d3drenderTargetView
);
757 ID3D11Texture2D_Release(pBackBuffer
);
759 msg_Err(vd
, "Failed to create the target view. (hr=0x%lX)", hr
);
763 D3D11_TEXTURE2D_DESC deptTexDesc
;
764 memset(&deptTexDesc
, 0,sizeof(deptTexDesc
));
765 deptTexDesc
.ArraySize
= 1;
766 deptTexDesc
.BindFlags
= D3D11_BIND_DEPTH_STENCIL
;
767 deptTexDesc
.CPUAccessFlags
= 0;
768 deptTexDesc
.Format
= DXGI_FORMAT_D24_UNORM_S8_UINT
;
769 deptTexDesc
.Width
= i_width
;
770 deptTexDesc
.Height
= i_height
;
771 deptTexDesc
.MipLevels
= 1;
772 deptTexDesc
.MiscFlags
= 0;
773 deptTexDesc
.SampleDesc
.Count
= 1;
774 deptTexDesc
.SampleDesc
.Quality
= 0;
775 deptTexDesc
.Usage
= D3D11_USAGE_DEFAULT
;
777 hr
= ID3D11Device_CreateTexture2D(sys
->d3d_dev
.d3ddevice
, &deptTexDesc
, NULL
, &pDepthStencil
);
779 msg_Err(vd
, "Could not create the depth stencil texture. (hr=0x%lX)", hr
);
783 D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc
;
784 memset(&depthViewDesc
, 0, sizeof(depthViewDesc
));
786 depthViewDesc
.Format
= deptTexDesc
.Format
;
787 depthViewDesc
.ViewDimension
= D3D11_DSV_DIMENSION_TEXTURE2D
;
788 depthViewDesc
.Texture2D
.MipSlice
= 0;
790 hr
= ID3D11Device_CreateDepthStencilView(sys
->d3d_dev
.d3ddevice
, (ID3D11Resource
*)pDepthStencil
, &depthViewDesc
, &sys
->d3ddepthStencilView
);
791 ID3D11Texture2D_Release(pDepthStencil
);
794 msg_Err(vd
, "Could not create the depth stencil view. (hr=0x%lX)", hr
);
801 /* rotation around the Z axis */
802 static void getZRotMatrix(float theta
, FLOAT matrix
[static 16])
806 sincosf(theta
, &st
, &ct
);
816 memcpy(matrix
, m
, sizeof(m
));
819 /* rotation around the Y axis */
820 static void getYRotMatrix(float theta
, FLOAT matrix
[static 16])
824 sincosf(theta
, &st
, &ct
);
834 memcpy(matrix
, m
, sizeof(m
));
837 /* rotation around the X axis */
838 static void getXRotMatrix(float phi
, FLOAT matrix
[static 16])
842 sincosf(phi
, &sp
, &cp
);
852 memcpy(matrix
, m
, sizeof(m
));
855 static void getZoomMatrix(float zoom
, FLOAT matrix
[static 16]) {
859 1.0f
, 0.0f
, 0.0f
, 0.0f
,
860 0.0f
, 1.0f
, 0.0f
, 0.0f
,
861 0.0f
, 0.0f
, 1.0f
, 0.0f
,
862 0.0f
, 0.0f
, zoom
, 1.0f
865 memcpy(matrix
, m
, sizeof(m
));
868 /* perspective matrix see https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml */
869 static void getProjectionMatrix(float sar
, float fovy
, FLOAT matrix
[static 16]) {
874 float f
= 1.f
/ tanf(fovy
/ 2.f
);
877 f
/ sar
, 0.f
, 0.f
, 0.f
,
879 0.f
, 0.f
, (zNear
+ zFar
) / (zNear
- zFar
), -1.f
,
880 0.f
, 0.f
, (2 * zNear
* zFar
) / (zNear
- zFar
), 0.f
};
882 memcpy(matrix
, m
, sizeof(m
));
885 static float UpdateFOVy(float f_fovx
, float f_sar
)
887 return 2 * atanf(tanf(f_fovx
/ 2) / f_sar
);
890 static float UpdateZ(float f_fovx
, float f_fovy
)
892 /* Do trigonometry to calculate the minimal z value
893 * that will allow us to zoom out without seeing the outside of the
894 * sphere (black borders). */
895 float tan_fovx_2
= tanf(f_fovx
/ 2);
896 float tan_fovy_2
= tanf(f_fovy
/ 2);
897 float z_min
= - SPHERE_RADIUS
/ sinf(atanf(sqrtf(
898 tan_fovx_2
* tan_fovx_2
+ tan_fovy_2
* tan_fovy_2
)));
900 /* The FOV value above which z is dynamically calculated. */
901 const float z_thresh
= 90.f
;
904 if (f_fovx
<= z_thresh
* M_PI
/ 180)
908 float f
= z_min
/ ((FIELD_OF_VIEW_DEGREES_MAX
- z_thresh
) * M_PI
/ 180);
909 f_z
= f
* f_fovx
- f
* z_thresh
* M_PI
/ 180;
916 static void SetQuadVSProjection(vout_display_t
*vd
, d3d_quad_t
*quad
, const vlc_viewpoint_t
*p_vp
)
918 if (!quad
->pVertexShaderConstants
)
921 #define RAD(d) ((float) ((d) * M_PI / 180.f))
922 float f_fovx
= RAD(p_vp
->fov
);
923 if ( f_fovx
> FIELD_OF_VIEW_DEGREES_MAX
* M_PI
/ 180 + 0.001f
||
927 float f_sar
= (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
;
928 float f_teta
= RAD(p_vp
->yaw
) - (float) M_PI_2
;
929 float f_phi
= RAD(p_vp
->pitch
);
930 float f_roll
= RAD(p_vp
->roll
);
931 float f_fovy
= UpdateFOVy(f_fovx
, f_sar
);
932 float f_z
= UpdateZ(f_fovx
, f_fovy
);
934 vout_display_sys_t
*sys
= vd
->sys
;
936 D3D11_MAPPED_SUBRESOURCE mapped
;
937 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexShaderConstants
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mapped
);
939 VS_PROJECTION_CONST
*dst_data
= mapped
.pData
;
940 getXRotMatrix(f_phi
, dst_data
->RotX
);
941 getYRotMatrix(f_teta
, dst_data
->RotY
);
942 getZRotMatrix(f_roll
, dst_data
->RotZ
);
943 getZoomMatrix(SPHERE_RADIUS
* f_z
, dst_data
->View
);
944 getProjectionMatrix(f_sar
, f_fovy
, dst_data
->Projection
);
946 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexShaderConstants
, 0);
950 static void UpdateSize(vout_display_t
*vd
)
952 vout_display_sys_t
*sys
= vd
->sys
;
953 msg_Dbg(vd
, "Detected size change %dx%d", RECTWidth(sys
->sys
.rect_dest_clipped
),
954 RECTHeight(sys
->sys
.rect_dest_clipped
));
956 UpdateBackBuffer(vd
);
958 UpdatePicQuadPosition(vd
);
960 #if defined(HAVE_ID3D11VIDEODECODER)
961 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
963 WaitForSingleObjectEx( sys
->context_lock
, INFINITE
, FALSE
);
967 UpdateQuadPosition(vd
, &sys
->picQuad
, &sys
->sys
.rect_src_clipped
,
968 vd
->fmt
.projection_mode
, vd
->fmt
.orientation
);
970 #if defined(HAVE_ID3D11VIDEODECODER)
971 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
973 ReleaseMutex( sys
->context_lock
);
978 static inline bool RectEquals(const RECT
*r1
, const RECT
*r2
)
980 return r1
->bottom
== r2
->bottom
&& r1
->top
== r2
->top
&&
981 r1
->left
== r2
->left
&& r1
->right
== r2
->right
;
984 #define BEFORE_UPDATE_RECTS \
985 unsigned int i_outside_width = vd->fmt.i_width; \
986 unsigned int i_outside_height = vd->fmt.i_height; \
987 vd->fmt.i_width = vd->sys->picQuad.i_width; \
988 vd->fmt.i_height = vd->sys->picQuad.i_height;
989 #define AFTER_UPDATE_RECTS \
990 vd->fmt.i_width = i_outside_width; \
991 vd->fmt.i_height = i_outside_height;
993 static int Control(vout_display_t
*vd
, int query
, va_list args
)
995 vout_display_sys_t
*sys
= vd
->sys
;
996 RECT before_src_clipped
= sys
->sys
.rect_src_clipped
;
997 RECT before_dest_clipped
= sys
->sys
.rect_dest_clipped
;
998 RECT before_dest
= sys
->sys
.rect_dest
;
1000 BEFORE_UPDATE_RECTS
;
1001 int res
= CommonControl( vd
, query
, args
);
1004 if (query
== VOUT_DISPLAY_CHANGE_VIEWPOINT
)
1006 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
1007 if ( sys
->picQuad
.pVertexShaderConstants
)
1009 SetQuadVSProjection( vd
, &sys
->picQuad
, &cfg
->viewpoint
);
1014 if (!RectEquals(&before_src_clipped
, &sys
->sys
.rect_src_clipped
) ||
1015 !RectEquals(&before_dest_clipped
, &sys
->sys
.rect_dest_clipped
) ||
1016 !RectEquals(&before_dest
, &sys
->sys
.rect_dest
) )
1024 static void Manage(vout_display_t
*vd
)
1026 vout_display_sys_t
*sys
= vd
->sys
;
1027 RECT before_src_clipped
= sys
->sys
.rect_src_clipped
;
1028 RECT before_dest_clipped
= sys
->sys
.rect_dest_clipped
;
1029 RECT before_dest
= sys
->sys
.rect_dest
;
1031 BEFORE_UPDATE_RECTS
;
1035 if (!RectEquals(&before_src_clipped
, &sys
->sys
.rect_src_clipped
) ||
1036 !RectEquals(&before_dest_clipped
, &sys
->sys
.rect_dest_clipped
) ||
1037 !RectEquals(&before_dest
, &sys
->sys
.rect_dest
))
1043 static void UpdateQuadLuminanceScale(vout_display_t
*vd
, d3d_quad_t
*quad
, float luminanceScale
)
1045 vout_display_sys_t
*sys
= vd
->sys
;
1046 D3D11_MAPPED_SUBRESOURCE mappedResource
;
1048 if (quad
->f_luminance_scale
== luminanceScale
)
1051 HRESULT hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
1052 if (SUCCEEDED(hr
)) {
1053 PS_CONSTANT_BUFFER
*dst_data
= mappedResource
.pData
;
1054 quad
->f_luminance_scale
= luminanceScale
;
1055 dst_data
->LuminanceScale
= quad
->f_luminance_scale
;
1056 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0);
1059 msg_Err(vd
, "Failed to lock the picture shader constants (hr=0x%lX)", hr
);
1063 static void DisplayD3DPicture(vout_display_sys_t
*sys
, d3d_quad_t
*quad
, ID3D11ShaderResourceView
*resourceView
[D3D11_MAX_SHADER_VIEW
])
1065 UINT stride
= sizeof(d3d_vertex_t
);
1068 /* Render the quad */
1070 ID3D11DeviceContext_IASetVertexBuffers(sys
->d3d_dev
.d3dcontext
, 0, 1, &quad
->pVertexBuffer
, &stride
, &offset
);
1071 ID3D11DeviceContext_IASetIndexBuffer(sys
->d3d_dev
.d3dcontext
, quad
->pIndexBuffer
, DXGI_FORMAT_R16_UINT
, 0);
1072 if ( quad
->pVertexShaderConstants
)
1073 ID3D11DeviceContext_VSSetConstantBuffers(sys
->d3d_dev
.d3dcontext
, 0, 1, &quad
->pVertexShaderConstants
);
1075 ID3D11DeviceContext_VSSetShader(sys
->d3d_dev
.d3dcontext
, quad
->d3dvertexShader
, NULL
, 0);
1078 ID3D11DeviceContext_PSSetShader(sys
->d3d_dev
.d3dcontext
, quad
->d3dpixelShader
, NULL
, 0);
1080 ID3D11DeviceContext_PSSetConstantBuffers(sys
->d3d_dev
.d3dcontext
, 0, quad
->PSConstantsCount
, quad
->pPixelShaderConstants
);
1081 ID3D11DeviceContext_PSSetShaderResources(sys
->d3d_dev
.d3dcontext
, 0, D3D11_MAX_SHADER_VIEW
, resourceView
);
1083 ID3D11DeviceContext_RSSetViewports(sys
->d3d_dev
.d3dcontext
, 1, &quad
->cropViewport
);
1085 ID3D11DeviceContext_DrawIndexed(sys
->d3d_dev
.d3dcontext
, quad
->indexCount
, 0, 0);
1088 static void Prepare(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
)
1090 vout_display_sys_t
*sys
= vd
->sys
;
1092 if (sys
->picQuadConfig
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
1094 D3D11_MAPPED_SUBRESOURCE mappedResource
;
1095 D3D11_TEXTURE2D_DESC texDesc
;
1098 plane_t planes
[PICTURE_PLANE_MAX
];
1100 bool b_mapped
= true;
1101 for (i
= 0; i
< picture
->i_planes
; i
++) {
1102 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, sys
->stagingSys
.resource
[i
],
1103 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
1104 if( unlikely(FAILED(hr
)) )
1107 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, sys
->stagingSys
.resource
[i
], 0);
1111 ID3D11Texture2D_GetDesc(sys
->stagingSys
.texture
[i
], &texDesc
);
1112 planes
[i
].i_lines
= texDesc
.Height
;
1113 planes
[i
].i_pitch
= mappedResource
.RowPitch
;
1114 planes
[i
].p_pixels
= mappedResource
.pData
;
1116 planes
[i
].i_visible_lines
= picture
->p
[i
].i_visible_lines
;
1117 planes
[i
].i_visible_pitch
= picture
->p
[i
].i_visible_pitch
;
1122 for (i
= 0; i
< picture
->i_planes
; i
++)
1123 plane_CopyPixels(&planes
[i
], &picture
->p
[i
]);
1125 for (i
= 0; i
< picture
->i_planes
; i
++)
1126 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, sys
->stagingSys
.resource
[i
], 0);
1131 picture_sys_t
*p_sys
= ActivePictureSys(picture
);
1133 #if defined(HAVE_ID3D11VIDEODECODER)
1134 if (sys
->context_lock
!= INVALID_HANDLE_VALUE
&& is_d3d11_opaque(picture
->format
.i_chroma
))
1135 WaitForSingleObjectEx( sys
->context_lock
, INFINITE
, FALSE
);
1137 if (!is_d3d11_opaque(picture
->format
.i_chroma
) || sys
->legacy_shader
) {
1138 D3D11_TEXTURE2D_DESC srcDesc
,texDesc
;
1139 if (!is_d3d11_opaque(picture
->format
.i_chroma
))
1140 Direct3D11UnmapPoolTexture(picture
);
1141 ID3D11Texture2D_GetDesc(p_sys
->texture
[KNOWN_DXGI_INDEX
], &srcDesc
);
1142 ID3D11Texture2D_GetDesc(sys
->stagingSys
.texture
[0], &texDesc
);
1145 .bottom
= __MIN(srcDesc
.Height
, texDesc
.Height
),
1147 .right
= __MIN(srcDesc
.Width
, texDesc
.Width
),
1150 ID3D11DeviceContext_CopySubresourceRegion(sys
->d3d_dev
.d3dcontext
,
1151 sys
->stagingSys
.resource
[KNOWN_DXGI_INDEX
],
1153 p_sys
->resource
[KNOWN_DXGI_INDEX
],
1154 p_sys
->slice_index
, &box
);
1158 D3D11_TEXTURE2D_DESC texDesc
;
1159 ID3D11Texture2D_GetDesc(p_sys
->texture
[0], &texDesc
);
1160 if (texDesc
.BindFlags
& D3D11_BIND_SHADER_RESOURCE
)
1162 /* for performance reason we don't want to allocate this during
1163 * display, do it preferrably when creating the texture */
1164 assert(p_sys
->resourceView
[0]!=NULL
);
1166 if ( sys
->picQuad
.i_height
!= texDesc
.Height
||
1167 sys
->picQuad
.i_width
!= texDesc
.Width
)
1169 /* the decoder produced different sizes than the vout, we need to
1170 * adjust the vertex */
1171 sys
->picQuad
.i_height
= texDesc
.Height
;
1172 sys
->picQuad
.i_width
= texDesc
.Width
;
1174 BEFORE_UPDATE_RECTS
;
1175 UpdateRects(vd
, NULL
, true);
1183 int subpicture_region_count
= 0;
1184 picture_t
**subpicture_regions
= NULL
;
1185 Direct3D11MapSubpicture(vd
, &subpicture_region_count
, &subpicture_regions
, subpicture
);
1186 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
1187 sys
->d3dregion_count
= subpicture_region_count
;
1188 sys
->d3dregions
= subpicture_regions
;
1191 if (sys
->dxgiswapChain4
&& picture
->format
.mastering
.max_luminance
)
1193 UpdateQuadLuminanceScale(vd
, &sys
->picQuad
, (float) picture
->format
.mastering
.max_luminance
/ sys
->display
.luminance_peak
);
1195 DXGI_HDR_METADATA_HDR10 hdr10
= {0};
1196 hdr10
.GreenPrimary
[0] = picture
->format
.mastering
.primaries
[0];
1197 hdr10
.GreenPrimary
[1] = picture
->format
.mastering
.primaries
[1];
1198 hdr10
.BluePrimary
[0] = picture
->format
.mastering
.primaries
[2];
1199 hdr10
.BluePrimary
[1] = picture
->format
.mastering
.primaries
[3];
1200 hdr10
.RedPrimary
[0] = picture
->format
.mastering
.primaries
[4];
1201 hdr10
.RedPrimary
[1] = picture
->format
.mastering
.primaries
[5];
1202 hdr10
.WhitePoint
[0] = picture
->format
.mastering
.white_point
[0];
1203 hdr10
.WhitePoint
[1] = picture
->format
.mastering
.white_point
[1];
1204 hdr10
.MinMasteringLuminance
= picture
->format
.mastering
.min_luminance
;
1205 hdr10
.MaxMasteringLuminance
= picture
->format
.mastering
.max_luminance
;
1206 hdr10
.MaxContentLightLevel
= picture
->format
.lighting
.MaxCLL
;
1207 hdr10
.MaxFrameAverageLightLevel
= picture
->format
.lighting
.MaxFALL
;
1208 IDXGISwapChain4_SetHDRMetaData(sys
->dxgiswapChain4
, DXGI_HDR_METADATA_TYPE_HDR10
, sizeof(hdr10
), &hdr10
);
1211 FLOAT blackRGBA
[4] = {0.0f
, 0.0f
, 0.0f
, 1.0f
};
1212 ID3D11DeviceContext_ClearRenderTargetView(sys
->d3d_dev
.d3dcontext
, sys
->d3drenderTargetView
, blackRGBA
);
1214 /* no ID3D11Device operations should come here */
1216 ID3D11DeviceContext_OMSetRenderTargets(sys
->d3d_dev
.d3dcontext
, 1, &sys
->d3drenderTargetView
, sys
->d3ddepthStencilView
);
1218 ID3D11DeviceContext_ClearDepthStencilView(sys
->d3d_dev
.d3dcontext
, sys
->d3ddepthStencilView
, D3D11_CLEAR_DEPTH
| D3D11_CLEAR_STENCIL
, 1.0f
, 0);
1220 /* Render the quad */
1221 if (!is_d3d11_opaque(picture
->format
.i_chroma
) || sys
->legacy_shader
)
1222 DisplayD3DPicture(sys
, &sys
->picQuad
, sys
->stagingSys
.resourceView
);
1224 picture_sys_t
*p_sys
= ActivePictureSys(picture
);
1225 DisplayD3DPicture(sys
, &sys
->picQuad
, p_sys
->resourceView
);
1229 // draw the additional vertices
1230 for (int i
= 0; i
< sys
->d3dregion_count
; ++i
) {
1231 if (sys
->d3dregions
[i
])
1233 d3d_quad_t
*quad
= (d3d_quad_t
*) sys
->d3dregions
[i
]->p_sys
;
1234 DisplayD3DPicture(sys
, quad
, quad
->picSys
.resourceView
);
1239 #if defined(HAVE_ID3D11VIDEODECODER)
1240 if (sys
->context_lock
!= INVALID_HANDLE_VALUE
&& is_d3d11_opaque(picture
->format
.i_chroma
))
1242 ReleaseMutex( sys
->context_lock
);
1246 ID3D11DeviceContext_Flush(sys
->d3d_dev
.d3dcontext
);
1249 static void Display(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
)
1251 vout_display_sys_t
*sys
= vd
->sys
;
1253 DXGI_PRESENT_PARAMETERS presentParams
;
1254 memset(&presentParams
, 0, sizeof(presentParams
));
1255 HRESULT hr
= IDXGISwapChain1_Present1(sys
->dxgiswapChain
, 0, 0, &presentParams
);
1256 if (hr
== DXGI_ERROR_DEVICE_REMOVED
|| hr
== DXGI_ERROR_DEVICE_RESET
)
1258 /* TODO device lost */
1259 msg_Dbg(vd
, "SwapChain Present failed. (hr=0x%lX)", hr
);
1262 picture_Release(picture
);
1264 subpicture_Delete(subpicture
);
1269 static void Direct3D11Destroy(vout_display_t
*vd
)
1271 #if !VLC_WINSTORE_APP
1272 vout_display_sys_t
*sys
= vd
->sys
;
1274 if (sys
->hd3dcompiler_dll
)
1275 FreeLibrary(sys
->hd3dcompiler_dll
);
1277 sys
->OurD3DCompile
= NULL
;
1278 sys
->hdxgi_dll
= NULL
;
1279 sys
->hd3dcompiler_dll
= NULL
;
1280 D3D11_Destroy( &vd
->sys
->hd3d
);
1284 #if !VLC_WINSTORE_APP
1285 static HINSTANCE
Direct3D11LoadShaderLibrary(void)
1287 HINSTANCE instance
= NULL
;
1288 /* d3dcompiler_47 is the latest on windows 8.1 */
1289 for (int i
= 47; i
> 41; --i
) {
1291 _sntprintf(filename
, 19, TEXT("D3DCOMPILER_%d.dll"), i
);
1292 instance
= LoadLibrary(filename
);
1293 if (instance
) break;
1299 #define COLOR_RANGE_FULL 1 /* 0-255 */
1300 #define COLOR_RANGE_STUDIO 0 /* 16-235 */
1302 #define TRANSFER_FUNC_10 TRANSFER_FUNC_LINEAR
1303 #define TRANSFER_FUNC_22 TRANSFER_FUNC_SRGB
1304 #define TRANSFER_FUNC_2084 TRANSFER_FUNC_SMPTE_ST2084
1306 #define COLOR_PRIMARIES_BT601 COLOR_PRIMARIES_BT601_525
1308 static const dxgi_color_space color_spaces
[] = {
1309 #define DXGIMAP(AXIS, RANGE, GAMMA, SITTING, PRIMARIES) \
1310 { DXGI_COLOR_SPACE_##AXIS##_##RANGE##_G##GAMMA##_##SITTING##_P##PRIMARIES, \
1311 #AXIS " Rec." #PRIMARIES " gamma:" #GAMMA " range:" #RANGE, \
1312 COLOR_AXIS_##AXIS, COLOR_PRIMARIES_BT##PRIMARIES, TRANSFER_FUNC_##GAMMA, \
1313 COLOR_SPACE_BT##PRIMARIES, COLOR_RANGE_##RANGE},
1315 DXGIMAP(RGB
, FULL
, 22, NONE
, 709)
1316 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 601)
1317 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 601)
1318 DXGIMAP(RGB
, FULL
, 10, NONE
, 709)
1319 DXGIMAP(RGB
, STUDIO
, 22, NONE
, 709)
1320 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 709)
1321 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 709)
1322 DXGIMAP(RGB
, STUDIO
, 22, NONE
, 2020)
1323 DXGIMAP(YCBCR
, STUDIO
, 22, LEFT
, 2020)
1324 DXGIMAP(YCBCR
, FULL
, 22, LEFT
, 2020)
1325 DXGIMAP(YCBCR
, STUDIO
, 22, TOPLEFT
, 2020)
1326 DXGIMAP(RGB
, FULL
, 22, NONE
, 2020)
1327 DXGIMAP(RGB
, FULL
, 2084, NONE
, 2020)
1328 DXGIMAP(YCBCR
, STUDIO
, 2084, LEFT
, 2020)
1329 DXGIMAP(RGB
, STUDIO
, 2084, NONE
, 2020)
1330 DXGIMAP(YCBCR
, STUDIO
, 2084, TOPLEFT
, 2020)
1331 /*DXGIMAP(YCBCR, FULL, 22, NONE, 2020, 601)*/
1332 {DXGI_COLOR_SPACE_RESERVED
, NULL
, 0, 0, 0, 0, 0},
1336 static void D3D11SetColorSpace(vout_display_t
*vd
)
1338 vout_display_sys_t
*sys
= vd
->sys
;
1341 int score
, best_score
= 0;
1343 IDXGIOutput
*dxgiOutput
= NULL
;
1344 IDXGISwapChain3
*dxgiswapChain3
= NULL
;
1345 sys
->display
.colorspace
= &color_spaces
[0];
1347 hr
= IDXGISwapChain_QueryInterface( sys
->dxgiswapChain
, &IID_IDXGISwapChain3
, (void **)&dxgiswapChain3
);
1349 msg_Warn(vd
, "could not get a IDXGISwapChain3");
1353 bool src_full_range
= vd
->source
.b_color_range_full
||
1354 /* the YUV->RGB conversion already output full range */
1355 is_d3d11_opaque(vd
->source
.i_chroma
) ||
1356 vlc_fourcc_IsYUV(vd
->source
.i_chroma
);
1358 /* pick the best output based on color support and transfer */
1359 /* TODO support YUV output later */
1360 for (int i
=0; color_spaces
[i
].name
; ++i
)
1362 hr
= IDXGISwapChain3_CheckColorSpaceSupport(dxgiswapChain3
, color_spaces
[i
].dxgi
, &support
);
1363 if (SUCCEEDED(hr
) && support
) {
1364 msg_Dbg(vd
, "supports colorspace %s", color_spaces
[i
].name
);
1366 if (color_spaces
[i
].primaries
== vd
->source
.primaries
)
1368 if (color_spaces
[i
].color
== vd
->source
.space
)
1369 score
+= 2; /* we don't want to translate color spaces */
1370 if (color_spaces
[i
].transfer
== vd
->source
.transfer
||
1371 /* favor 2084 output for HLG source */
1372 (color_spaces
[i
].transfer
== TRANSFER_FUNC_SMPTE_ST2084
&& vd
->source
.transfer
== TRANSFER_FUNC_HLG
))
1374 if (color_spaces
[i
].b_full_range
== src_full_range
)
1376 if (score
> best_score
|| (score
&& best
== -1)) {
1386 msg_Warn(vd
, "no matching colorspace found force %s", color_spaces
[best
].name
);
1389 #ifdef HAVE_DXGI1_6_H
1390 if (SUCCEEDED(IDXGISwapChain_GetContainingOutput( sys
->dxgiswapChain
, &dxgiOutput
)))
1392 IDXGIOutput6
*dxgiOutput6
= NULL
;
1393 if (SUCCEEDED(IDXGIOutput_QueryInterface( dxgiOutput
, &IID_IDXGIOutput6
, (void **)&dxgiOutput6
)))
1395 DXGI_OUTPUT_DESC1 desc1
;
1396 if (SUCCEEDED(IDXGIOutput6_GetDesc1( dxgiOutput6
, &desc1
)))
1398 const dxgi_color_space
*csp
= NULL
;
1399 for (int i
=0; color_spaces
[i
].name
; ++i
)
1401 if (color_spaces
[i
].dxgi
== desc1
.ColorSpace
)
1404 csp
= &color_spaces
[i
];
1409 msg_Dbg(vd
, "Output max luminance: %.1f, colorspace %s, bits per pixel %d", desc1
.MaxFullFrameLuminance
, csp
?csp
->name
:"unknown", desc1
.BitsPerColor
);
1410 //sys->display.luminance_peak = desc1.MaxFullFrameLuminance;
1412 IDXGIOutput6_Release( dxgiOutput6
);
1414 IDXGIOutput_Release( dxgiOutput
);
1418 hr
= IDXGISwapChain3_SetColorSpace1(dxgiswapChain3
, color_spaces
[best
].dxgi
);
1421 sys
->display
.colorspace
= &color_spaces
[best
];
1422 msg_Dbg(vd
, "using colorspace %s", sys
->display
.colorspace
->name
);
1425 msg_Err(vd
, "Failed to set colorspace %s. (hr=0x%lX)", sys
->display
.colorspace
->name
, hr
);
1427 /* guestimate the display peak luminance */
1428 switch (sys
->display
.colorspace
->transfer
)
1430 case TRANSFER_FUNC_LINEAR
:
1431 case TRANSFER_FUNC_SRGB
:
1432 sys
->display
.luminance_peak
= DEFAULT_SRGB_BRIGHTNESS
;
1434 case TRANSFER_FUNC_SMPTE_ST2084
:
1435 sys
->display
.luminance_peak
= MAX_PQ_BRIGHTNESS
;
1437 /* there is no other output transfer on Windows */
1439 vlc_assert_unreachable();
1443 IDXGISwapChain3_Release(dxgiswapChain3
);
1446 static const d3d_format_t
*GetDirectRenderingFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1448 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
1449 if (is_d3d11_opaque(i_src_chroma
))
1450 supportFlags
|= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
1451 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, false, 0, is_d3d11_opaque(i_src_chroma
), supportFlags
);
1454 static const d3d_format_t
*GetDirectDecoderFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1456 UINT supportFlags
= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
1457 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, false, 0, is_d3d11_opaque(i_src_chroma
), supportFlags
);
1460 static const d3d_format_t
*GetDisplayFormatByDepth(vout_display_t
*vd
, uint8_t bit_depth
, bool from_processor
, bool rgb_only
)
1462 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
1464 supportFlags
|= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT
;
1465 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, 0, rgb_only
, bit_depth
, false, supportFlags
);
1468 static const d3d_format_t
*GetBlendableFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
1470 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
| D3D11_FORMAT_SUPPORT_BLENDABLE
;
1471 return FindD3D11Format( vd
->sys
->d3d_dev
.d3ddevice
, i_src_chroma
, false, 0, false, supportFlags
);
1474 static int Direct3D11Open(vout_display_t
*vd
)
1476 vout_display_sys_t
*sys
= vd
->sys
;
1477 IDXGIFactory2
*dxgifactory
;
1479 #if !VLC_WINSTORE_APP
1482 DXGI_SWAP_CHAIN_DESC1 scd
;
1483 memset(&scd
, 0, sizeof(scd
));
1484 scd
.BufferCount
= 3;
1485 scd
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
1486 scd
.SampleDesc
.Count
= 1;
1487 scd
.SampleDesc
.Quality
= 0;
1488 scd
.Width
= vd
->source
.i_visible_width
;
1489 scd
.Height
= vd
->source
.i_visible_height
;
1490 switch(vd
->source
.i_chroma
)
1492 case VLC_CODEC_D3D11_OPAQUE_10B
:
1493 scd
.Format
= DXGI_FORMAT_R10G10B10A2_UNORM
;
1496 scd
.Format
= DXGI_FORMAT_R8G8B8A8_UNORM
; /* TODO: use DXGI_FORMAT_NV12 */
1499 //scd.Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1500 scd
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
1502 hr
= D3D11_CreateDevice(vd
, &sys
->hd3d
,
1503 is_d3d11_opaque(vd
->source
.i_chroma
),
1506 msg_Err(vd
, "Could not Create the D3D11 device. (hr=0x%lX)", hr
);
1507 return VLC_EGENERIC
;
1510 IDXGIAdapter
*dxgiadapter
= D3D11DeviceAdapter(sys
->d3d_dev
.d3ddevice
);
1511 if (unlikely(dxgiadapter
==NULL
)) {
1512 msg_Err(vd
, "Could not get the DXGI Adapter");
1513 return VLC_EGENERIC
;
1516 hr
= IDXGIAdapter_GetParent(dxgiadapter
, &IID_IDXGIFactory2
, (void **)&dxgifactory
);
1517 IDXGIAdapter_Release(dxgiadapter
);
1519 msg_Err(vd
, "Could not get the DXGI Factory. (hr=0x%lX)", hr
);
1520 return VLC_EGENERIC
;
1523 hr
= IDXGIFactory2_CreateSwapChainForHwnd(dxgifactory
, (IUnknown
*)sys
->d3d_dev
.d3ddevice
,
1524 sys
->sys
.hvideownd
, &scd
, NULL
, NULL
, &sys
->dxgiswapChain
);
1525 IDXGIFactory2_Release(dxgifactory
);
1527 msg_Err(vd
, "Could not create the SwapChain. (hr=0x%lX)", hr
);
1528 return VLC_EGENERIC
;
1532 IDXGISwapChain_QueryInterface( sys
->dxgiswapChain
, &IID_IDXGISwapChain4
, (void **)&sys
->dxgiswapChain4
);
1534 D3D11SetColorSpace(vd
);
1537 video_format_Copy(&fmt
, &vd
->source
);
1538 int err
= SetupOutputFormat(vd
, &fmt
);
1539 if (err
!= VLC_SUCCESS
)
1541 if (!is_d3d11_opaque(vd
->source
.i_chroma
)
1542 #if !VLC_WINSTORE_APP
1547 const vlc_fourcc_t
*list
= vlc_fourcc_IsYUV(vd
->source
.i_chroma
) ?
1548 vlc_fourcc_GetYUVFallback(vd
->source
.i_chroma
) :
1549 vlc_fourcc_GetRGBFallback(vd
->source
.i_chroma
);
1550 for (unsigned i
= 0; list
[i
] != 0; i
++) {
1551 fmt
.i_chroma
= list
[i
];
1552 if (fmt
.i_chroma
== vd
->source
.i_chroma
)
1554 err
= SetupOutputFormat(vd
, &fmt
);
1555 if (err
== VLC_SUCCESS
)
1559 if (err
!= VLC_SUCCESS
)
1563 if (Direct3D11CreateGenericResources(vd
)) {
1564 msg_Err(vd
, "Failed to allocate resources");
1565 Direct3D11DestroyResources(vd
);
1566 return VLC_EGENERIC
;
1569 video_format_Clean(&vd
->fmt
);
1575 static int SetupOutputFormat(vout_display_t
*vd
, video_format_t
*fmt
)
1577 vout_display_sys_t
*sys
= vd
->sys
;
1579 // look for the requested pixel format first
1580 sys
->picQuadConfig
= GetDirectRenderingFormat(vd
, fmt
->i_chroma
);
1582 /* look for a decoder format that can be decoded but not used in shaders */
1583 const d3d_format_t
*decoder_format
= NULL
;
1584 if ( !sys
->picQuadConfig
&& is_d3d11_opaque(fmt
->i_chroma
) )
1585 decoder_format
= GetDirectDecoderFormat(vd
, fmt
->i_chroma
);
1587 decoder_format
= sys
->picQuadConfig
;
1589 // look for any pixel format that we can handle with enough pixels per channel
1590 if ( !sys
->picQuadConfig
)
1592 uint8_t bits_per_channel
;
1593 switch (fmt
->i_chroma
)
1595 case VLC_CODEC_D3D11_OPAQUE
:
1596 bits_per_channel
= 8;
1598 case VLC_CODEC_D3D11_OPAQUE_10B
:
1599 bits_per_channel
= 10;
1603 const vlc_chroma_description_t
*p_format
= vlc_fourcc_GetChromaDescription(fmt
->i_chroma
);
1604 bits_per_channel
= p_format
== NULL
|| p_format
->pixel_bits
== 0 ? 8 : p_format
->pixel_bits
/ p_format
->pixel_size
;
1609 bool is_rgb
= !vlc_fourcc_IsYUV(fmt
->i_chroma
);
1610 sys
->picQuadConfig
= GetDisplayFormatByDepth(vd
, bits_per_channel
, decoder_format
!=NULL
, is_rgb
);
1611 if (!sys
->picQuadConfig
&& is_rgb
)
1612 sys
->picQuadConfig
= GetDisplayFormatByDepth(vd
, bits_per_channel
, decoder_format
!=NULL
, false);
1615 // look for any pixel format that we can handle
1616 if ( !sys
->picQuadConfig
)
1617 sys
->picQuadConfig
= GetDisplayFormatByDepth(vd
, 0, false, false);
1619 if ( !sys
->picQuadConfig
)
1621 msg_Err(vd
, "Could not get a suitable texture pixel format");
1622 return VLC_EGENERIC
;
1625 fmt
->i_chroma
= decoder_format
? decoder_format
->fourcc
: sys
->picQuadConfig
->fourcc
;
1627 msg_Dbg( vd
, "Using pixel format %s for chroma %4.4s", sys
->picQuadConfig
->name
,
1628 (char *)&fmt
->i_chroma
);
1629 DxgiFormatMask( sys
->picQuadConfig
->formatTexture
, fmt
);
1631 /* check the region pixel format */
1632 sys
->d3dregion_format
= GetBlendableFormat(vd
, VLC_CODEC_RGBA
);
1633 if (!sys
->d3dregion_format
)
1634 sys
->d3dregion_format
= GetBlendableFormat(vd
, VLC_CODEC_BGRA
);
1636 if (Direct3D11CreateFormatResources(vd
, fmt
)) {
1637 msg_Err(vd
, "Failed to allocate format resources");
1638 Direct3D11DestroyResources(vd
);
1639 return VLC_EGENERIC
;
1646 static void Direct3D11Close(vout_display_t
*vd
)
1648 vout_display_sys_t
*sys
= vd
->sys
;
1650 Direct3D11DestroyResources(vd
);
1651 if (sys
->dxgiswapChain4
)
1653 IDXGISwapChain_Release(sys
->dxgiswapChain4
);
1654 sys
->dxgiswapChain4
= NULL
;
1656 if (sys
->dxgiswapChain
)
1658 IDXGISwapChain_Release(sys
->dxgiswapChain
);
1659 sys
->dxgiswapChain
= NULL
;
1662 D3D11_ReleaseDevice( &sys
->d3d_dev
);
1664 msg_Dbg(vd
, "Direct3D11 device adapter closed");
1667 static void UpdatePicQuadPosition(vout_display_t
*vd
)
1669 vout_display_sys_t
*sys
= vd
->sys
;
1671 sys
->picQuad
.cropViewport
.Width
= RECTWidth(sys
->sys
.rect_dest_clipped
);
1672 sys
->picQuad
.cropViewport
.Height
= RECTHeight(sys
->sys
.rect_dest_clipped
);
1673 sys
->picQuad
.cropViewport
.TopLeftX
= sys
->sys
.rect_dest_clipped
.left
;
1674 sys
->picQuad
.cropViewport
.TopLeftY
= sys
->sys
.rect_dest_clipped
.top
;
1676 sys
->picQuad
.cropViewport
.MinDepth
= 0.0f
;
1677 sys
->picQuad
.cropViewport
.MaxDepth
= 1.0f
;
1679 SetQuadVSProjection(vd
, &sys
->picQuad
, &vd
->cfg
->viewpoint
);
1682 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
);
1686 static ID3DBlob
* CompileShader(vout_display_t
*vd
, const char *psz_shader
, bool pixel
)
1688 vout_display_sys_t
*sys
= vd
->sys
;
1689 ID3DBlob
* pShaderBlob
= NULL
, *pErrBlob
;
1691 /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1692 HRESULT hr
= D3DCompile(psz_shader
, strlen(psz_shader
),
1693 NULL
, NULL
, NULL
, "main",
1694 pixel
? (sys
->legacy_shader
? "ps_4_0_level_9_1" : "ps_4_0") :
1695 (sys
->legacy_shader
? "vs_4_0_level_9_1" : "vs_4_0"),
1696 0, 0, &pShaderBlob
, &pErrBlob
);
1699 char *err
= pErrBlob
? ID3D10Blob_GetBufferPointer(pErrBlob
) : NULL
;
1700 msg_Err(vd
, "invalid %s Shader (hr=0x%lX): %s", pixel
?"Pixel":"Vertex", hr
, err
);
1702 ID3D10Blob_Release(pErrBlob
);
1708 static bool IsRGBShader(const d3d_format_t
*cfg
)
1710 return cfg
->resourceFormat
[0] != DXGI_FORMAT_R8_UNORM
&&
1711 cfg
->resourceFormat
[0] != DXGI_FORMAT_R16_UNORM
&&
1712 cfg
->formatTexture
!= DXGI_FORMAT_YUY2
;
1715 static HRESULT
CompilePixelShader(vout_display_t
*vd
, const d3d_format_t
*format
,
1716 video_transfer_func_t transfer
, bool src_full_range
,
1717 ID3D11PixelShader
**output
)
1719 vout_display_sys_t
*sys
= vd
->sys
;
1721 static const char *DEFAULT_NOOP
= "return rgb";
1722 const char *psz_sampler
;
1723 const char *psz_src_transform
= DEFAULT_NOOP
;
1724 const char *psz_display_transform
= DEFAULT_NOOP
;
1725 const char *psz_tone_mapping
= DEFAULT_NOOP
;
1726 const char *psz_peak_luminance
= DEFAULT_NOOP
;
1727 const char *psz_adjust_range
= DEFAULT_NOOP
;
1728 char *psz_range
= NULL
;
1730 switch (format
->formatTexture
)
1732 case DXGI_FORMAT_NV12
:
1733 case DXGI_FORMAT_P010
:
1735 "sample.x = shaderTexture[0].Sample(samplerState, coords).x;\
1736 sample.yz = shaderTexture[1].Sample(samplerState, coords).xy;\
1739 case DXGI_FORMAT_YUY2
:
1741 "sample.x = shaderTexture[0].Sample(samplerState, coords).x;\
1742 sample.y = shaderTexture[0].Sample(samplerState, coords).y;\
1743 sample.z = shaderTexture[0].Sample(samplerState, coords).a;\
1746 case DXGI_FORMAT_R8G8B8A8_UNORM
:
1747 case DXGI_FORMAT_B8G8R8A8_UNORM
:
1748 case DXGI_FORMAT_B8G8R8X8_UNORM
:
1749 case DXGI_FORMAT_B5G6R5_UNORM
:
1751 "sample = shaderTexture[0].Sample(samplerState, coords);";
1753 case DXGI_FORMAT_UNKNOWN
:
1755 "sample.x = shaderTexture[0].Sample(samplerState, coords).x;\
1756 sample.y = shaderTexture[1].Sample(samplerState, coords).x;\
1757 sample.z = shaderTexture[2].Sample(samplerState, coords).x;\
1761 vlc_assert_unreachable();
1764 video_transfer_func_t src_transfer
;
1766 if (transfer
!= sys
->display
.colorspace
->transfer
)
1768 /* we need to go in linear mode */
1771 case TRANSFER_FUNC_SMPTE_ST2084
:
1772 /* ST2084 to Linear */
1775 "rgb = pow(rgb, 1.0/ST2084_m2);\
1776 rgb = max(rgb - ST2084_c1, 0.0) / (ST2084_c2 - ST2084_c3 * rgb);\
1777 rgb = pow(rgb, 1.0/ST2084_m1);\
1779 src_transfer
= TRANSFER_FUNC_LINEAR
;
1781 case TRANSFER_FUNC_HLG
:
1784 "rgb.r = inverse_HLG(rgb.r);\
1785 rgb.g = inverse_HLG(rgb.g);\
1786 rgb.b = inverse_HLG(rgb.b);\
1788 src_transfer
= TRANSFER_FUNC_LINEAR
;
1790 case TRANSFER_FUNC_BT709
:
1791 psz_src_transform
= "return pow(rgb, 1.0 / 0.45)";
1792 src_transfer
= TRANSFER_FUNC_LINEAR
;
1794 case TRANSFER_FUNC_BT470_M
:
1795 case TRANSFER_FUNC_SRGB
:
1796 psz_src_transform
= "return pow(rgb, 2.2)";
1797 src_transfer
= TRANSFER_FUNC_LINEAR
;
1799 case TRANSFER_FUNC_BT470_BG
:
1800 psz_src_transform
= "return pow(rgb, 2.8)";
1801 src_transfer
= TRANSFER_FUNC_LINEAR
;
1804 msg_Dbg(vd
, "unhandled source transfer %d", transfer
);
1805 src_transfer
= transfer
;
1809 switch (sys
->display
.colorspace
->transfer
)
1811 case TRANSFER_FUNC_SRGB
:
1812 if (src_transfer
== TRANSFER_FUNC_LINEAR
)
1814 /* Linear to sRGB */
1815 psz_display_transform
= "return pow(rgb, 1.0 / 2.2)";
1817 if (transfer
== TRANSFER_FUNC_SMPTE_ST2084
|| transfer
== TRANSFER_FUNC_HLG
)
1819 /* HDR tone mapping */
1821 "static const float3 HABLE_DIV = hable(11.2);\
1822 rgb = hable(rgb) / HABLE_DIV;\
1827 msg_Warn(vd
, "don't know how to transfer from %d to sRGB", src_transfer
);
1830 case TRANSFER_FUNC_SMPTE_ST2084
:
1831 if (src_transfer
== TRANSFER_FUNC_LINEAR
)
1833 /* Linear to ST2084 */
1834 psz_display_transform
=
1836 "rgb = pow(rgb, ST2084_m1);\
1837 rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
1838 rgb = pow(rgb, ST2084_m2);\
1842 msg_Warn(vd
, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer
);
1845 msg_Warn(vd
, "don't know how to transfer from %d to %d", src_transfer
, sys
->display
.colorspace
->transfer
);
1850 if ( transfer
== TRANSFER_FUNC_SMPTE_ST2084
&&
1851 sys
->display
.colorspace
->transfer
!= TRANSFER_FUNC_SMPTE_ST2084
)
1852 /* the luminance may be dynamic */
1853 psz_peak_luminance
= "return rgb * LuminanceScale";
1855 int range_adjust
= 0;
1856 if (sys
->display
.colorspace
->b_full_range
) {
1857 if (!src_full_range
)
1858 range_adjust
= 1; /* raise the source to full range */
1861 range_adjust
= -1; /* lower the source to studio range */
1863 if (!IsRGBShader(format
))
1864 range_adjust
--; /* the YUV->RGB conversion already output full range */
1866 if (range_adjust
!= 0)
1868 psz_range
= malloc(256);
1869 if (likely(psz_range
))
1871 FLOAT itu_black_level
;
1872 FLOAT itu_range_factor
;
1873 FLOAT itu_white_level
;
1874 switch (format
->bitsPerChannel
)
1877 /* Rec. ITU-R BT.709-6 §4.6 */
1878 itu_black_level
= 16.f
/ 255.f
;
1879 itu_white_level
= 235.f
/ 255.f
;
1880 itu_range_factor
= (float)(235 - 16) / 255.f
;
1883 /* Rec. ITU-R BT.709-6 §4.6 */
1884 itu_black_level
= 64.f
/ 1023.f
;
1885 itu_white_level
= 940.f
/ 1023.f
;
1886 itu_range_factor
= (float)(940 - 64) / 1023.f
;
1889 /* Rec. ITU-R BT.2020-2 Table 5 */
1890 itu_black_level
= 256.f
/ 4095.f
;
1891 itu_white_level
= 3760.f
/ 4095.f
;
1892 itu_range_factor
= (float)(3760 - 256) / 4095.f
;
1895 /* unknown bitdepth, use approximation for infinite bit depth */
1896 itu_black_level
= 16.f
/ 256.f
;
1897 itu_white_level
= 235.f
/ 256.f
;
1898 itu_range_factor
= (float)(235 - 16) / 256.f
;
1902 FLOAT black_level
= 0;
1903 FLOAT range_factor
= 1.0f
;
1904 if (range_adjust
> 0)
1906 /* expand the range from studio to full range */
1907 while (range_adjust
--)
1909 black_level
-= itu_black_level
;
1910 range_factor
/= itu_range_factor
;
1912 sprintf(psz_range
, "return max(0,min(1,(rgb + %f) * %f))",
1913 black_level
, range_factor
);
1917 /* shrink the range to studio range */
1918 while (range_adjust
++)
1920 black_level
+= itu_black_level
;
1921 range_factor
*= itu_range_factor
;
1923 sprintf(psz_range
, "return clamp(rgb + %f * %f,%f,%f)",
1924 black_level
, range_factor
, itu_black_level
, itu_white_level
);
1926 psz_adjust_range
= psz_range
;
1930 char *shader
= malloc(strlen(globPixelShaderDefault
) + 32 + strlen(psz_sampler
) +
1931 strlen(psz_src_transform
) + strlen(psz_display_transform
) +
1932 strlen(psz_tone_mapping
) + strlen(psz_peak_luminance
) + strlen(psz_adjust_range
));
1935 msg_Err(vd
, "no room for the Pixel Shader");
1937 return E_OUTOFMEMORY
;
1939 sprintf(shader
, globPixelShaderDefault
, sys
->legacy_shader
? "" : "Array", psz_src_transform
,
1940 psz_display_transform
, psz_tone_mapping
, psz_peak_luminance
, psz_adjust_range
, psz_sampler
);
1942 if (!IsRGBShader(format
)) {
1943 msg_Dbg(vd
,"psz_src_transform %s", psz_src_transform
);
1944 msg_Dbg(vd
,"psz_tone_mapping %s", psz_tone_mapping
);
1945 msg_Dbg(vd
,"psz_peak_luminance %s", psz_peak_luminance
);
1946 msg_Dbg(vd
,"psz_display_transform %s", psz_display_transform
);
1947 msg_Dbg(vd
,"psz_adjust_range %s", psz_adjust_range
);
1948 msg_Dbg(vd
,"psz_sampler %s", psz_sampler
);
1953 ID3DBlob
*pPSBlob
= CompileShader(vd
, shader
, true);
1956 return E_INVALIDARG
;
1958 HRESULT hr
= ID3D11Device_CreatePixelShader(sys
->d3d_dev
.d3ddevice
,
1959 (void *)ID3D10Blob_GetBufferPointer(pPSBlob
),
1960 ID3D10Blob_GetBufferSize(pPSBlob
), NULL
, output
);
1962 ID3D10Blob_Release(pPSBlob
);
1966 static bool CanUseTextureArray(vout_display_t
*vd
)
1968 #ifndef HAVE_ID3D11VIDEODECODER
1972 struct wddm_version WDDM
= {
1978 if (D3D11CheckDriverVersion(&vd
->sys
->d3d_dev
, GPU_MANUFACTURER_AMD
, &WDDM
) == VLC_SUCCESS
)
1981 msg_Dbg(vd
, "fallback to legacy shader mode for old AMD drivers");
1986 /* TODO : handle errors better
1987 TODO : seperate out into smaller functions like createshaders */
1988 static int Direct3D11CreateFormatResources(vout_display_t
*vd
, const video_format_t
*fmt
)
1990 vout_display_sys_t
*sys
= vd
->sys
;
1993 sys
->legacy_shader
= !CanUseTextureArray(vd
);
1995 hr
= CompilePixelShader(vd
, sys
->picQuadConfig
, fmt
->transfer
, fmt
->b_color_range_full
, &sys
->picQuadPixelShader
);
1998 #ifdef HAVE_ID3D11VIDEODECODER
1999 if (!sys
->legacy_shader
)
2001 sys
->legacy_shader
= true;
2002 msg_Dbg(vd
, "fallback to legacy shader mode");
2003 hr
= CompilePixelShader(vd
, sys
->picQuadConfig
, fmt
->transfer
, fmt
->b_color_range_full
, &sys
->picQuadPixelShader
);
2008 msg_Err(vd
, "Failed to create the pixel shader. (hr=0x%lX)", hr
);
2009 return VLC_EGENERIC
;
2013 sys
->picQuad
.i_width
= fmt
->i_width
;
2014 sys
->picQuad
.i_height
= fmt
->i_height
;
2015 if (!sys
->legacy_shader
&& is_d3d11_opaque(fmt
->i_chroma
))
2017 sys
->picQuad
.i_width
= (sys
->picQuad
.i_width
+ 0x7F) & ~0x7F;
2018 sys
->picQuad
.i_height
= (sys
->picQuad
.i_height
+ 0x7F) & ~0x7F;
2021 if ( sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_R8G8B8A8_UNORM
&&
2022 sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_B5G6R5_UNORM
)
2024 sys
->picQuad
.i_width
= (sys
->picQuad
.i_width
+ 0x01) & ~0x01;
2025 sys
->picQuad
.i_height
= (sys
->picQuad
.i_height
+ 0x01) & ~0x01;
2028 BEFORE_UPDATE_RECTS
;
2029 UpdateRects(vd
, NULL
, true);
2032 #ifdef HAVE_ID3D11VIDEODECODER
2033 if (!is_d3d11_opaque(fmt
->i_chroma
) || sys
->legacy_shader
)
2035 /* we need a staging texture */
2036 ID3D11Texture2D
*textures
[D3D11_MAX_SHADER_VIEW
] = {0};
2037 video_format_t surface_fmt
= *fmt
;
2038 surface_fmt
.i_width
= sys
->picQuad
.i_width
;
2039 surface_fmt
.i_height
= sys
->picQuad
.i_height
;
2041 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->picQuadConfig
, &surface_fmt
, 1, textures
))
2043 msg_Err(vd
, "Failed to allocate the staging texture");
2044 return VLC_EGENERIC
;
2047 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->picQuadConfig
,
2048 textures
, 0, sys
->stagingSys
.resourceView
))
2050 msg_Err(vd
, "Failed to allocate the staging shader view");
2051 return VLC_EGENERIC
;
2054 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++)
2055 sys
->stagingSys
.texture
[plane
] = textures
[plane
];
2059 vd
->info
.is_slow
= !is_d3d11_opaque(fmt
->i_chroma
) && sys
->picQuadConfig
->formatTexture
!= DXGI_FORMAT_UNKNOWN
;
2063 static int Direct3D11CreateGenericResources(vout_display_t
*vd
)
2065 vout_display_sys_t
*sys
= vd
->sys
;
2068 #if defined(HAVE_ID3D11VIDEODECODER)
2069 sys
->context_lock
= CreateMutexEx( NULL
, NULL
, 0, SYNCHRONIZE
);
2070 ID3D11Device_SetPrivateData( sys
->d3d_dev
.d3ddevice
, &GUID_CONTEXT_MUTEX
, sizeof( sys
->context_lock
), &sys
->context_lock
);
2073 ID3D11BlendState
*pSpuBlendState
;
2074 D3D11_BLEND_DESC spuBlendDesc
= { 0 };
2075 spuBlendDesc
.RenderTarget
[0].BlendEnable
= TRUE
;
2076 spuBlendDesc
.RenderTarget
[0].SrcBlend
= D3D11_BLEND_SRC_ALPHA
;
2077 spuBlendDesc
.RenderTarget
[0].DestBlend
= D3D11_BLEND_INV_SRC_ALPHA
;
2078 spuBlendDesc
.RenderTarget
[0].BlendOp
= D3D11_BLEND_OP_ADD
;
2080 spuBlendDesc
.RenderTarget
[0].SrcBlendAlpha
= D3D11_BLEND_ONE
;
2081 spuBlendDesc
.RenderTarget
[0].DestBlendAlpha
= D3D11_BLEND_ZERO
;
2082 spuBlendDesc
.RenderTarget
[0].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
2084 spuBlendDesc
.RenderTarget
[0].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
2086 spuBlendDesc
.RenderTarget
[1].BlendEnable
= TRUE
;
2087 spuBlendDesc
.RenderTarget
[1].SrcBlend
= D3D11_BLEND_ONE
;
2088 spuBlendDesc
.RenderTarget
[1].DestBlend
= D3D11_BLEND_ZERO
;
2089 spuBlendDesc
.RenderTarget
[1].BlendOp
= D3D11_BLEND_OP_ADD
;
2091 spuBlendDesc
.RenderTarget
[1].SrcBlendAlpha
= D3D11_BLEND_ONE
;
2092 spuBlendDesc
.RenderTarget
[1].DestBlendAlpha
= D3D11_BLEND_ZERO
;
2093 spuBlendDesc
.RenderTarget
[1].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
2095 spuBlendDesc
.RenderTarget
[1].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
2096 hr
= ID3D11Device_CreateBlendState(sys
->d3d_dev
.d3ddevice
, &spuBlendDesc
, &pSpuBlendState
);
2098 msg_Err(vd
, "Could not create SPU blend state. (hr=0x%lX)", hr
);
2099 return VLC_EGENERIC
;
2101 ID3D11DeviceContext_OMSetBlendState(sys
->d3d_dev
.d3dcontext
, pSpuBlendState
, NULL
, 0xFFFFFFFF);
2102 ID3D11BlendState_Release(pSpuBlendState
);
2104 /* disable depth testing as we're only doing 2D
2105 * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
2106 * see http://rastertek.com/dx11tut11.html
2108 D3D11_DEPTH_STENCIL_DESC stencilDesc
;
2109 ZeroMemory(&stencilDesc
, sizeof(stencilDesc
));
2111 ID3D11DepthStencilState
*pDepthStencilState
;
2112 hr
= ID3D11Device_CreateDepthStencilState(sys
->d3d_dev
.d3ddevice
, &stencilDesc
, &pDepthStencilState
);
2113 if (SUCCEEDED(hr
)) {
2114 ID3D11DeviceContext_OMSetDepthStencilState(sys
->d3d_dev
.d3dcontext
, pDepthStencilState
, 0);
2115 ID3D11DepthStencilState_Release(pDepthStencilState
);
2118 hr
= UpdateBackBuffer(vd
);
2120 msg_Err(vd
, "Could not update the backbuffer. (hr=0x%lX)", hr
);
2121 return VLC_EGENERIC
;
2124 if (sys
->d3dregion_format
!= NULL
)
2126 hr
= CompilePixelShader(vd
, sys
->d3dregion_format
, TRANSFER_FUNC_SRGB
, true, &sys
->pSPUPixelShader
);
2129 ID3D11PixelShader_Release(sys
->picQuadPixelShader
);
2130 sys
->picQuadPixelShader
= NULL
;
2131 msg_Err(vd
, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr
);
2132 return VLC_EGENERIC
;
2136 ID3DBlob
*pVSBlob
= CompileShader(vd
, globVertexShaderFlat
, false);
2138 return VLC_EGENERIC
;
2140 hr
= ID3D11Device_CreateVertexShader(sys
->d3d_dev
.d3ddevice
, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2141 ID3D10Blob_GetBufferSize(pVSBlob
), NULL
, &sys
->flatVSShader
);
2144 ID3D11Device_Release(pVSBlob
);
2145 msg_Err(vd
, "Failed to create the flat vertex shader. (hr=0x%lX)", hr
);
2146 return VLC_EGENERIC
;
2149 D3D11_INPUT_ELEMENT_DESC layout
[] = {
2150 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT
, 0, D3D11_APPEND_ALIGNED_ELEMENT
, D3D11_INPUT_PER_VERTEX_DATA
, 0},
2151 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT
, 0, D3D11_APPEND_ALIGNED_ELEMENT
, D3D11_INPUT_PER_VERTEX_DATA
, 0},
2154 ID3D11InputLayout
* pVertexLayout
= NULL
;
2155 hr
= ID3D11Device_CreateInputLayout(sys
->d3d_dev
.d3ddevice
, layout
, 2, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2156 ID3D10Blob_GetBufferSize(pVSBlob
), &pVertexLayout
);
2158 ID3D10Blob_Release(pVSBlob
);
2161 msg_Err(vd
, "Failed to create the vertex input layout. (hr=0x%lX)", hr
);
2162 return VLC_EGENERIC
;
2164 ID3D11DeviceContext_IASetInputLayout(sys
->d3d_dev
.d3dcontext
, pVertexLayout
);
2165 ID3D11InputLayout_Release(pVertexLayout
);
2167 pVSBlob
= CompileShader(vd
, globVertexShaderProjection
, false);
2169 return VLC_EGENERIC
;
2171 hr
= ID3D11Device_CreateVertexShader(sys
->d3d_dev
.d3ddevice
, (void *)ID3D10Blob_GetBufferPointer(pVSBlob
),
2172 ID3D10Blob_GetBufferSize(pVSBlob
), NULL
, &sys
->projectionVSShader
);
2175 ID3D11Device_Release(pVSBlob
);
2176 msg_Err(vd
, "Failed to create the projection vertex shader. (hr=0x%lX)", hr
);
2177 return VLC_EGENERIC
;
2179 ID3D10Blob_Release(pVSBlob
);
2181 ID3D11DeviceContext_IASetPrimitiveTopology(sys
->d3d_dev
.d3dcontext
, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
);
2183 UpdatePicQuadPosition(vd
);
2185 D3D11_SAMPLER_DESC sampDesc
;
2186 memset(&sampDesc
, 0, sizeof(sampDesc
));
2187 sampDesc
.Filter
= D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
;
2188 sampDesc
.AddressU
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2189 sampDesc
.AddressV
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2190 sampDesc
.AddressW
= D3D11_TEXTURE_ADDRESS_CLAMP
;
2191 sampDesc
.ComparisonFunc
= D3D11_COMPARISON_ALWAYS
;
2192 sampDesc
.MinLOD
= 0;
2193 sampDesc
.MaxLOD
= D3D11_FLOAT32_MAX
;
2195 ID3D11SamplerState
*d3dsampState
[2];
2196 hr
= ID3D11Device_CreateSamplerState(sys
->d3d_dev
.d3ddevice
, &sampDesc
, &d3dsampState
[0]);
2198 msg_Err(vd
, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr
);
2199 return VLC_EGENERIC
;
2202 sampDesc
.Filter
= D3D11_FILTER_MIN_MAG_MIP_POINT
;
2203 hr
= ID3D11Device_CreateSamplerState(sys
->d3d_dev
.d3ddevice
, &sampDesc
, &d3dsampState
[1]);
2205 msg_Err(vd
, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr
);
2206 ID3D11SamplerState_Release(d3dsampState
[0]);
2207 return VLC_EGENERIC
;
2210 ID3D11DeviceContext_PSSetSamplers(sys
->d3d_dev
.d3dcontext
, 0, 2, d3dsampState
);
2211 ID3D11SamplerState_Release(d3dsampState
[0]);
2212 ID3D11SamplerState_Release(d3dsampState
[1]);
2214 msg_Dbg(vd
, "Direct3D11 resources created");
2218 static void Direct3D11DestroyPool(vout_display_t
*vd
)
2220 vout_display_sys_t
*sys
= vd
->sys
;
2223 picture_pool_Release(sys
->sys
.pool
);
2224 sys
->sys
.pool
= NULL
;
2228 * Compute the vertex ordering needed to rotate the video. Without
2229 * rotation, the vertices of the rectangle are defined in a counterclockwise
2230 * order. This function computes a remapping of the coordinates to
2231 * implement the rotation, given fixed texture coordinates.
2232 * The unrotated order is the following:
2236 * For a 180 degrees rotation it should like this:
2240 * Vertex 0 should be assigned coordinates at index 2 from the
2241 * unrotated order and so on, thus yielding order: 2 3 0 1.
2243 static void orientationVertexOrder(video_orientation_t orientation
, int vertex_order
[static 4])
2245 switch (orientation
) {
2246 case ORIENT_ROTATED_90
:
2247 vertex_order
[0] = 3;
2248 vertex_order
[1] = 0;
2249 vertex_order
[2] = 1;
2250 vertex_order
[3] = 2;
2252 case ORIENT_ROTATED_270
:
2253 vertex_order
[0] = 1;
2254 vertex_order
[1] = 2;
2255 vertex_order
[2] = 3;
2256 vertex_order
[3] = 0;
2258 case ORIENT_ROTATED_180
:
2259 vertex_order
[0] = 2;
2260 vertex_order
[1] = 3;
2261 vertex_order
[2] = 0;
2262 vertex_order
[3] = 1;
2264 case ORIENT_TRANSPOSED
:
2265 vertex_order
[0] = 2;
2266 vertex_order
[1] = 1;
2267 vertex_order
[2] = 0;
2268 vertex_order
[3] = 3;
2270 case ORIENT_HFLIPPED
:
2271 vertex_order
[0] = 1;
2272 vertex_order
[1] = 0;
2273 vertex_order
[2] = 3;
2274 vertex_order
[3] = 2;
2276 case ORIENT_VFLIPPED
:
2277 vertex_order
[0] = 3;
2278 vertex_order
[1] = 2;
2279 vertex_order
[2] = 1;
2280 vertex_order
[3] = 0;
2282 case ORIENT_ANTI_TRANSPOSED
: /* transpose + vflip */
2283 vertex_order
[0] = 0;
2284 vertex_order
[1] = 3;
2285 vertex_order
[2] = 2;
2286 vertex_order
[3] = 1;
2289 vertex_order
[0] = 0;
2290 vertex_order
[1] = 1;
2291 vertex_order
[2] = 2;
2292 vertex_order
[3] = 3;
2297 static void SetupQuadFlat(d3d_vertex_t
*dst_data
, const RECT
*output
,
2298 const d3d_quad_t
*quad
,
2299 WORD
*triangle_pos
, video_orientation_t orientation
)
2301 unsigned int src_width
= quad
->i_width
;
2302 unsigned int src_height
= quad
->i_height
;
2303 float MidY
= (output
->top
+ output
->bottom
) / 2.f
;
2304 float MidX
= (output
->left
+ output
->right
) / 2.f
;
2306 float top
, bottom
, left
, right
;
2307 top
= MidY
/ (MidY
- output
->top
);
2308 bottom
= -(src_height
- MidY
) / (output
->bottom
- MidY
);
2309 left
= -MidX
/ (MidX
- output
->left
);
2310 right
= (src_width
- MidX
) / (output
->right
- MidX
);
2312 const float vertices_coords
[4][2] = {
2319 /* Compute index remapping necessary to implement the rotation. */
2320 int vertex_order
[4];
2321 orientationVertexOrder(orientation
, vertex_order
);
2323 for (int i
= 0; i
< 4; ++i
) {
2324 dst_data
[i
].position
.x
= vertices_coords
[vertex_order
[i
]][0];
2325 dst_data
[i
].position
.y
= vertices_coords
[vertex_order
[i
]][1];
2329 dst_data
[0].position
.z
= 0.0f
;
2330 dst_data
[0].texture
.x
= 0.0f
;
2331 dst_data
[0].texture
.y
= 1.0f
;
2334 dst_data
[1].position
.z
= 0.0f
;
2335 dst_data
[1].texture
.x
= 1.0f
;
2336 dst_data
[1].texture
.y
= 1.0f
;
2339 dst_data
[2].position
.z
= 0.0f
;
2340 dst_data
[2].texture
.x
= 1.0f
;
2341 dst_data
[2].texture
.y
= 0.0f
;
2344 dst_data
[3].position
.z
= 0.0f
;
2345 dst_data
[3].texture
.x
= 0.0f
;
2346 dst_data
[3].texture
.y
= 0.0f
;
2348 /* Make sure surfaces are facing the right way */
2349 if( orientation
== ORIENT_TOP_RIGHT
|| orientation
== ORIENT_BOTTOM_LEFT
2350 || orientation
== ORIENT_LEFT_TOP
|| orientation
== ORIENT_RIGHT_BOTTOM
)
2352 triangle_pos
[0] = 0;
2353 triangle_pos
[1] = 1;
2354 triangle_pos
[2] = 3;
2356 triangle_pos
[3] = 3;
2357 triangle_pos
[4] = 1;
2358 triangle_pos
[5] = 2;
2362 triangle_pos
[0] = 3;
2363 triangle_pos
[1] = 1;
2364 triangle_pos
[2] = 0;
2366 triangle_pos
[3] = 2;
2367 triangle_pos
[4] = 1;
2368 triangle_pos
[5] = 3;
2372 #define SPHERE_SLICES 128
2373 #define nbLatBands SPHERE_SLICES
2374 #define nbLonBands SPHERE_SLICES
2376 static void SetupQuadSphere(d3d_vertex_t
*dst_data
, WORD
*triangle_pos
)
2378 for (unsigned lat
= 0; lat
<= nbLatBands
; lat
++) {
2379 float theta
= lat
* (float) M_PI
/ nbLatBands
;
2380 float sinTheta
, cosTheta
;
2382 sincosf(theta
, &sinTheta
, &cosTheta
);
2384 for (unsigned lon
= 0; lon
<= nbLonBands
; lon
++) {
2385 float phi
= lon
* 2 * (float) M_PI
/ nbLonBands
;
2386 float sinPhi
, cosPhi
;
2388 sincosf(phi
, &sinPhi
, &cosPhi
);
2390 float x
= cosPhi
* sinTheta
;
2392 float z
= sinPhi
* sinTheta
;
2394 unsigned off1
= lat
* (nbLonBands
+ 1) + lon
;
2395 dst_data
[off1
].position
.x
= SPHERE_RADIUS
* x
;
2396 dst_data
[off1
].position
.y
= SPHERE_RADIUS
* y
;
2397 dst_data
[off1
].position
.z
= SPHERE_RADIUS
* z
;
2399 dst_data
[off1
].texture
.x
= lon
/ (float) nbLonBands
; // 0(left) to 1(right)
2400 dst_data
[off1
].texture
.y
= lat
/ (float) nbLatBands
; // 0(top) to 1 (bottom)
2404 for (unsigned lat
= 0; lat
< nbLatBands
; lat
++) {
2405 for (unsigned lon
= 0; lon
< nbLonBands
; lon
++) {
2406 unsigned first
= (lat
* (nbLonBands
+ 1)) + lon
;
2407 unsigned second
= first
+ nbLonBands
+ 1;
2409 unsigned off
= (lat
* nbLatBands
+ lon
) * 3 * 2;
2411 triangle_pos
[off
] = first
;
2412 triangle_pos
[off
+ 1] = first
+ 1;
2413 triangle_pos
[off
+ 2] = second
;
2415 triangle_pos
[off
+ 3] = second
;
2416 triangle_pos
[off
+ 4] = first
+ 1;
2417 triangle_pos
[off
+ 5] = second
+ 1;
2422 static bool AllocQuadVertices(vout_display_t
*vd
, d3d_quad_t
*quad
,
2423 video_projection_mode_t projection
)
2426 vout_display_sys_t
*sys
= vd
->sys
;
2428 if (projection
== PROJECTION_MODE_RECTANGULAR
)
2430 quad
->vertexCount
= 4;
2431 quad
->indexCount
= 2 * 3;
2433 else if (projection
== PROJECTION_MODE_EQUIRECTANGULAR
)
2435 quad
->vertexCount
= (SPHERE_SLICES
+1) * (SPHERE_SLICES
+1);
2436 quad
->indexCount
= nbLatBands
* nbLonBands
* 2 * 3;
2440 msg_Warn(vd
, "Projection mode %d not handled", projection
);
2444 D3D11_BUFFER_DESC bd
;
2445 memset(&bd
, 0, sizeof(bd
));
2446 bd
.Usage
= D3D11_USAGE_DYNAMIC
;
2447 bd
.ByteWidth
= sizeof(d3d_vertex_t
) * quad
->vertexCount
;
2448 bd
.BindFlags
= D3D11_BIND_VERTEX_BUFFER
;
2449 bd
.CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
;
2451 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &bd
, NULL
, &quad
->pVertexBuffer
);
2453 msg_Err(vd
, "Failed to create vertex buffer. (hr=%lX)", hr
);
2457 /* create the index of the vertices */
2458 D3D11_BUFFER_DESC quadDesc
= {
2459 .Usage
= D3D11_USAGE_DYNAMIC
,
2460 .ByteWidth
= sizeof(WORD
) * quad
->indexCount
,
2461 .BindFlags
= D3D11_BIND_INDEX_BUFFER
,
2462 .CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
,
2465 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &quadDesc
, NULL
, &quad
->pIndexBuffer
);
2467 msg_Err(vd
, "Could not create the quad indices. (hr=0x%lX)", hr
);
2474 static bool UpdateQuadPosition( vout_display_t
*vd
, d3d_quad_t
*quad
,
2476 video_projection_mode_t projection
,
2477 video_orientation_t orientation
)
2479 vout_display_sys_t
*sys
= vd
->sys
;
2481 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2483 /* create the vertices */
2484 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2486 msg_Err(vd
, "Failed to lock the vertex buffer (hr=0x%lX)", hr
);
2489 d3d_vertex_t
*dst_data
= mappedResource
.pData
;
2491 /* create the vertex indices */
2492 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pIndexBuffer
, 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2494 msg_Err(vd
, "Failed to lock the index buffer (hr=0x%lX)", hr
);
2495 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0);
2498 WORD
*triangle_pos
= mappedResource
.pData
;
2500 if ( projection
== PROJECTION_MODE_RECTANGULAR
)
2501 SetupQuadFlat(dst_data
, output
, quad
, triangle_pos
, orientation
);
2503 SetupQuadSphere(dst_data
, triangle_pos
);
2505 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pIndexBuffer
, 0);
2506 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pVertexBuffer
, 0);
2511 static int SetupQuad(vout_display_t
*vd
, const video_format_t
*fmt
, d3d_quad_t
*quad
,
2513 const d3d_format_t
*cfg
, ID3D11PixelShader
*d3dpixelShader
,
2514 video_projection_mode_t projection
, video_orientation_t orientation
)
2516 vout_display_sys_t
*sys
= vd
->sys
;
2518 const bool RGB_shader
= IsRGBShader(cfg
);
2520 unsigned src_luminance_peak
;
2521 switch (fmt
->transfer
)
2523 case TRANSFER_FUNC_SMPTE_ST2084
:
2524 /* that's the default PQ value if the metadata are not set */
2525 src_luminance_peak
= MAX_PQ_BRIGHTNESS
;
2527 case TRANSFER_FUNC_HLG
:
2528 src_luminance_peak
= 1000;
2530 case TRANSFER_FUNC_BT470_BG
:
2531 case TRANSFER_FUNC_BT470_M
:
2532 case TRANSFER_FUNC_BT709
:
2533 case TRANSFER_FUNC_SRGB
:
2534 src_luminance_peak
= DEFAULT_BRIGHTNESS
;
2537 msg_Dbg(vd
, "unhandled source transfer %d", fmt
->transfer
);
2538 src_luminance_peak
= DEFAULT_BRIGHTNESS
;
2541 quad
->f_luminance_scale
= (float)src_luminance_peak
/ (float)sys
->display
.luminance_peak
;
2543 /* pixel shader constant buffer */
2544 PS_CONSTANT_BUFFER defaultConstants
;
2545 defaultConstants
.Opacity
= 1.0;
2546 if (fmt
->i_visible_width
== fmt
->i_width
)
2547 defaultConstants
.BoundaryX
= 1.0; /* let texture clamping happen */
2549 defaultConstants
.BoundaryX
= (FLOAT
) (fmt
->i_visible_width
- 1) / fmt
->i_width
;
2550 if (fmt
->i_visible_height
== fmt
->i_height
)
2551 defaultConstants
.BoundaryY
= 1.0; /* let texture clamping happen */
2553 defaultConstants
.BoundaryY
= (FLOAT
) (fmt
->i_visible_height
- 1) / fmt
->i_height
;
2554 defaultConstants
.LuminanceScale
= quad
->f_luminance_scale
;
2556 static_assert((sizeof(PS_CONSTANT_BUFFER
)%16)==0,"Constant buffers require 16-byte alignment");
2557 D3D11_BUFFER_DESC constantDesc
= {
2558 .Usage
= D3D11_USAGE_DYNAMIC
,
2559 .ByteWidth
= sizeof(PS_CONSTANT_BUFFER
),
2560 .BindFlags
= D3D11_BIND_CONSTANT_BUFFER
,
2561 .CPUAccessFlags
= D3D11_CPU_ACCESS_WRITE
,
2563 D3D11_SUBRESOURCE_DATA constantInit
= { .pSysMem
= &defaultConstants
};
2564 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, &constantInit
, &quad
->pPixelShaderConstants
[0]);
2566 msg_Err(vd
, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr
);
2570 FLOAT itu_black_level
= 0.f
;
2571 FLOAT itu_achromacy
= 0.f
;
2574 switch (cfg
->bitsPerChannel
)
2577 /* Rec. ITU-R BT.709-6 §4.6 */
2578 itu_black_level
= 16.f
/ 255.f
;
2579 itu_achromacy
= 128.f
/ 255.f
;
2582 /* Rec. ITU-R BT.709-6 §4.6 */
2583 itu_black_level
= 64.f
/ 1023.f
;
2584 itu_achromacy
= 512.f
/ 1023.f
;
2587 /* Rec. ITU-R BT.2020-2 Table 5 */
2588 itu_black_level
= 256.f
/ 4095.f
;
2589 itu_achromacy
= 2048.f
/ 4095.f
;
2592 /* unknown bitdepth, use approximation for infinite bit depth */
2593 itu_black_level
= 16.f
/ 256.f
;
2594 itu_achromacy
= 128.f
/ 256.f
;
2599 static const FLOAT IDENTITY_4X4
[4 * 4] = {
2606 /* matrices for studio range */
2607 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion, in studio range */
2608 static const FLOAT COLORSPACE_BT601_TO_FULL
[4*4] = {
2609 1.164383561643836f
, 0.f
, 1.596026785714286f
, 0.f
,
2610 1.164383561643836f
, -0.391762290094914f
, -0.812967647237771f
, 0.f
,
2611 1.164383561643836f
, 2.017232142857142f
, 0.f
, 0.f
,
2614 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.709_conversion, in studio range */
2615 static const FLOAT COLORSPACE_BT709_TO_FULL
[4*4] = {
2616 1.164383561643836f
, 0.f
, 1.792741071428571f
, 0.f
,
2617 1.164383561643836f
, -0.213248614273730f
, -0.532909328559444f
, 0.f
,
2618 1.164383561643836f
, 2.112401785714286f
, 0.f
, 0.f
,
2621 /* see https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion, in studio range */
2622 static const FLOAT COLORSPACE_BT2020_TO_FULL
[4*4] = {
2623 1.164383561643836f
, 0.000000000000f
, 1.678674107143f
, 0.f
,
2624 1.164383561643836f
, -0.127007098661f
, -0.440987687946f
, 0.f
,
2625 1.164383561643836f
, 2.141772321429f
, 0.000000000000f
, 0.f
,
2629 PS_COLOR_TRANSFORM colorspace
;
2631 memcpy(colorspace
.WhitePoint
, IDENTITY_4X4
, sizeof(colorspace
.WhitePoint
));
2633 const FLOAT
*ppColorspace
;
2635 ppColorspace
= IDENTITY_4X4
;
2637 switch (fmt
->space
){
2638 case COLOR_SPACE_BT709
:
2639 ppColorspace
= COLORSPACE_BT709_TO_FULL
;
2641 case COLOR_SPACE_BT2020
:
2642 ppColorspace
= COLORSPACE_BT2020_TO_FULL
;
2644 case COLOR_SPACE_BT601
:
2645 ppColorspace
= COLORSPACE_BT601_TO_FULL
;
2648 case COLOR_SPACE_UNDEF
:
2649 if( fmt
->i_height
> 576 )
2650 ppColorspace
= COLORSPACE_BT709_TO_FULL
;
2652 ppColorspace
= COLORSPACE_BT601_TO_FULL
;
2655 /* all matrices work in studio range and output in full range */
2656 colorspace
.WhitePoint
[0*4 + 3] = -itu_black_level
;
2657 colorspace
.WhitePoint
[1*4 + 3] = -itu_achromacy
;
2658 colorspace
.WhitePoint
[2*4 + 3] = -itu_achromacy
;
2661 memcpy(colorspace
.Colorspace
, ppColorspace
, sizeof(colorspace
.Colorspace
));
2663 constantInit
.pSysMem
= &colorspace
;
2665 static_assert((sizeof(PS_COLOR_TRANSFORM
)%16)==0,"Constant buffers require 16-byte alignment");
2666 constantDesc
.ByteWidth
= sizeof(PS_COLOR_TRANSFORM
);
2667 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, &constantInit
, &quad
->pPixelShaderConstants
[1]);
2669 msg_Err(vd
, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr
);
2672 quad
->PSConstantsCount
= 2;
2674 /* vertex shader constant buffer */
2675 if ( projection
== PROJECTION_MODE_EQUIRECTANGULAR
)
2677 constantDesc
.ByteWidth
= sizeof(VS_PROJECTION_CONST
);
2678 static_assert((sizeof(VS_PROJECTION_CONST
)%16)==0,"Constant buffers require 16-byte alignment");
2679 hr
= ID3D11Device_CreateBuffer(sys
->d3d_dev
.d3ddevice
, &constantDesc
, NULL
, &quad
->pVertexShaderConstants
);
2681 msg_Err(vd
, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr
);
2685 SetQuadVSProjection( vd
, quad
, &vd
->cfg
->viewpoint
);
2688 quad
->picSys
.formatTexture
= cfg
->formatTexture
;
2689 quad
->picSys
.context
= sys
->d3d_dev
.d3dcontext
;
2690 ID3D11DeviceContext_AddRef(quad
->picSys
.context
);
2692 if (!AllocQuadVertices(vd
, quad
, projection
))
2694 if (!UpdateQuadPosition(vd
, quad
, output
, projection
, orientation
))
2697 quad
->d3dpixelShader
= d3dpixelShader
;
2698 if (projection
== PROJECTION_MODE_RECTANGULAR
)
2699 quad
->d3dvertexShader
= sys
->flatVSShader
;
2701 quad
->d3dvertexShader
= sys
->projectionVSShader
;
2707 return VLC_EGENERIC
;
2710 static void ReleaseQuad(d3d_quad_t
*quad
)
2712 if (quad
->pPixelShaderConstants
[0])
2714 ID3D11Buffer_Release(quad
->pPixelShaderConstants
[0]);
2715 quad
->pPixelShaderConstants
[0] = NULL
;
2717 if (quad
->pPixelShaderConstants
[1])
2719 ID3D11Buffer_Release(quad
->pPixelShaderConstants
[1]);
2720 quad
->pPixelShaderConstants
[1] = NULL
;
2722 if (quad
->pVertexBuffer
)
2724 ID3D11Buffer_Release(quad
->pVertexBuffer
);
2725 quad
->pVertexBuffer
= NULL
;
2727 quad
->d3dvertexShader
= NULL
;
2728 if (quad
->pIndexBuffer
)
2730 ID3D11Buffer_Release(quad
->pIndexBuffer
);
2731 quad
->pIndexBuffer
= NULL
;
2733 if (quad
->pVertexShaderConstants
)
2735 ID3D11Buffer_Release(quad
->pVertexShaderConstants
);
2736 quad
->pVertexShaderConstants
= NULL
;
2738 ReleasePictureSys(&quad
->picSys
);
2741 static void Direct3D11DestroyResources(vout_display_t
*vd
)
2743 vout_display_sys_t
*sys
= vd
->sys
;
2745 Direct3D11DestroyPool(vd
);
2747 ReleaseQuad(&sys
->picQuad
);
2748 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
2749 sys
->d3dregion_count
= 0;
2751 ReleasePictureSys(&sys
->stagingSys
);
2753 if (sys
->flatVSShader
)
2755 ID3D11VertexShader_Release(sys
->flatVSShader
);
2756 sys
->flatVSShader
= NULL
;
2758 if (sys
->projectionVSShader
)
2760 ID3D11VertexShader_Release(sys
->projectionVSShader
);
2761 sys
->projectionVSShader
= NULL
;
2763 if (sys
->d3drenderTargetView
)
2765 ID3D11RenderTargetView_Release(sys
->d3drenderTargetView
);
2766 sys
->d3drenderTargetView
= NULL
;
2768 if (sys
->d3ddepthStencilView
)
2770 ID3D11DepthStencilView_Release(sys
->d3ddepthStencilView
);
2771 sys
->d3ddepthStencilView
= NULL
;
2773 if (sys
->pSPUPixelShader
)
2775 ID3D11PixelShader_Release(sys
->pSPUPixelShader
);
2776 sys
->pSPUPixelShader
= NULL
;
2778 if (sys
->picQuadPixelShader
)
2780 ID3D11PixelShader_Release(sys
->picQuadPixelShader
);
2781 sys
->picQuadPixelShader
= NULL
;
2783 #if defined(HAVE_ID3D11VIDEODECODER)
2784 if( sys
->context_lock
!= INVALID_HANDLE_VALUE
)
2786 CloseHandle( sys
->context_lock
);
2787 sys
->context_lock
= INVALID_HANDLE_VALUE
;
2791 msg_Dbg(vd
, "Direct3D11 resources destroyed");
2794 static void Direct3D11DeleteRegions(int count
, picture_t
**region
)
2796 for (int i
= 0; i
< count
; ++i
) {
2798 picture_Release(region
[i
]);
2804 static void DestroyPictureQuad(picture_t
*p_picture
)
2806 ReleaseQuad( (d3d_quad_t
*) p_picture
->p_sys
);
2810 static void UpdateQuadOpacity(vout_display_t
*vd
, const d3d_quad_t
*quad
, float opacity
)
2812 vout_display_sys_t
*sys
= vd
->sys
;
2813 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2815 HRESULT hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
2816 if (SUCCEEDED(hr
)) {
2817 PS_CONSTANT_BUFFER
*dst_data
= mappedResource
.pData
;
2818 dst_data
->Opacity
= opacity
;
2819 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, (ID3D11Resource
*)quad
->pPixelShaderConstants
[0], 0);
2822 msg_Err(vd
, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr
);
2826 static int Direct3D11MapSubpicture(vout_display_t
*vd
, int *subpicture_region_count
,
2827 picture_t
***region
, subpicture_t
*subpicture
)
2829 vout_display_sys_t
*sys
= vd
->sys
;
2830 D3D11_MAPPED_SUBRESOURCE mappedResource
;
2831 D3D11_TEXTURE2D_DESC texDesc
;
2835 if (sys
->d3dregion_format
== NULL
)
2836 return VLC_EGENERIC
;
2839 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
)
2842 *region
= calloc(count
, sizeof(picture_t
*));
2843 if (unlikely(*region
==NULL
))
2845 *subpicture_region_count
= count
;
2848 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
, i
++) {
2849 if (!r
->fmt
.i_width
|| !r
->fmt
.i_height
)
2850 continue; // won't render anything, keep the cache for later
2852 for (int j
= 0; j
< sys
->d3dregion_count
; j
++) {
2853 picture_t
*cache
= sys
->d3dregions
[j
];
2854 if (cache
!= NULL
&& ((d3d_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
]) {
2855 ID3D11Texture2D_GetDesc( ((d3d_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
], &texDesc
);
2856 if (texDesc
.Format
== sys
->d3dregion_format
->formatTexture
&&
2857 texDesc
.Width
== r
->fmt
.i_visible_width
&&
2858 texDesc
.Height
== r
->fmt
.i_visible_height
) {
2859 (*region
)[i
] = cache
;
2860 memset(&sys
->d3dregions
[j
], 0, sizeof(cache
)); // do not reuse this cached value
2866 picture_t
*quad_picture
= (*region
)[i
];
2867 if (quad_picture
== NULL
) {
2868 ID3D11Texture2D
*textures
[D3D11_MAX_SHADER_VIEW
] = {0};
2869 d3d_quad_t
*d3dquad
= calloc(1, sizeof(*d3dquad
));
2870 if (unlikely(d3dquad
==NULL
)) {
2873 if (AllocateTextures(VLC_OBJECT(vd
), &sys
->d3d_dev
, sys
->d3dregion_format
, &r
->fmt
, 1, textures
)) {
2874 msg_Err(vd
, "Failed to allocate %dx%d texture for OSD",
2875 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2876 for (int j
=0; j
<D3D11_MAX_SHADER_VIEW
; j
++)
2878 ID3D11Texture2D_Release(textures
[j
]);
2883 for (unsigned plane
= 0; plane
< D3D11_MAX_SHADER_VIEW
; plane
++) {
2884 d3dquad
->picSys
.texture
[plane
] = textures
[plane
];
2886 if (AllocateShaderView(VLC_OBJECT(vd
), sys
->d3d_dev
.d3ddevice
, sys
->d3dregion_format
,
2887 d3dquad
->picSys
.texture
, 0,
2888 d3dquad
->picSys
.resourceView
)) {
2889 msg_Err(vd
, "Failed to create %dx%d shader view for OSD",
2890 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2894 d3dquad
->i_width
= r
->fmt
.i_width
;
2895 d3dquad
->i_height
= r
->fmt
.i_height
;
2897 output
.left
= r
->fmt
.i_x_offset
;
2898 output
.right
= r
->fmt
.i_x_offset
+ r
->fmt
.i_width
;
2899 output
.top
= r
->fmt
.i_y_offset
;
2900 output
.bottom
= r
->fmt
.i_y_offset
+ r
->fmt
.i_height
;
2902 err
= SetupQuad( vd
, &r
->fmt
, d3dquad
, &output
,
2903 sys
->d3dregion_format
, sys
->pSPUPixelShader
,
2904 PROJECTION_MODE_RECTANGULAR
, ORIENT_NORMAL
);
2905 if (err
!= VLC_SUCCESS
) {
2906 msg_Err(vd
, "Failed to create %dx%d quad for OSD",
2907 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
2911 picture_resource_t picres
= {
2912 .p_sys
= (picture_sys_t
*) d3dquad
,
2913 .pf_destroy
= DestroyPictureQuad
,
2915 (*region
)[i
] = picture_NewFromResource(&r
->fmt
, &picres
);
2916 if ((*region
)[i
] == NULL
) {
2917 msg_Err(vd
, "Failed to create %dx%d picture for OSD",
2918 r
->fmt
.i_width
, r
->fmt
.i_height
);
2919 ReleaseQuad(d3dquad
);
2922 quad_picture
= (*region
)[i
];
2925 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
);
2926 if( SUCCEEDED(hr
) ) {
2927 err
= CommonUpdatePicture(quad_picture
, NULL
, mappedResource
.pData
, mappedResource
.RowPitch
);
2928 if (err
!= VLC_SUCCESS
) {
2929 msg_Err(vd
, "Failed to set the buffer on the SPU picture" );
2930 picture_Release(quad_picture
);
2934 picture_CopyPixels(quad_picture
, r
->p_picture
);
2936 ID3D11DeviceContext_Unmap(sys
->d3d_dev
.d3dcontext
, ((d3d_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0);
2938 msg_Err(vd
, "Failed to map the SPU texture (hr=0x%lX)", hr
);
2939 picture_Release(quad_picture
);
2943 d3d_quad_t
*quad
= (d3d_quad_t
*) quad_picture
->p_sys
;
2945 quad
->cropViewport
.Width
= (FLOAT
) r
->fmt
.i_visible_width
* RECTWidth(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_width
;
2946 quad
->cropViewport
.Height
= (FLOAT
) r
->fmt
.i_visible_height
* RECTHeight(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_height
;
2947 quad
->cropViewport
.MinDepth
= 0.0f
;
2948 quad
->cropViewport
.MaxDepth
= 1.0f
;
2949 quad
->cropViewport
.TopLeftX
= sys
->sys
.rect_dest
.left
+ (FLOAT
) r
->i_x
* RECTWidth(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_width
;
2950 quad
->cropViewport
.TopLeftY
= sys
->sys
.rect_dest
.top
+ (FLOAT
) r
->i_y
* RECTHeight(sys
->sys
.rect_dest
) / subpicture
->i_original_picture_height
;
2952 UpdateQuadOpacity(vd
, quad
, r
->i_alpha
/ 255.0f
);