direct3d11: only set successful state when at the end
[vlc.git] / modules / video_output / win32 / direct3d11.c
blob82c5e4013fd057eb2c32a3859931613d463a60e0
1 /*****************************************************************************
2 * direct3d11.c: Windows Direct3D11 video output module
3 *****************************************************************************
4 * Copyright (C) 2014-2015 VLC authors and VideoLAN
6 * Authors: Martell Malone <martellmalone@gmail.com>
7 * Steve Lhomme <robux4@gmail.com>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_WIN7
25 # undef _WIN32_WINNT
26 # define _WIN32_WINNT _WIN32_WINNT_WIN7
27 #endif
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
37 #include <assert.h>
38 #include <math.h>
40 #define COBJMACROS
41 #include <initguid.h>
42 #include <d3d11.h>
43 #ifdef HAVE_DXGI1_6_H
44 # include <dxgi1_6.h>
45 #else
46 # include <dxgi1_5.h>
47 #endif
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"
55 #include "common.h"
57 #if !VLC_WINSTORE_APP
58 # define D3DCompile(args...) sys->OurD3DCompile(args)
59 #endif
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.")
76 vlc_module_begin ()
77 set_shortname("Direct3D11")
78 set_description(N_("Direct3D11 video output"))
79 set_help(D3D11_HELP)
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)
85 #if VLC_WINSTORE_APP
86 add_integer("winrt-d3dcontext", 0x0, NULL, NULL, true); /* ID3D11DeviceContext* */
87 add_integer("winrt-swapchain", 0x0, NULL, NULL, true); /* IDXGISwapChain1* */
88 #endif
90 set_capability("vout display", 300)
91 add_shortcut("direct3d11")
92 set_callbacks(Open, Close)
93 vlc_module_end ()
95 /* A Quad is texture that can be displayed in a rectangle */
96 typedef struct
98 picture_sys_t picSys;
99 ID3D11Buffer *pVertexBuffer;
100 UINT vertexCount;
101 ID3D11VertexShader *d3dvertexShader;
102 ID3D11Buffer *pIndexBuffer;
103 UINT indexCount;
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 } d3d_quad_t;
113 typedef enum video_color_axis {
114 COLOR_AXIS_RGB,
115 COLOR_AXIS_YCBCR,
116 } video_color_axis;
118 typedef struct {
119 DXGI_COLOR_SPACE_TYPE dxgi;
120 const char *name;
121 video_color_axis axis;
122 video_color_primaries_t primaries;
123 video_transfer_func_t transfer;
124 video_color_space_t color;
125 bool b_full_range;
126 } dxgi_color_space;
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;
135 } display;
137 #if !VLC_WINSTORE_APP
138 HINSTANCE hdxgi_dll; /* handle of the opened dxgi dll */
139 d3d11_handle_t hd3d;
140 HINSTANCE hd3dcompiler_dll; /* handle of the opened d3dcompiler dll */
141 pD3DCompile OurD3DCompile;
142 #endif
143 #if defined(HAVE_ID3D11VIDEODECODER)
144 HANDLE context_lock; /* D3D11 Context lock necessary
145 for hw decoding */
146 #endif
147 IDXGISwapChain1 *dxgiswapChain; /* DXGI 1.2 swap chain */
148 IDXGISwapChain4 *dxgiswapChain4; /* DXGI 1.5 for HDR */
149 d3d11_device_t d3d_dev;
150 d3d_quad_t picQuad;
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 */
164 bool legacy_shader;
166 // SPU
167 vlc_fourcc_t pSubpictureChromas[2];
168 ID3D11PixelShader *pSPUPixelShader;
169 const d3d_format_t *d3dregion_format;
170 int d3dregion_count;
171 picture_t **d3dregions;
174 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
175 typedef struct d3d_vertex_t {
176 struct {
177 FLOAT x;
178 FLOAT y;
179 FLOAT z;
180 } position;
181 struct {
182 FLOAT x;
183 FLOAT y;
184 } texture;
185 } d3d_vertex_t;
187 typedef struct {
188 FLOAT Opacity;
189 FLOAT opacityPadding[3];
190 } PS_CONSTANT_BUFFER;
192 typedef struct {
193 FLOAT WhitePoint[4*4];
194 FLOAT Colorspace[4*4];
195 } PS_COLOR_TRANSFORM;
197 typedef struct {
198 FLOAT RotX[4*4];
199 FLOAT RotY[4*4];
200 FLOAT RotZ[4*4];
201 FLOAT View[4*4];
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,
234 const RECT *output,
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 = "\
245 struct VS_INPUT\
247 float4 Position : POSITION;\
248 float4 Texture : TEXCOORD0;\
251 struct VS_OUTPUT\
253 float4 Position : SV_POSITION;\
254 float4 Texture : TEXCOORD0;\
257 VS_OUTPUT main( VS_INPUT In )\
259 return In;\
263 #define STRINGIZE2(s) #s
264 #define STRINGIZE(s) STRINGIZE2(s)
266 static const char* globVertexShaderProjection = "\
267 cbuffer VS_PROJECTION_CONST : register(b0)\
269 float4x4 RotX;\
270 float4x4 RotY;\
271 float4x4 RotZ;\
272 float4x4 View;\
273 float4x4 Projection;\
275 struct VS_INPUT\
277 float4 Position : POSITION;\
278 float4 Texture : TEXCOORD0;\
281 struct VS_OUTPUT\
283 float4 Position : SV_POSITION;\
284 float4 Texture : TEXCOORD0;\
287 VS_OUTPUT main( VS_INPUT In )\
289 VS_OUTPUT Output;\
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;\
298 return Output;\
302 static const char* globPixelShaderDefault = "\
303 cbuffer PS_CONSTANT_BUFFER : register(b0)\
305 float Opacity;\
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;\
316 struct PS_INPUT\
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) {\
329 x.r = hable(x.r);\
330 x.g = hable(x.g);\
331 x.b = hable(x.b);\
332 return 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² */\
341 if (x <= 0.5)\
342 x = x * x * B67_inv_r2;\
343 else\
344 x = exp((x - B67_c) / B67_a) + B67_b;\
345 return x;\
348 inline float3 sourceToLinear(float3 rgb) {\
349 %s;\
352 inline float3 linearToDisplay(float3 rgb) {\
353 %s;\
356 inline float3 toneMapping(float3 rgb) {\
357 %s;\
360 inline float3 adjustPeakLuminance(float3 rgb) {\
361 %s;\
364 inline float3 adjustRange(float3 rgb) {\
365 %s;\
368 float4 main( PS_INPUT In ) : SV_TARGET\
370 float4 sample;\
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;
395 HRESULT hr;
396 hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
397 if( FAILED(hr) )
399 return VLC_EGENERIC;
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));
414 if (!sys)
415 return VLC_ENOMEM;
417 if (D3D11_Create(vd, &sys->hd3d) != VLC_SUCCESS)
418 return VLC_EGENERIC;
420 sys->hd3dcompiler_dll = Direct3D11LoadShaderLibrary();
421 if (!sys->hd3dcompiler_dll) {
422 msg_Err(vd, "cannot load d3dcompiler.dll, aborting");
423 Direct3D11Destroy(vd);
424 return VLC_EGENERIC;
427 sys->OurD3DCompile = (void *)GetProcAddress(sys->hd3dcompiler_dll, "D3DCompile");
428 if (!sys->OurD3DCompile) {
429 msg_Err(vd, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
430 Direct3D11Destroy(vd);
431 return VLC_EGENERIC;
433 return VLC_SUCCESS;
435 #else
436 static int OpenCoreW(vout_display_t *vd)
438 IDXGISwapChain1* dxgiswapChain = var_InheritInteger(vd, "winrt-swapchain");
439 if (!dxgiswapChain)
440 return VLC_EGENERIC;
441 ID3D11DeviceContext* d3dcontext = var_InheritInteger(vd, "winrt-d3dcontext");
442 if (!d3dcontext)
443 return VLC_EGENERIC;
444 ID3D11Device* d3ddevice = NULL;
445 ID3D11DeviceContext_GetDevice(d3dcontext, &d3ddevice);
446 if (!d3ddevice)
447 return VLC_EGENERIC;
449 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
450 if (!sys)
451 return VLC_ENOMEM;
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);
460 return VLC_SUCCESS;
462 #endif
464 #if VLC_WINSTORE_APP
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;
468 out->left = 0;
469 out->top = 0;
470 uint32_t i_width;
471 uint32_t i_height;
472 UINT dataSize = sizeof(i_width);
473 HRESULT hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
474 if (FAILED(hr)) {
475 return false;
477 dataSize = sizeof(i_height);
478 hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
479 if (FAILED(hr)) {
480 return false;
482 out->right = i_width;
483 out->bottom = i_height;
484 return true;
486 #endif
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);
494 #else
495 int ret = OpenCoreW(vd);
496 #endif
498 if (ret != VLC_SUCCESS)
499 return ret;
501 if (CommonInit(vd))
502 goto error;
504 #if VLC_WINSTORE_APP
505 vd->sys->sys.pf_GetRect = GetRect;
506 #endif
508 video_format_t fmt;
509 video_format_Copy(&fmt, &vd->source);
510 if (Direct3D11Open(vd, &fmt)) {
511 msg_Err(vd, "Direct3D11 could not be opened");
512 goto error;
515 video_format_Clean(&vd->fmt);
516 vd->fmt = fmt;
518 #if !VLC_WINSTORE_APP
519 EventThreadUpdateTitle(vd->sys->sys.event, VOUT_TITLE " (Direct3D11 output)");
520 #endif
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;
533 else
534 vd->info.subpicture_chromas = NULL;
536 vd->pool = Pool;
537 vd->prepare = Prepare;
538 vd->display = Display;
539 vd->control = Control;
540 vd->manage = Manage;
542 msg_Dbg(vd, "Direct3D11 Open Succeeded");
544 return VLC_SUCCESS;
546 error:
547 Close(object);
548 return VLC_EGENERIC;
551 static void Close(vlc_object_t *object)
553 vout_display_t * vd = (vout_display_t *)object;
555 Direct3D11Close(vd);
556 CommonClean(vd);
557 Direct3D11Destroy(vd);
558 free(vd->sys);
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 */
564 pool_size += 2;
566 vout_display_sys_t *sys = vd->sys;
567 ID3D11Texture2D *textures[pool_size * D3D11_MAX_SHADER_VIEW];
568 picture_t **pictures = NULL;
569 picture_t *picture;
570 unsigned picture_count = 0;
572 if (sys->sys.pool)
573 return sys->sys.pool;
575 if (vd->info.is_slow)
576 pool_size = 1;
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) {
583 HRESULT hr;
584 ID3D10Multithread *pMultithread;
585 hr = ID3D11Device_QueryInterface( sys->d3d_dev.d3ddevice, &IID_ID3D10Multithread, (void **)&pMultithread);
586 if (SUCCEEDED(hr)) {
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))
595 goto error;
597 pictures = calloc(pool_size, sizeof(*pictures));
598 if (!pictures)
599 goto error;
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))
604 goto error;
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 = {
614 .p_sys = picsys,
615 .pf_destroy = DestroyDisplayPoolPicture,
618 picture = picture_NewFromResource(&surface_fmt, &resource);
619 if (unlikely(picture == NULL)) {
620 free(picsys);
621 msg_Err( vd, "Failed to create picture %d in the pool.", picture_count );
622 goto error;
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))
636 goto error;
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))
643 goto error;
644 } else
645 #endif
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))
651 goto error;
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.");
659 return NULL;
662 if (sys->picQuadConfig->formatTexture == DXGI_FORMAT_UNKNOWN)
663 sys->sys.pool = picture_pool_NewFromFormat( &surface_fmt, pool_size );
664 else
666 picture_pool_configuration_t pool_cfg = {
667 .picture = pictures,
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 );
677 error:
678 if (sys->sys.pool == NULL) {
679 picture_pool_configuration_t pool_cfg = {
680 .picture_count = 0,
682 if (pictures) {
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]);
686 free(pictures);
689 /* create an empty pool to avoid crashing */
690 sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
691 } else {
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 );
702 free(p_sys);
703 free(picture);
706 static HRESULT UpdateBackBuffer(vout_display_t *vd)
708 vout_display_sys_t *sys = vd->sys;
709 HRESULT hr;
710 ID3D11Texture2D* pDepthStencil;
711 ID3D11Texture2D* pBackBuffer;
712 RECT rect;
713 #if VLC_WINSTORE_APP
714 if (!GetRect(&sys->sys, &rect))
715 #endif
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);
732 if (FAILED(hr)) {
733 msg_Err(vd, "Failed to resize the backbuffer. (hr=0x%lX)", hr);
734 return hr;
737 hr = IDXGISwapChain_GetBuffer(sys->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *)&pBackBuffer);
738 if (FAILED(hr)) {
739 msg_Err(vd, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr);
740 return hr;
743 hr = ID3D11Device_CreateRenderTargetView(sys->d3d_dev.d3ddevice, (ID3D11Resource *)pBackBuffer, NULL, &sys->d3drenderTargetView);
744 ID3D11Texture2D_Release(pBackBuffer);
745 if (FAILED(hr)) {
746 msg_Err(vd, "Failed to create the target view. (hr=0x%lX)", hr);
747 return 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);
765 if (FAILED(hr)) {
766 msg_Err(vd, "Could not create the depth stencil texture. (hr=0x%lX)", hr);
767 return 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);
780 if (FAILED(hr)) {
781 msg_Err(vd, "Could not create the depth stencil view. (hr=0x%lX)", hr);
782 return hr;
785 return S_OK;
788 /* rotation around the Z axis */
789 static void getZRotMatrix(float theta, FLOAT matrix[static 16])
791 float st, ct;
793 sincosf(theta, &st, &ct);
795 const FLOAT m[] = {
796 /* x y z w */
797 ct, -st, 0.f, 0.f,
798 st, ct, 0.f, 0.f,
799 0.f, 0.f, 1.f, 0.f,
800 0.f, 0.f, 0.f, 1.f
803 memcpy(matrix, m, sizeof(m));
806 /* rotation around the Y axis */
807 static void getYRotMatrix(float theta, FLOAT matrix[static 16])
809 float st, ct;
811 sincosf(theta, &st, &ct);
813 const FLOAT m[] = {
814 /* x y z w */
815 ct, 0.f, -st, 0.f,
816 0.f, 1.f, 0.f, 0.f,
817 st, 0.f, ct, 0.f,
818 0.f, 0.f, 0.f, 1.f
821 memcpy(matrix, m, sizeof(m));
824 /* rotation around the X axis */
825 static void getXRotMatrix(float phi, FLOAT matrix[static 16])
827 float sp, cp;
829 sincosf(phi, &sp, &cp);
831 const FLOAT m[] = {
832 /* x y z w */
833 1.f, 0.f, 0.f, 0.f,
834 0.f, cp, sp, 0.f,
835 0.f, -sp, cp, 0.f,
836 0.f, 0.f, 0.f, 1.f
839 memcpy(matrix, m, sizeof(m));
842 static void getZoomMatrix(float zoom, FLOAT matrix[static 16]) {
844 const FLOAT m[] = {
845 /* x y z w */
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]) {
858 float zFar = 1000;
859 float zNear = 0.01;
861 float f = 1.f / tanf(fovy / 2.f);
863 const FLOAT m[] = {
864 f / sar, 0.f, 0.f, 0.f,
865 0.f, 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;
890 float f_z;
891 if (f_fovx <= z_thresh * M_PI / 180)
892 f_z = 0;
893 else
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;
897 if (f_z < z_min)
898 f_z = z_min;
900 return f_z;
903 static void SetQuadVSProjection(vout_display_t *vd, d3d_quad_t *quad, const vlc_viewpoint_t *p_vp)
905 if (!quad->pVertexShaderConstants)
906 return;
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 ||
911 f_fovx < -0.001f )
912 return;
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;
922 HRESULT hr;
923 D3D11_MAPPED_SUBRESOURCE mapped;
924 hr = ID3D11DeviceContext_Map(sys->d3d_dev.d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
925 if (SUCCEEDED(hr)) {
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);
934 #undef RAD
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 );
952 #endif
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 );
962 #endif
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;
987 BEFORE_UPDATE_RECTS;
988 int res = CommonControl( vd, query, args );
989 AFTER_UPDATE_RECTS;
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 );
997 res = VLC_SUCCESS;
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) )
1005 UpdateSize(vd);
1008 return res;
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;
1019 CommonManage(vd);
1020 AFTER_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))
1026 UpdateSize(vd);
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);
1033 UINT offset = 0;
1035 /* Render the quad */
1036 /* vertex shader */
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);
1044 /* pixel shader */
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;
1063 int i;
1064 HRESULT hr;
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);
1069 if( FAILED(hr) )
1070 break;
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);
1082 else
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 );
1089 #endif
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);
1095 D3D11_BOX box = {
1096 .top = 0,
1097 .bottom = picture->format.i_y_offset + picture->format.i_visible_height,
1098 .left = 0,
1099 .right = picture->format.i_x_offset + picture->format.i_visible_width,
1100 .back = 1,
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],
1112 0, 0, 0, 0,
1113 p_sys->resource[KNOWN_DXGI_INDEX],
1114 p_sys->slice_index, &box);
1116 else
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);
1136 AFTER_UPDATE_RECTS;
1137 UpdateSize(vd);
1142 if (subpicture) {
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);
1163 else {
1164 picture_sys_t *p_sys = ActivePictureSys(picture);
1165 DisplayD3DPicture(sys, &sys->picQuad, p_sys->resourceView);
1168 if (subpicture) {
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 );
1184 #endif
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);
1221 if (subpicture)
1222 subpicture_Delete(subpicture);
1224 CommonDisplay(vd);
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 );
1239 #endif
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) {
1248 TCHAR filename[19];
1249 _sntprintf(filename, 19, TEXT("D3DCOMPILER_%d.dll"), i);
1250 instance = LoadLibrary(filename);
1251 if (instance) break;
1253 return instance;
1255 #endif
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},
1291 #undef DXGIMAP
1294 static void D3D11SetColorSpace(vout_display_t *vd)
1296 vout_display_sys_t *sys = vd->sys;
1297 HRESULT hr;
1298 int best = -1;
1299 int score, best_score = 0;
1300 UINT support;
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);
1306 if (FAILED(hr)) {
1307 msg_Warn(vd, "could not get a IDXGISwapChain3");
1308 goto done;
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);
1323 score = 0;
1324 if (color_spaces[i].primaries == vd->source.primaries)
1325 score++;
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))
1331 score++;
1332 if (color_spaces[i].b_full_range == src_full_range)
1333 score++;
1334 if (score > best_score || (score && best == -1)) {
1335 best = i;
1336 best_score = score;
1341 if (best == -1)
1343 best = 0;
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)
1361 best = i;
1362 csp = &color_spaces[i];
1363 break;
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 );
1374 #endif
1376 hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
1377 if (SUCCEEDED(hr))
1379 sys->display.colorspace = &color_spaces[best];
1380 msg_Dbg(vd, "using colorspace %s", sys->display.colorspace->name);
1382 else
1383 msg_Err(vd, "Failed to set colorspace %s. (hr=0x%lX)", sys->display.colorspace->name, hr);
1384 done:
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;
1391 break;
1392 case TRANSFER_FUNC_SMPTE_ST2084:
1393 sys->display.luminance_peak = MAX_PQ_BRIGHTNESS;
1394 break;
1395 /* there is no other output transfer on Windows */
1396 default:
1397 vlc_assert_unreachable();
1400 if (dxgiswapChain3)
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;
1421 if (from_processor)
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
1438 HRESULT hr = S_OK;
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;
1452 break;
1453 default:
1454 scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; /* TODO: use DXGI_FORMAT_NV12 */
1455 break;
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),
1462 &sys->d3d_dev);
1463 if (FAILED(hr)) {
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);
1476 if (FAILED(hr)) {
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);
1484 if (FAILED(hr)) {
1485 msg_Err(vd, "Could not create the SwapChain. (hr=0x%lX)", hr);
1486 return VLC_EGENERIC;
1488 #endif
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);
1501 else
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;
1512 break;
1513 case VLC_CODEC_D3D11_OPAQUE_10B:
1514 bits_per_channel = 10;
1515 break;
1516 default:
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;
1521 break;
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;
1554 return VLC_SUCCESS;
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);
1592 #ifndef NDEBUG
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 );
1594 #endif
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);
1609 if (FAILED(hr)) {
1610 char *err = pErrBlob ? ID3D10Blob_GetBufferPointer(pErrBlob) : NULL;
1611 msg_Err(vd, "invalid %s Shader (hr=0x%lX): %s", pixel?"Pixel":"Vertex", hr, err );
1612 if (pErrBlob)
1613 ID3D10Blob_Release(pErrBlob);
1614 return NULL;
1616 return pShaderBlob;
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:
1646 psz_sampler =
1647 "sample.x = shaderTexture[0].Sample(SampleType, In.Texture).x;\
1648 sample.yz = shaderTexture[1].Sample(SampleType, In.Texture).xy;\
1649 sample.a = 1;";
1650 break;
1651 case DXGI_FORMAT_YUY2:
1652 psz_sampler =
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;\
1656 sample.a = 1;";
1657 break;
1658 case DXGI_FORMAT_R8G8B8A8_UNORM:
1659 case DXGI_FORMAT_B8G8R8A8_UNORM:
1660 case DXGI_FORMAT_B8G8R8X8_UNORM:
1661 case DXGI_FORMAT_B5G6R5_UNORM:
1662 psz_sampler =
1663 "sample = shaderTexture[0].Sample(SampleType, In.Texture);";
1664 break;
1665 case DXGI_FORMAT_UNKNOWN:
1666 psz_sampler =
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;\
1670 sample.a = 1;";
1671 break;
1672 default:
1673 vlc_assert_unreachable();
1676 video_transfer_func_t src_transfer;
1677 unsigned src_luminance_peak;
1678 switch (transfer)
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;
1684 else
1685 /* TODO adjust this value during playback using HDR metadata ? */
1686 src_luminance_peak = 5000;
1687 break;
1688 case TRANSFER_FUNC_HLG:
1689 src_luminance_peak = 1000;
1690 break;
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;
1696 break;
1697 default:
1698 msg_Dbg(vd, "unhandled source transfer %d", transfer);
1699 src_luminance_peak = DEFAULT_BRIGHTNESS;
1700 break;
1703 if (transfer != sys->display.colorspace->transfer)
1705 /* we need to go in linear mode */
1706 switch (transfer)
1708 case TRANSFER_FUNC_SMPTE_ST2084:
1709 /* ST2084 to Linear */
1710 psz_src_transform =
1711 ST2084_PQ_CONSTANTS
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);\
1715 return rgb";
1716 src_transfer = TRANSFER_FUNC_LINEAR;
1717 break;
1718 case TRANSFER_FUNC_HLG:
1719 /* HLG to Linear */
1720 psz_src_transform =
1721 "rgb.r = inverse_HLG(rgb.r);\
1722 rgb.g = inverse_HLG(rgb.g);\
1723 rgb.b = inverse_HLG(rgb.b);\
1724 return rgb / 20.0";
1725 src_transfer = TRANSFER_FUNC_LINEAR;
1726 break;
1727 case TRANSFER_FUNC_BT709:
1728 psz_src_transform = "return pow(rgb, 1.0 / 0.45)";
1729 src_transfer = TRANSFER_FUNC_LINEAR;
1730 break;
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;
1735 break;
1736 case TRANSFER_FUNC_BT470_BG:
1737 psz_src_transform = "return pow(rgb, 2.8)";
1738 src_transfer = TRANSFER_FUNC_LINEAR;
1739 break;
1740 default:
1741 msg_Dbg(vd, "unhandled source transfer %d", transfer);
1742 src_transfer = transfer;
1743 break;
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 */
1757 psz_tone_mapping =
1758 "static const float3 HABLE_DIV = hable(11.2);\
1759 rgb = hable(rgb) / HABLE_DIV;\
1760 return rgb";
1763 else
1764 msg_Warn(vd, "don't know how to transfer from %d to sRGB", src_transfer);
1765 break;
1767 case TRANSFER_FUNC_SMPTE_ST2084:
1768 if (src_transfer == TRANSFER_FUNC_LINEAR)
1770 /* Linear to ST2084 */
1771 psz_display_transform =
1772 ST2084_PQ_CONSTANTS
1773 "rgb = pow(rgb, ST2084_m1);\
1774 rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
1775 rgb = pow(rgb, ST2084_m2);\
1776 return rgb";
1778 else
1779 msg_Warn(vd, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer);
1780 break;
1781 default:
1782 msg_Warn(vd, "don't know how to transfer from %d to %d", src_transfer, sys->display.colorspace->transfer);
1783 break;
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 */
1800 if (src_full_range)
1801 range_adjust--;
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)
1813 case 8:
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;
1818 break;
1819 case 10:
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;
1824 break;
1825 case 12:
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;
1830 break;
1831 default:
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;
1836 break;
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);
1852 else
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));
1870 if (!shader)
1872 msg_Err(vd, "no room for the Pixel Shader");
1873 free(psz_luminance);
1874 free(psz_range);
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);
1879 #ifndef NDEBUG
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);
1887 #endif
1888 free(psz_luminance);
1889 free(psz_range);
1891 ID3DBlob *pPSBlob = CompileShader(vd, shader, true);
1892 free(shader);
1893 if (!pPSBlob)
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);
1901 return hr;
1904 #if (!VLC_WINSTORE_APP) && defined (HAVE_ID3D11VIDEODECODER)
1906 static HKEY GetAdapterRegistry(DXGI_ADAPTER_DESC *adapterDesc)
1908 HKEY hKey;
1909 TCHAR key[128];
1910 TCHAR szData[256], lookup[256];
1911 DWORD len = 256;
1913 _sntprintf(lookup, 256, TEXT("pci\\ven_%04x&dev_%04x&rev_cf"), adapterDesc->VendorId, adapterDesc->DeviceId);
1914 for (int i=0;;i++)
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 )
1918 return NULL;
1920 len = sizeof(szData);
1921 if( RegQueryValueEx( hKey, TEXT("MatchingDeviceId"), NULL, NULL, (LPBYTE) &szData, &len ) == ERROR_SUCCESS ) {
1922 if (_tcscmp(lookup, szData) == 0)
1923 return hKey;
1926 RegCloseKey(hKey);
1928 return NULL;
1930 #endif
1932 static bool CanUseTextureArray(vout_display_t *vd)
1934 #ifndef HAVE_ID3D11VIDEODECODER
1935 (void) vd;
1936 return false;
1937 #else
1938 vout_display_sys_t *sys = vd->sys;
1939 TCHAR szData[256];
1940 DWORD len = 256;
1941 IDXGIAdapter *pAdapter = D3D11DeviceAdapter(sys->d3d_dev.d3ddevice);
1942 if (!pAdapter)
1943 return false;
1945 DXGI_ADAPTER_DESC adapterDesc;
1946 HRESULT hr = IDXGIAdapter_GetDesc(pAdapter, &adapterDesc);
1947 IDXGIAdapter_Release(pAdapter);
1948 if (FAILED(hr))
1949 return false;
1951 /* ATI/AMD hardware has issues with pixel shaders with Texture2DArray */
1952 if (adapterDesc.VendorId != 0x1002)
1953 return true;
1955 #if !VLC_WINSTORE_APP
1956 HKEY hKey = GetAdapterRegistry(&adapterDesc);
1957 if (hKey == NULL)
1958 return false;
1960 LONG err = RegQueryValueEx( hKey, TEXT("DriverVersion"), NULL, NULL, (LPBYTE) &szData, &len );
1961 RegCloseKey(hKey);
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)));
1971 #endif
1972 msg_Dbg(vd, "fallback to legacy shader mode for old AMD drivers");
1973 return false;
1974 #endif
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;
1982 HRESULT hr;
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 );
1987 #endif
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);
2013 if (FAILED(hr)) {
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);
2038 if (FAILED(hr))
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);
2047 #endif
2048 if (FAILED(hr))
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;
2060 else
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);
2074 AFTER_UPDATE_RECTS;
2076 hr = UpdateBackBuffer(vd);
2077 if (FAILED(hr)) {
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);
2085 if (FAILED(hr))
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);
2095 if (!pVSBlob)
2096 return VLC_EGENERIC;
2098 hr = ID3D11Device_CreateVertexShader(sys->d3d_dev.d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2099 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->flatVSShader);
2101 if(FAILED(hr)) {
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);
2118 if(FAILED(hr)) {
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);
2126 if (!pVSBlob)
2127 return VLC_EGENERIC;
2129 hr = ID3D11Device_CreateVertexShader(sys->d3d_dev.d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2130 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->projectionVSShader);
2132 if(FAILED(hr)) {
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);
2156 if (FAILED(hr)) {
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");
2164 return VLC_SUCCESS;
2167 static void Direct3D11DestroyPool(vout_display_t *vd)
2169 vout_display_sys_t *sys = vd->sys;
2171 if (sys->sys.pool)
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:
2182 * 3--2
2183 * | |
2184 * 0--1
2185 * For a 180 degrees rotation it should like this:
2186 * 1--0
2187 * | |
2188 * 2--3
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;
2200 break;
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;
2206 break;
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;
2212 break;
2213 case ORIENT_TRANSPOSED:
2214 vertex_order[0] = 2;
2215 vertex_order[1] = 1;
2216 vertex_order[2] = 0;
2217 vertex_order[3] = 3;
2218 break;
2219 case ORIENT_HFLIPPED:
2220 vertex_order[0] = 1;
2221 vertex_order[1] = 0;
2222 vertex_order[2] = 3;
2223 vertex_order[3] = 2;
2224 break;
2225 case ORIENT_VFLIPPED:
2226 vertex_order[0] = 3;
2227 vertex_order[1] = 2;
2228 vertex_order[2] = 1;
2229 vertex_order[3] = 0;
2230 break;
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;
2236 break;
2237 default:
2238 vertex_order[0] = 0;
2239 vertex_order[1] = 1;
2240 vertex_order[2] = 2;
2241 vertex_order[3] = 3;
2242 break;
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)
2263 src_width += 1;
2264 if (src_height != dst_height)
2265 src_height += 1;
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] = {
2274 { left, bottom },
2275 { right, bottom },
2276 { right, top },
2277 { left, top },
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];
2289 // bottom left
2290 dst_data[0].position.z = 0.0f;
2291 dst_data[0].texture.x = 0.0f;
2292 dst_data[0].texture.y = 1.0f;
2294 // bottom right
2295 dst_data[1].position.z = 0.0f;
2296 dst_data[1].texture.x = 1.0f;
2297 dst_data[1].texture.y = 1.0f;
2299 // top right
2300 dst_data[2].position.z = 0.0f;
2301 dst_data[2].texture.x = 1.0f;
2302 dst_data[2].texture.y = 0.0f;
2304 // top left
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;
2321 else
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;
2352 float y = cosTheta;
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)
2386 HRESULT hr;
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;
2399 else
2401 msg_Warn(vd, "Projection mode %d not handled", projection);
2402 return false;
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);
2413 if(FAILED(hr)) {
2414 msg_Err(vd, "Failed to create vertex buffer. (hr=%lX)", hr);
2415 return false;
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);
2427 if(FAILED(hr)) {
2428 msg_Err(vd, "Could not create the quad indices. (hr=0x%lX)", hr);
2429 return false;
2432 return true;
2435 static bool UpdateQuadPosition( vout_display_t *vd, d3d_quad_t *quad,
2436 const RECT *output,
2437 video_projection_mode_t projection,
2438 video_orientation_t orientation )
2440 vout_display_sys_t *sys = vd->sys;
2441 HRESULT hr;
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);
2446 if (FAILED(hr)) {
2447 msg_Err(vd, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
2448 return false;
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);
2454 if (FAILED(hr)) {
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);
2457 return false;
2459 WORD *triangle_pos = mappedResource.pData;
2461 if ( projection == PROJECTION_MODE_RECTANGULAR )
2462 SetupQuadFlat(dst_data, output, quad, triangle_pos, orientation);
2463 else
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);
2469 return true;
2472 static int SetupQuad(vout_display_t *vd, const video_format_t *fmt, d3d_quad_t *quad,
2473 const RECT *output,
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;
2478 HRESULT hr;
2479 const bool RGB_shader = IsRGBShader(cfg);
2481 /* pixel shader constant buffer */
2482 PS_CONSTANT_BUFFER defaultConstants = {
2483 .Opacity = 1,
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]);
2494 if(FAILED(hr)) {
2495 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2496 goto error;
2499 FLOAT itu_black_level = 0.f;
2500 FLOAT itu_achromacy = 0.f;
2501 if (!RGB_shader)
2503 switch (cfg->bitsPerChannel)
2505 case 8:
2506 /* Rec. ITU-R BT.709-6 §4.6 */
2507 itu_black_level = 16.f / 255.f;
2508 itu_achromacy = 128.f / 255.f;
2509 break;
2510 case 10:
2511 /* Rec. ITU-R BT.709-6 §4.6 */
2512 itu_black_level = 64.f / 1023.f;
2513 itu_achromacy = 512.f / 1023.f;
2514 break;
2515 case 12:
2516 /* Rec. ITU-R BT.2020-2 Table 5 */
2517 itu_black_level = 256.f / 4095.f;
2518 itu_achromacy = 2048.f / 4095.f;
2519 break;
2520 default:
2521 /* unknown bitdepth, use approximation for infinite bit depth */
2522 itu_black_level = 16.f / 256.f;
2523 itu_achromacy = 128.f / 256.f;
2524 break;
2528 static const FLOAT IDENTITY_4X4[4 * 4] = {
2529 1.f, 0.f, 0.f, 0.f,
2530 0.f, 1.f, 0.f, 0.f,
2531 0.f, 0.f, 1.f, 0.f,
2532 0.f, 0.f, 0.f, 1.f,
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,
2541 0.f, 0.f, 0.f, 1.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,
2548 0.f, 0.f, 0.f, 1.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,
2555 0.f, 0.f, 0.f, 1.f,
2558 PS_COLOR_TRANSFORM colorspace;
2560 memcpy(colorspace.WhitePoint, IDENTITY_4X4, sizeof(colorspace.WhitePoint));
2562 const FLOAT *ppColorspace;
2563 if (RGB_shader)
2564 ppColorspace = IDENTITY_4X4;
2565 else {
2566 switch (fmt->space){
2567 case COLOR_SPACE_BT709:
2568 ppColorspace = COLORSPACE_BT709_TO_FULL;
2569 break;
2570 case COLOR_SPACE_BT2020:
2571 ppColorspace = COLORSPACE_BT2020_TO_FULL;
2572 break;
2573 case COLOR_SPACE_BT601:
2574 ppColorspace = COLORSPACE_BT601_TO_FULL;
2575 break;
2576 default:
2577 case COLOR_SPACE_UNDEF:
2578 if( fmt->i_height > 576 )
2579 ppColorspace = COLORSPACE_BT709_TO_FULL;
2580 else
2581 ppColorspace = COLORSPACE_BT601_TO_FULL;
2582 break;
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]);
2597 if(FAILED(hr)) {
2598 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2599 goto error;
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);
2609 if(FAILED(hr)) {
2610 msg_Err(vd, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
2611 goto error;
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))
2622 goto error;
2623 if (!UpdateQuadPosition(vd, quad, output, projection, orientation))
2624 goto error;
2626 quad->d3dpixelShader = d3dpixelShader;
2627 if (projection == PROJECTION_MODE_RECTANGULAR)
2628 quad->d3dvertexShader = sys->flatVSShader;
2629 else
2630 quad->d3dvertexShader = sys->projectionVSShader;
2632 return VLC_SUCCESS;
2634 error:
2635 ReleaseQuad(quad);
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;
2718 #endif
2720 msg_Dbg(vd, "Direct3D11 resources destroyed");
2723 static void Direct3D11DeleteRegions(int count, picture_t **region)
2725 for (int i = 0; i < count; ++i) {
2726 if (region[i]) {
2727 picture_Release(region[i]);
2730 free(region);
2733 static void DestroyPictureQuad(picture_t *p_picture)
2735 ReleaseQuad( (d3d_quad_t *) p_picture->p_sys );
2736 free( p_picture );
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);
2750 else {
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;
2761 HRESULT hr;
2762 int err;
2764 if (sys->d3dregion_format == NULL)
2765 return VLC_EGENERIC;
2767 int count = 0;
2768 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
2769 count++;
2771 *region = calloc(count, sizeof(picture_t *));
2772 if (unlikely(*region==NULL))
2773 return VLC_ENOMEM;
2774 *subpicture_region_count = count;
2776 int i = 0;
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
2790 break;
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)) {
2800 continue;
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++)
2806 if (textures[j])
2807 ID3D11Texture2D_Release(textures[j]);
2808 free(d3dquad);
2809 continue;
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);
2820 free(d3dquad);
2821 continue;
2823 d3dquad->i_width = r->fmt.i_width;
2824 d3dquad->i_height = r->fmt.i_height;
2825 RECT output;
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);
2837 free(d3dquad);
2838 continue;
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);
2849 continue;
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);
2860 continue;
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);
2866 } else {
2867 msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
2868 picture_Release(quad_picture);
2869 continue;
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 );
2883 return VLC_SUCCESS;