direct3d11: simplify the math to compute the quad corners position
[vlc.git] / modules / video_output / win32 / direct3d11.c
blob137205eecac181f3aa55541d632bcab3b253becd
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 float f_luminance_scale;
112 } d3d_quad_t;
114 typedef enum video_color_axis {
115 COLOR_AXIS_RGB,
116 COLOR_AXIS_YCBCR,
117 } video_color_axis;
119 typedef struct {
120 DXGI_COLOR_SPACE_TYPE dxgi;
121 const char *name;
122 video_color_axis axis;
123 video_color_primaries_t primaries;
124 video_transfer_func_t transfer;
125 video_color_space_t color;
126 bool b_full_range;
127 } dxgi_color_space;
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;
136 } display;
138 #if !VLC_WINSTORE_APP
139 HINSTANCE hdxgi_dll; /* handle of the opened dxgi dll */
140 d3d11_handle_t hd3d;
141 HINSTANCE hd3dcompiler_dll; /* handle of the opened d3dcompiler dll */
142 pD3DCompile OurD3DCompile;
143 #endif
144 #if defined(HAVE_ID3D11VIDEODECODER)
145 HANDLE context_lock; /* D3D11 Context lock necessary
146 for hw decoding */
147 #endif
148 IDXGISwapChain1 *dxgiswapChain; /* DXGI 1.2 swap chain */
149 IDXGISwapChain4 *dxgiswapChain4; /* DXGI 1.5 for HDR */
150 d3d11_device_t d3d_dev;
151 d3d_quad_t picQuad;
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 */
165 bool legacy_shader;
167 // SPU
168 vlc_fourcc_t pSubpictureChromas[2];
169 ID3D11PixelShader *pSPUPixelShader;
170 const d3d_format_t *d3dregion_format;
171 int d3dregion_count;
172 picture_t **d3dregions;
175 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
176 typedef struct d3d_vertex_t {
177 struct {
178 FLOAT x;
179 FLOAT y;
180 FLOAT z;
181 } position;
182 struct {
183 FLOAT x;
184 FLOAT y;
185 } texture;
186 } d3d_vertex_t;
188 typedef struct {
189 FLOAT Opacity;
190 FLOAT BoundaryX;
191 FLOAT BoundaryY;
192 FLOAT LuminanceScale;
193 } PS_CONSTANT_BUFFER;
195 typedef struct {
196 FLOAT WhitePoint[4*4];
197 FLOAT Colorspace[4*4];
198 } PS_COLOR_TRANSFORM;
200 typedef struct {
201 FLOAT RotX[4*4];
202 FLOAT RotY[4*4];
203 FLOAT RotZ[4*4];
204 FLOAT View[4*4];
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,
239 const RECT *output,
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 = "\
250 struct VS_INPUT\
252 float4 Position : POSITION;\
253 float4 Texture : TEXCOORD0;\
256 struct VS_OUTPUT\
258 float4 Position : SV_POSITION;\
259 float4 Texture : TEXCOORD0;\
262 VS_OUTPUT main( VS_INPUT In )\
264 return In;\
268 #define STRINGIZE2(s) #s
269 #define STRINGIZE(s) STRINGIZE2(s)
271 static const char* globVertexShaderProjection = "\
272 cbuffer VS_PROJECTION_CONST : register(b0)\
274 float4x4 RotX;\
275 float4x4 RotY;\
276 float4x4 RotZ;\
277 float4x4 View;\
278 float4x4 Projection;\
280 struct VS_INPUT\
282 float4 Position : POSITION;\
283 float4 Texture : TEXCOORD0;\
286 struct VS_OUTPUT\
288 float4 Position : SV_POSITION;\
289 float4 Texture : TEXCOORD0;\
292 VS_OUTPUT main( VS_INPUT In )\
294 VS_OUTPUT Output;\
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;\
303 return Output;\
307 static const char* globPixelShaderDefault = "\
308 cbuffer PS_CONSTANT_BUFFER : register(b0)\
310 float Opacity;\
311 float BoundaryX;\
312 float BoundaryY;\
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];\
323 struct PS_INPUT\
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) {\
336 x.r = hable(x.r);\
337 x.g = hable(x.g);\
338 x.b = hable(x.b);\
339 return 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² */\
348 if (x <= 0.5)\
349 x = x * x * B67_inv_r2;\
350 else\
351 x = exp((x - B67_c) / B67_a) + B67_b;\
352 return x;\
355 inline float3 sourceToLinear(float3 rgb) {\
356 %s;\
359 inline float3 linearToDisplay(float3 rgb) {\
360 %s;\
363 inline float3 toneMapping(float3 rgb) {\
364 %s;\
367 inline float3 adjustPeakLuminance(float3 rgb) {\
368 %s;\
371 inline float3 adjustRange(float3 rgb) {\
372 %s;\
375 inline float4 sampleTexture(SamplerState samplerState, float4 coords) {\
376 float4 sample;\
377 %s /* sampling routine in sample */\
378 return sample;\
381 float4 main( PS_INPUT In ) : SV_TARGET\
383 float4 sample;\
385 if (In.Texture.x > BoundaryX || In.Texture.y > BoundaryY) \
386 sample = sampleTexture( SamplerStates[1], In.Texture );\
387 else\
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;
411 HRESULT hr;
412 hr = ID3D11DeviceContext_Map(p_sys->context, p_sys->resource[KNOWN_DXGI_INDEX], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
413 if( FAILED(hr) )
415 return VLC_EGENERIC;
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));
430 if (!sys)
431 return VLC_ENOMEM;
433 if (D3D11_Create(vd, &sys->hd3d) != VLC_SUCCESS)
434 return VLC_EGENERIC;
436 sys->hd3dcompiler_dll = Direct3D11LoadShaderLibrary();
437 if (!sys->hd3dcompiler_dll) {
438 msg_Err(vd, "cannot load d3dcompiler.dll, aborting");
439 Direct3D11Destroy(vd);
440 return VLC_EGENERIC;
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);
447 return VLC_EGENERIC;
449 return VLC_SUCCESS;
451 #else
452 static int OpenCoreW(vout_display_t *vd)
454 IDXGISwapChain1* dxgiswapChain = var_InheritInteger(vd, "winrt-swapchain");
455 if (!dxgiswapChain)
456 return VLC_EGENERIC;
457 ID3D11DeviceContext* d3dcontext = var_InheritInteger(vd, "winrt-d3dcontext");
458 if (!d3dcontext)
459 return VLC_EGENERIC;
460 ID3D11Device* d3ddevice = NULL;
461 ID3D11DeviceContext_GetDevice(d3dcontext, &d3ddevice);
462 if (!d3ddevice)
463 return VLC_EGENERIC;
465 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
466 if (!sys)
467 return VLC_ENOMEM;
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);
476 return VLC_SUCCESS;
478 #endif
480 #if VLC_WINSTORE_APP
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;
484 out->left = 0;
485 out->top = 0;
486 uint32_t i_width;
487 uint32_t i_height;
488 UINT dataSize = sizeof(i_width);
489 HRESULT hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
490 if (FAILED(hr)) {
491 return false;
493 dataSize = sizeof(i_height);
494 hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
495 if (FAILED(hr)) {
496 return false;
498 out->right = i_width;
499 out->bottom = i_height;
500 return true;
502 #endif
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);
510 #else
511 int ret = OpenCoreW(vd);
512 #endif
514 if (ret != VLC_SUCCESS)
515 return ret;
517 if (CommonInit(vd))
518 goto error;
520 #if VLC_WINSTORE_APP
521 vd->sys->sys.pf_GetRect = GetRect;
522 #endif
524 if (Direct3D11Open(vd)) {
525 msg_Err(vd, "Direct3D11 could not be opened");
526 goto error;
529 #if !VLC_WINSTORE_APP
530 EventThreadUpdateTitle(vd->sys->sys.event, VOUT_TITLE " (Direct3D11 output)");
531 #endif
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;
544 else
545 vd->info.subpicture_chromas = NULL;
547 vd->pool = Pool;
548 vd->prepare = Prepare;
549 vd->display = Display;
550 vd->control = Control;
551 vd->manage = Manage;
553 msg_Dbg(vd, "Direct3D11 Open Succeeded");
555 return VLC_SUCCESS;
557 error:
558 Close(object);
559 return VLC_EGENERIC;
562 static void Close(vlc_object_t *object)
564 vout_display_t * vd = (vout_display_t *)object;
566 Direct3D11Close(vd);
567 CommonClean(vd);
568 Direct3D11Destroy(vd);
569 free(vd->sys);
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 */
575 pool_size += 2;
577 vout_display_sys_t *sys = vd->sys;
578 ID3D11Texture2D *textures[pool_size * D3D11_MAX_SHADER_VIEW];
579 picture_t **pictures = NULL;
580 picture_t *picture;
581 unsigned picture_count = 0;
583 if (sys->sys.pool)
584 return sys->sys.pool;
586 if (vd->info.is_slow)
587 pool_size = 1;
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) {
594 HRESULT hr;
595 ID3D10Multithread *pMultithread;
596 hr = ID3D11Device_QueryInterface( sys->d3d_dev.d3ddevice, &IID_ID3D10Multithread, (void **)&pMultithread);
597 if (SUCCEEDED(hr)) {
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))
606 goto error;
608 pictures = calloc(pool_size, sizeof(*pictures));
609 if (!pictures)
610 goto error;
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))
615 goto error;
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 = {
625 .p_sys = picsys,
626 .pf_destroy = DestroyDisplayPoolPicture,
629 picture = picture_NewFromResource(&surface_fmt, &resource);
630 if (unlikely(picture == NULL)) {
631 free(picsys);
632 msg_Err( vd, "Failed to create picture %d in the pool.", picture_count );
633 goto error;
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)
644 #endif
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))
650 goto error;
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.");
658 return NULL;
661 if (sys->picQuadConfig->formatTexture == DXGI_FORMAT_UNKNOWN)
662 sys->sys.pool = picture_pool_NewFromFormat( &surface_fmt, pool_size );
663 else
665 picture_pool_configuration_t pool_cfg = {
666 .picture = pictures,
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 );
676 error:
677 if (sys->sys.pool == NULL) {
678 picture_pool_configuration_t pool_cfg = {
679 .picture_count = 0,
681 if (pictures) {
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]);
685 free(pictures);
688 /* create an empty pool to avoid crashing */
689 sys->sys.pool = picture_pool_NewExtended( &pool_cfg );
690 } else {
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 );
701 free(p_sys);
702 free(picture);
705 static HRESULT UpdateBackBuffer(vout_display_t *vd)
707 vout_display_sys_t *sys = vd->sys;
708 HRESULT hr;
709 ID3D11Texture2D* pDepthStencil;
710 ID3D11Texture2D* pBackBuffer;
711 RECT rect;
712 #if VLC_WINSTORE_APP
713 if (!GetRect(&sys->sys, &rect))
714 #endif
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);
723 if (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);
745 if (FAILED(hr)) {
746 msg_Err(vd, "Failed to resize the backbuffer. (hr=0x%lX)", hr);
747 return hr;
750 hr = IDXGISwapChain_GetBuffer(sys->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *)&pBackBuffer);
751 if (FAILED(hr)) {
752 msg_Err(vd, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr);
753 return hr;
756 hr = ID3D11Device_CreateRenderTargetView(sys->d3d_dev.d3ddevice, (ID3D11Resource *)pBackBuffer, NULL, &sys->d3drenderTargetView);
757 ID3D11Texture2D_Release(pBackBuffer);
758 if (FAILED(hr)) {
759 msg_Err(vd, "Failed to create the target view. (hr=0x%lX)", hr);
760 return 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);
778 if (FAILED(hr)) {
779 msg_Err(vd, "Could not create the depth stencil texture. (hr=0x%lX)", hr);
780 return 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);
793 if (FAILED(hr)) {
794 msg_Err(vd, "Could not create the depth stencil view. (hr=0x%lX)", hr);
795 return hr;
798 return S_OK;
801 /* rotation around the Z axis */
802 static void getZRotMatrix(float theta, FLOAT matrix[static 16])
804 float st, ct;
806 sincosf(theta, &st, &ct);
808 const FLOAT m[] = {
809 /* x y z w */
810 ct, -st, 0.f, 0.f,
811 st, ct, 0.f, 0.f,
812 0.f, 0.f, 1.f, 0.f,
813 0.f, 0.f, 0.f, 1.f
816 memcpy(matrix, m, sizeof(m));
819 /* rotation around the Y axis */
820 static void getYRotMatrix(float theta, FLOAT matrix[static 16])
822 float st, ct;
824 sincosf(theta, &st, &ct);
826 const FLOAT m[] = {
827 /* x y z w */
828 ct, 0.f, -st, 0.f,
829 0.f, 1.f, 0.f, 0.f,
830 st, 0.f, ct, 0.f,
831 0.f, 0.f, 0.f, 1.f
834 memcpy(matrix, m, sizeof(m));
837 /* rotation around the X axis */
838 static void getXRotMatrix(float phi, FLOAT matrix[static 16])
840 float sp, cp;
842 sincosf(phi, &sp, &cp);
844 const FLOAT m[] = {
845 /* x y z w */
846 1.f, 0.f, 0.f, 0.f,
847 0.f, cp, sp, 0.f,
848 0.f, -sp, cp, 0.f,
849 0.f, 0.f, 0.f, 1.f
852 memcpy(matrix, m, sizeof(m));
855 static void getZoomMatrix(float zoom, FLOAT matrix[static 16]) {
857 const FLOAT m[] = {
858 /* x y z w */
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]) {
871 float zFar = 1000;
872 float zNear = 0.01;
874 float f = 1.f / tanf(fovy / 2.f);
876 const FLOAT m[] = {
877 f / sar, 0.f, 0.f, 0.f,
878 0.f, 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;
903 float f_z;
904 if (f_fovx <= z_thresh * M_PI / 180)
905 f_z = 0;
906 else
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;
910 if (f_z < z_min)
911 f_z = z_min;
913 return f_z;
916 static void SetQuadVSProjection(vout_display_t *vd, d3d_quad_t *quad, const vlc_viewpoint_t *p_vp)
918 if (!quad->pVertexShaderConstants)
919 return;
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 ||
924 f_fovx < -0.001f )
925 return;
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;
935 HRESULT hr;
936 D3D11_MAPPED_SUBRESOURCE mapped;
937 hr = ID3D11DeviceContext_Map(sys->d3d_dev.d3dcontext, (ID3D11Resource *)quad->pVertexShaderConstants, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
938 if (SUCCEEDED(hr)) {
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);
947 #undef RAD
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 );
965 #endif
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 );
975 #endif
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 );
1002 AFTER_UPDATE_RECTS;
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 );
1010 res = VLC_SUCCESS;
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) )
1018 UpdateSize(vd);
1021 return res;
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;
1032 CommonManage(vd);
1033 AFTER_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))
1039 UpdateSize(vd);
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)
1049 return;
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);
1058 else {
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);
1066 UINT offset = 0;
1068 /* Render the quad */
1069 /* vertex shader */
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);
1077 /* pixel shader */
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;
1096 int i;
1097 HRESULT hr;
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)) )
1106 while (i-- > 0)
1107 ID3D11DeviceContext_Unmap(sys->d3d_dev.d3dcontext, sys->stagingSys.resource[i], 0);
1108 b_mapped = false;
1109 break;
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;
1120 if (b_mapped)
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);
1129 else
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 );
1136 #endif
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);
1143 D3D11_BOX box = {
1144 .top = 0,
1145 .bottom = __MIN(srcDesc.Height, texDesc.Height),
1146 .left = 0,
1147 .right = __MIN(srcDesc.Width, texDesc.Width),
1148 .back = 1,
1150 ID3D11DeviceContext_CopySubresourceRegion(sys->d3d_dev.d3dcontext,
1151 sys->stagingSys.resource[KNOWN_DXGI_INDEX],
1152 0, 0, 0, 0,
1153 p_sys->resource[KNOWN_DXGI_INDEX],
1154 p_sys->slice_index, &box);
1156 else
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);
1176 AFTER_UPDATE_RECTS;
1177 UpdateSize(vd);
1182 if (subpicture) {
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);
1223 else {
1224 picture_sys_t *p_sys = ActivePictureSys(picture);
1225 DisplayD3DPicture(sys, &sys->picQuad, p_sys->resourceView);
1228 if (subpicture) {
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 );
1244 #endif
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);
1263 if (subpicture)
1264 subpicture_Delete(subpicture);
1266 CommonDisplay(vd);
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 );
1281 #endif
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) {
1290 TCHAR filename[19];
1291 _sntprintf(filename, 19, TEXT("D3DCOMPILER_%d.dll"), i);
1292 instance = LoadLibrary(filename);
1293 if (instance) break;
1295 return instance;
1297 #endif
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},
1333 #undef DXGIMAP
1336 static void D3D11SetColorSpace(vout_display_t *vd)
1338 vout_display_sys_t *sys = vd->sys;
1339 HRESULT hr;
1340 int best = -1;
1341 int score, best_score = 0;
1342 UINT support;
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);
1348 if (FAILED(hr)) {
1349 msg_Warn(vd, "could not get a IDXGISwapChain3");
1350 goto done;
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);
1365 score = 0;
1366 if (color_spaces[i].primaries == vd->source.primaries)
1367 score++;
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))
1373 score++;
1374 if (color_spaces[i].b_full_range == src_full_range)
1375 score++;
1376 if (score > best_score || (score && best == -1)) {
1377 best = i;
1378 best_score = score;
1383 if (best == -1)
1385 best = 0;
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)
1403 best = i;
1404 csp = &color_spaces[i];
1405 break;
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 );
1416 #endif
1418 hr = IDXGISwapChain3_SetColorSpace1(dxgiswapChain3, color_spaces[best].dxgi);
1419 if (SUCCEEDED(hr))
1421 sys->display.colorspace = &color_spaces[best];
1422 msg_Dbg(vd, "using colorspace %s", sys->display.colorspace->name);
1424 else
1425 msg_Err(vd, "Failed to set colorspace %s. (hr=0x%lX)", sys->display.colorspace->name, hr);
1426 done:
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;
1433 break;
1434 case TRANSFER_FUNC_SMPTE_ST2084:
1435 sys->display.luminance_peak = MAX_PQ_BRIGHTNESS;
1436 break;
1437 /* there is no other output transfer on Windows */
1438 default:
1439 vlc_assert_unreachable();
1442 if (dxgiswapChain3)
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;
1463 if (from_processor)
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
1480 HRESULT hr = S_OK;
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;
1494 break;
1495 default:
1496 scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; /* TODO: use DXGI_FORMAT_NV12 */
1497 break;
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),
1504 &sys->d3d_dev);
1505 if (FAILED(hr)) {
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);
1518 if (FAILED(hr)) {
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);
1526 if (FAILED(hr)) {
1527 msg_Err(vd, "Could not create the SwapChain. (hr=0x%lX)", hr);
1528 return VLC_EGENERIC;
1530 #endif
1532 IDXGISwapChain_QueryInterface( sys->dxgiswapChain, &IID_IDXGISwapChain4, (void **)&sys->dxgiswapChain4);
1534 D3D11SetColorSpace(vd);
1536 video_format_t fmt;
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
1543 && vd->obj.force
1544 #endif
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)
1553 continue;
1554 err = SetupOutputFormat(vd, &fmt);
1555 if (err == VLC_SUCCESS)
1556 break;
1559 if (err != VLC_SUCCESS)
1560 return err;
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);
1570 vd->fmt = fmt;
1572 return VLC_SUCCESS;
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);
1586 else
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;
1597 break;
1598 case VLC_CODEC_D3D11_OPAQUE_10B:
1599 bits_per_channel = 10;
1600 break;
1601 default:
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;
1606 break;
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;
1641 vd->fmt = *fmt;
1643 return VLC_SUCCESS;
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);
1681 #ifndef NDEBUG
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 );
1683 #endif
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);
1698 if (FAILED(hr)) {
1699 char *err = pErrBlob ? ID3D10Blob_GetBufferPointer(pErrBlob) : NULL;
1700 msg_Err(vd, "invalid %s Shader (hr=0x%lX): %s", pixel?"Pixel":"Vertex", hr, err );
1701 if (pErrBlob)
1702 ID3D10Blob_Release(pErrBlob);
1703 return NULL;
1705 return pShaderBlob;
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:
1734 psz_sampler =
1735 "sample.x = shaderTexture[0].Sample(samplerState, coords).x;\
1736 sample.yz = shaderTexture[1].Sample(samplerState, coords).xy;\
1737 sample.a = 1;";
1738 break;
1739 case DXGI_FORMAT_YUY2:
1740 psz_sampler =
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;\
1744 sample.a = 1;";
1745 break;
1746 case DXGI_FORMAT_R8G8B8A8_UNORM:
1747 case DXGI_FORMAT_B8G8R8A8_UNORM:
1748 case DXGI_FORMAT_B8G8R8X8_UNORM:
1749 case DXGI_FORMAT_B5G6R5_UNORM:
1750 psz_sampler =
1751 "sample = shaderTexture[0].Sample(samplerState, coords);";
1752 break;
1753 case DXGI_FORMAT_UNKNOWN:
1754 psz_sampler =
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;\
1758 sample.a = 1;";
1759 break;
1760 default:
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 */
1769 switch (transfer)
1771 case TRANSFER_FUNC_SMPTE_ST2084:
1772 /* ST2084 to Linear */
1773 psz_src_transform =
1774 ST2084_PQ_CONSTANTS
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);\
1778 return rgb";
1779 src_transfer = TRANSFER_FUNC_LINEAR;
1780 break;
1781 case TRANSFER_FUNC_HLG:
1782 /* HLG to Linear */
1783 psz_src_transform =
1784 "rgb.r = inverse_HLG(rgb.r);\
1785 rgb.g = inverse_HLG(rgb.g);\
1786 rgb.b = inverse_HLG(rgb.b);\
1787 return rgb / 20.0";
1788 src_transfer = TRANSFER_FUNC_LINEAR;
1789 break;
1790 case TRANSFER_FUNC_BT709:
1791 psz_src_transform = "return pow(rgb, 1.0 / 0.45)";
1792 src_transfer = TRANSFER_FUNC_LINEAR;
1793 break;
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;
1798 break;
1799 case TRANSFER_FUNC_BT470_BG:
1800 psz_src_transform = "return pow(rgb, 2.8)";
1801 src_transfer = TRANSFER_FUNC_LINEAR;
1802 break;
1803 default:
1804 msg_Dbg(vd, "unhandled source transfer %d", transfer);
1805 src_transfer = transfer;
1806 break;
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 */
1820 psz_tone_mapping =
1821 "static const float3 HABLE_DIV = hable(11.2);\
1822 rgb = hable(rgb) / HABLE_DIV;\
1823 return rgb";
1826 else
1827 msg_Warn(vd, "don't know how to transfer from %d to sRGB", src_transfer);
1828 break;
1830 case TRANSFER_FUNC_SMPTE_ST2084:
1831 if (src_transfer == TRANSFER_FUNC_LINEAR)
1833 /* Linear to ST2084 */
1834 psz_display_transform =
1835 ST2084_PQ_CONSTANTS
1836 "rgb = pow(rgb, ST2084_m1);\
1837 rgb = (ST2084_c1 + ST2084_c2 * rgb) / (1 + ST2084_c3 * rgb);\
1838 rgb = pow(rgb, ST2084_m2);\
1839 return rgb";
1841 else
1842 msg_Warn(vd, "don't know how to transfer from %d to SMPTE ST 2084", src_transfer);
1843 break;
1844 default:
1845 msg_Warn(vd, "don't know how to transfer from %d to %d", src_transfer, sys->display.colorspace->transfer);
1846 break;
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 */
1859 } else {
1860 if (src_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)
1876 case 8:
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;
1881 break;
1882 case 10:
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;
1887 break;
1888 case 12:
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;
1893 break;
1894 default:
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;
1899 break;
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);
1915 else
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));
1933 if (!shader)
1935 msg_Err(vd, "no room for the Pixel Shader");
1936 free(psz_range);
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);
1941 #ifndef NDEBUG
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);
1950 #endif
1951 free(psz_range);
1953 ID3DBlob *pPSBlob = CompileShader(vd, shader, true);
1954 free(shader);
1955 if (!pPSBlob)
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);
1963 return hr;
1966 static bool CanUseTextureArray(vout_display_t *vd)
1968 #ifndef HAVE_ID3D11VIDEODECODER
1969 (void) vd;
1970 return false;
1971 #else
1972 struct wddm_version WDDM = {
1973 .wddm = 22,
1974 .d3d_features = 19,
1975 .revision = 162,
1976 .build = 0,
1978 if (D3D11CheckDriverVersion(&vd->sys->d3d_dev, GPU_MANUFACTURER_AMD, &WDDM) == VLC_SUCCESS)
1979 return true;
1981 msg_Dbg(vd, "fallback to legacy shader mode for old AMD drivers");
1982 return false;
1983 #endif
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;
1991 HRESULT hr;
1993 sys->legacy_shader = !CanUseTextureArray(vd);
1995 hr = CompilePixelShader(vd, sys->picQuadConfig, fmt->transfer, fmt->b_color_range_full, &sys->picQuadPixelShader);
1996 if (FAILED(hr))
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);
2005 #endif
2006 if (FAILED(hr))
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;
2020 else
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);
2030 AFTER_UPDATE_RECTS;
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];
2057 #endif
2059 vd->info.is_slow = !is_d3d11_opaque(fmt->i_chroma) && sys->picQuadConfig->formatTexture != DXGI_FORMAT_UNKNOWN;
2060 return VLC_SUCCESS;
2063 static int Direct3D11CreateGenericResources(vout_display_t *vd)
2065 vout_display_sys_t *sys = vd->sys;
2066 HRESULT hr;
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 );
2071 #endif
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);
2097 if (FAILED(hr)) {
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);
2119 if (FAILED(hr)) {
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);
2127 if (FAILED(hr))
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);
2137 if (!pVSBlob)
2138 return VLC_EGENERIC;
2140 hr = ID3D11Device_CreateVertexShader(sys->d3d_dev.d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2141 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->flatVSShader);
2143 if(FAILED(hr)) {
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);
2160 if(FAILED(hr)) {
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);
2168 if (!pVSBlob)
2169 return VLC_EGENERIC;
2171 hr = ID3D11Device_CreateVertexShader(sys->d3d_dev.d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
2172 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &sys->projectionVSShader);
2174 if(FAILED(hr)) {
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]);
2197 if (FAILED(hr)) {
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]);
2204 if (FAILED(hr)) {
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");
2215 return VLC_SUCCESS;
2218 static void Direct3D11DestroyPool(vout_display_t *vd)
2220 vout_display_sys_t *sys = vd->sys;
2222 if (sys->sys.pool)
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:
2233 * 3--2
2234 * | |
2235 * 0--1
2236 * For a 180 degrees rotation it should like this:
2237 * 1--0
2238 * | |
2239 * 2--3
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;
2251 break;
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;
2257 break;
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;
2263 break;
2264 case ORIENT_TRANSPOSED:
2265 vertex_order[0] = 2;
2266 vertex_order[1] = 1;
2267 vertex_order[2] = 0;
2268 vertex_order[3] = 3;
2269 break;
2270 case ORIENT_HFLIPPED:
2271 vertex_order[0] = 1;
2272 vertex_order[1] = 0;
2273 vertex_order[2] = 3;
2274 vertex_order[3] = 2;
2275 break;
2276 case ORIENT_VFLIPPED:
2277 vertex_order[0] = 3;
2278 vertex_order[1] = 2;
2279 vertex_order[2] = 1;
2280 vertex_order[3] = 0;
2281 break;
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;
2287 break;
2288 default:
2289 vertex_order[0] = 0;
2290 vertex_order[1] = 1;
2291 vertex_order[2] = 2;
2292 vertex_order[3] = 3;
2293 break;
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] = {
2313 { left, bottom },
2314 { right, bottom },
2315 { right, top },
2316 { left, top },
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];
2328 // bottom left
2329 dst_data[0].position.z = 0.0f;
2330 dst_data[0].texture.x = 0.0f;
2331 dst_data[0].texture.y = 1.0f;
2333 // bottom right
2334 dst_data[1].position.z = 0.0f;
2335 dst_data[1].texture.x = 1.0f;
2336 dst_data[1].texture.y = 1.0f;
2338 // top right
2339 dst_data[2].position.z = 0.0f;
2340 dst_data[2].texture.x = 1.0f;
2341 dst_data[2].texture.y = 0.0f;
2343 // top left
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;
2360 else
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;
2391 float y = cosTheta;
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)
2425 HRESULT hr;
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;
2438 else
2440 msg_Warn(vd, "Projection mode %d not handled", projection);
2441 return false;
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);
2452 if(FAILED(hr)) {
2453 msg_Err(vd, "Failed to create vertex buffer. (hr=%lX)", hr);
2454 return false;
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);
2466 if(FAILED(hr)) {
2467 msg_Err(vd, "Could not create the quad indices. (hr=0x%lX)", hr);
2468 return false;
2471 return true;
2474 static bool UpdateQuadPosition( vout_display_t *vd, d3d_quad_t *quad,
2475 const RECT *output,
2476 video_projection_mode_t projection,
2477 video_orientation_t orientation )
2479 vout_display_sys_t *sys = vd->sys;
2480 HRESULT hr;
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);
2485 if (FAILED(hr)) {
2486 msg_Err(vd, "Failed to lock the vertex buffer (hr=0x%lX)", hr);
2487 return false;
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);
2493 if (FAILED(hr)) {
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);
2496 return false;
2498 WORD *triangle_pos = mappedResource.pData;
2500 if ( projection == PROJECTION_MODE_RECTANGULAR )
2501 SetupQuadFlat(dst_data, output, quad, triangle_pos, orientation);
2502 else
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);
2508 return true;
2511 static int SetupQuad(vout_display_t *vd, const video_format_t *fmt, d3d_quad_t *quad,
2512 const RECT *output,
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;
2517 HRESULT hr;
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;
2526 break;
2527 case TRANSFER_FUNC_HLG:
2528 src_luminance_peak = 1000;
2529 break;
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;
2535 break;
2536 default:
2537 msg_Dbg(vd, "unhandled source transfer %d", fmt->transfer);
2538 src_luminance_peak = DEFAULT_BRIGHTNESS;
2539 break;
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 */
2548 else
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 */
2552 else
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]);
2565 if(FAILED(hr)) {
2566 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2567 goto error;
2570 FLOAT itu_black_level = 0.f;
2571 FLOAT itu_achromacy = 0.f;
2572 if (!RGB_shader)
2574 switch (cfg->bitsPerChannel)
2576 case 8:
2577 /* Rec. ITU-R BT.709-6 §4.6 */
2578 itu_black_level = 16.f / 255.f;
2579 itu_achromacy = 128.f / 255.f;
2580 break;
2581 case 10:
2582 /* Rec. ITU-R BT.709-6 §4.6 */
2583 itu_black_level = 64.f / 1023.f;
2584 itu_achromacy = 512.f / 1023.f;
2585 break;
2586 case 12:
2587 /* Rec. ITU-R BT.2020-2 Table 5 */
2588 itu_black_level = 256.f / 4095.f;
2589 itu_achromacy = 2048.f / 4095.f;
2590 break;
2591 default:
2592 /* unknown bitdepth, use approximation for infinite bit depth */
2593 itu_black_level = 16.f / 256.f;
2594 itu_achromacy = 128.f / 256.f;
2595 break;
2599 static const FLOAT IDENTITY_4X4[4 * 4] = {
2600 1.f, 0.f, 0.f, 0.f,
2601 0.f, 1.f, 0.f, 0.f,
2602 0.f, 0.f, 1.f, 0.f,
2603 0.f, 0.f, 0.f, 1.f,
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,
2612 0.f, 0.f, 0.f, 1.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,
2619 0.f, 0.f, 0.f, 1.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,
2626 0.f, 0.f, 0.f, 1.f,
2629 PS_COLOR_TRANSFORM colorspace;
2631 memcpy(colorspace.WhitePoint, IDENTITY_4X4, sizeof(colorspace.WhitePoint));
2633 const FLOAT *ppColorspace;
2634 if (RGB_shader)
2635 ppColorspace = IDENTITY_4X4;
2636 else {
2637 switch (fmt->space){
2638 case COLOR_SPACE_BT709:
2639 ppColorspace = COLORSPACE_BT709_TO_FULL;
2640 break;
2641 case COLOR_SPACE_BT2020:
2642 ppColorspace = COLORSPACE_BT2020_TO_FULL;
2643 break;
2644 case COLOR_SPACE_BT601:
2645 ppColorspace = COLORSPACE_BT601_TO_FULL;
2646 break;
2647 default:
2648 case COLOR_SPACE_UNDEF:
2649 if( fmt->i_height > 576 )
2650 ppColorspace = COLORSPACE_BT709_TO_FULL;
2651 else
2652 ppColorspace = COLORSPACE_BT601_TO_FULL;
2653 break;
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]);
2668 if(FAILED(hr)) {
2669 msg_Err(vd, "Could not create the pixel shader constant buffer. (hr=0x%lX)", hr);
2670 goto error;
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);
2680 if(FAILED(hr)) {
2681 msg_Err(vd, "Could not create the vertex shader constant buffer. (hr=0x%lX)", hr);
2682 goto error;
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))
2693 goto error;
2694 if (!UpdateQuadPosition(vd, quad, output, projection, orientation))
2695 goto error;
2697 quad->d3dpixelShader = d3dpixelShader;
2698 if (projection == PROJECTION_MODE_RECTANGULAR)
2699 quad->d3dvertexShader = sys->flatVSShader;
2700 else
2701 quad->d3dvertexShader = sys->projectionVSShader;
2703 return VLC_SUCCESS;
2705 error:
2706 ReleaseQuad(quad);
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;
2789 #endif
2791 msg_Dbg(vd, "Direct3D11 resources destroyed");
2794 static void Direct3D11DeleteRegions(int count, picture_t **region)
2796 for (int i = 0; i < count; ++i) {
2797 if (region[i]) {
2798 picture_Release(region[i]);
2801 free(region);
2804 static void DestroyPictureQuad(picture_t *p_picture)
2806 ReleaseQuad( (d3d_quad_t *) p_picture->p_sys );
2807 free( p_picture );
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);
2821 else {
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;
2832 HRESULT hr;
2833 int err;
2835 if (sys->d3dregion_format == NULL)
2836 return VLC_EGENERIC;
2838 int count = 0;
2839 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
2840 count++;
2842 *region = calloc(count, sizeof(picture_t *));
2843 if (unlikely(*region==NULL))
2844 return VLC_ENOMEM;
2845 *subpicture_region_count = count;
2847 int i = 0;
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
2861 break;
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)) {
2871 continue;
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++)
2877 if (textures[j])
2878 ID3D11Texture2D_Release(textures[j]);
2879 free(d3dquad);
2880 continue;
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);
2891 free(d3dquad);
2892 continue;
2894 d3dquad->i_width = r->fmt.i_width;
2895 d3dquad->i_height = r->fmt.i_height;
2896 RECT output;
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);
2908 free(d3dquad);
2909 continue;
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);
2920 continue;
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);
2931 continue;
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);
2937 } else {
2938 msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
2939 picture_Release(quad_picture);
2940 continue;
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 );
2954 return VLC_SUCCESS;