direct3d11: keep track of the number of vertices we use
[vlc.git] / modules / video_output / win32 / direct3d11.c
blobb42b00eb886a220c7ca618359236682651033f2b
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 < 0x601
25 # undef _WIN32_WINNT
26 # define _WIN32_WINNT 0x601
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>
39 #define COBJMACROS
40 #define INITGUID
41 #include <d3d11.h>
43 /* avoided until we can pass ISwapchainPanel without c++/cx mode
44 # include <windows.ui.xaml.media.dxinterop.h> */
46 #include "common.h"
48 #include "../../video_chroma/dxgi_fmt.h"
50 #if !VLC_WINSTORE_APP
51 # if USE_DXGI
52 # define D3D11CreateDeviceAndSwapChain(args...) sys->OurD3D11CreateDeviceAndSwapChain(args)
53 # else
54 # define D3D11CreateDevice(args...) sys->OurD3D11CreateDevice(args)
55 # endif
56 # define D3DCompile(args...) sys->OurD3DCompile(args)
57 #endif
59 DEFINE_GUID(GUID_SWAPCHAIN_WIDTH, 0xf1b59347, 0x1643, 0x411a, 0xad, 0x6b, 0xc7, 0x80, 0x17, 0x7a, 0x06, 0xb6);
60 DEFINE_GUID(GUID_SWAPCHAIN_HEIGHT, 0x6ea976a0, 0x9d60, 0x4bb7, 0xa5, 0xa9, 0x7d, 0xd1, 0x18, 0x7f, 0xc9, 0xbd);
61 DEFINE_GUID(GUID_CONTEXT_MUTEX, 0x472e8835, 0x3f8e, 0x4f93, 0xa0, 0xcb, 0x25, 0x79, 0x77, 0x6c, 0xed, 0x86);
63 static int Open(vlc_object_t *);
64 static void Close(vlc_object_t *);
66 #define D3D11_HELP N_("Recommended video output for Windows 8 and later versions")
67 #define HW_BLENDING_TEXT N_("Use hardware blending support")
68 #define HW_BLENDING_LONGTEXT N_(\
69 "Try to use hardware acceleration for subtitle/OSD blending.")
71 vlc_module_begin ()
72 set_shortname("Direct3D11")
73 set_description(N_("Direct3D11 video output"))
74 set_help(D3D11_HELP)
75 set_category(CAT_VIDEO)
76 set_subcategory(SUBCAT_VIDEO_VOUT)
78 add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
80 #if VLC_WINSTORE_APP
81 add_integer("winrt-d3ddevice", 0x0, NULL, NULL, true); /* ID3D11Device* */
82 add_integer("winrt-d3dcontext", 0x0, NULL, NULL, true); /* ID3D11DeviceContext* */
83 add_integer("winrt-swapchain", 0x0, NULL, NULL, true); /* IDXGISwapChain1* */
84 #endif
86 set_capability("vout display", 240)
87 add_shortcut("direct3d11")
88 set_callbacks(Open, Close)
89 vlc_module_end ()
91 #ifdef HAVE_ID3D11VIDEODECODER
92 /* VLC_CODEC_D3D11_OPAQUE */
93 struct picture_sys_t
95 ID3D11VideoDecoderOutputView *decoder; /* may be NULL for pictures from the pool */
96 ID3D11Texture2D *texture;
97 ID3D11DeviceContext *context;
98 unsigned slice_index;
100 #endif
102 /* internal picture_t pool */
103 typedef struct
105 ID3D11Texture2D *texture;
106 vout_display_t *vd;
107 } picture_sys_pool_t;
109 /* matches the D3D11_INPUT_ELEMENT_DESC we setup */
110 typedef struct d3d_vertex_t {
111 struct {
112 FLOAT x;
113 FLOAT y;
114 FLOAT z;
115 } position;
116 struct {
117 FLOAT x;
118 FLOAT y;
119 } texture;
120 FLOAT opacity;
121 } d3d_vertex_t;
123 #define RECTWidth(r) (int)((r).right - (r).left)
124 #define RECTHeight(r) (int)((r).bottom - (r).top)
126 static picture_pool_t *Pool(vout_display_t *vd, unsigned count);
128 static void Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
129 static void Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
131 static HINSTANCE Direct3D11LoadShaderLibrary(void);
132 static void Direct3D11Destroy(vout_display_t *);
134 static int Direct3D11Open (vout_display_t *, video_format_t *);
135 static void Direct3D11Close(vout_display_t *);
137 static int Direct3D11CreateResources (vout_display_t *, video_format_t *);
138 static void Direct3D11DestroyResources(vout_display_t *);
140 static int Direct3D11CreatePool (vout_display_t *, video_format_t *);
141 static void Direct3D11DestroyPool(vout_display_t *);
143 static void DestroyDisplayPicture(picture_t *);
144 static void DestroyDisplayPoolPicture(picture_t *);
145 static int Direct3D11MapTexture(picture_t *);
146 static int Direct3D11UnmapTexture(picture_t *);
147 static void Direct3D11DeleteRegions(int, picture_t **);
148 static int Direct3D11MapSubpicture(vout_display_t *, int *, picture_t ***, subpicture_t *);
150 static int AllocQuad(vout_display_t *, const video_format_t *, d3d_quad_t *,
151 d3d_quad_cfg_t *, ID3D11PixelShader *, bool b_visible);
152 static void ReleaseQuad(d3d_quad_t *);
153 static void UpdatePicQuadPosition(vout_display_t *);
154 static void UpdateQuadOpacity(vout_display_t *, const d3d_quad_t *, float);
156 static int Control(vout_display_t *vd, int query, va_list args);
157 static void Manage(vout_display_t *vd);
159 /* All the #if USE_DXGI contain an alternative method to setup dx11
160 They both need to be benchmarked to see which performs better */
161 #if USE_DXGI
162 /* I have no idea why MS decided dxgi headers do not define this
163 As they do have prototypes for d3d11 functions */
164 typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory);
165 #endif
167 /* TODO: Move to a direct3d11_shaders header */
168 static const char* globVertexShaderDefault = "\
169 struct VS_INPUT\
171 float4 Position : POSITION;\
172 float2 Texture : TEXCOORD0;\
173 float Opacity : OPACITY;\
176 struct VS_OUTPUT\
178 float4 Position : SV_POSITION;\
179 float2 Texture : TEXCOORD0;\
180 float Opacity : OPACITY;\
183 VS_OUTPUT VS( VS_INPUT In )\
185 VS_OUTPUT Output;\
186 Output.Position = In.Position;\
187 Output.Texture = In.Texture;\
188 Output.Opacity = In.Opacity;\
189 return Output;\
193 static const char* globPixelShaderDefault = "\
194 Texture2D shaderTexture;\
195 SamplerState SampleType;\
197 struct PS_INPUT\
199 float4 Position : SV_POSITION;\
200 float2 Texture : TEXCOORD0;\
201 float Opacity : OPACITY;\
204 float4 PS( PS_INPUT In ) : SV_TARGET\
206 float4 rgba; \
208 rgba = shaderTexture.Sample(SampleType, In.Texture);\
209 rgba.a = rgba.a * In.Opacity;\
210 return rgba; \
214 static const char *globPixelShaderBiplanarYUV_BT601_2RGB = "\
215 Texture2D shaderTextureY;\
216 Texture2D shaderTextureUV;\
217 SamplerState SampleType;\
219 struct PS_INPUT\
221 float4 Position : SV_POSITION;\
222 float2 Texture : TEXCOORD0;\
223 float Opacity : OPACITY;\
226 float4 PS( PS_INPUT In ) : SV_TARGET\
228 float3 yuv;\
229 float4 rgba;\
230 yuv.x = shaderTextureY.Sample(SampleType, In.Texture).x;\
231 yuv.yz = shaderTextureUV.Sample(SampleType, In.Texture).xy;\
232 yuv.x = 1.164383561643836 * (yuv.x-0.0625);\
233 yuv.y = yuv.y - 0.5;\
234 yuv.z = yuv.z - 0.5;\
235 rgba.x = saturate(yuv.x + 1.596026785714286 * yuv.z);\
236 rgba.y = saturate(yuv.x - 0.812967647237771 * yuv.z - 0.391762290094914 * yuv.y);\
237 rgba.z = saturate(yuv.x + 2.017232142857142 * yuv.y);\
238 rgba.a = In.Opacity;\
239 return rgba;\
243 static const char *globPixelShaderBiplanarYUV_BT709_2RGB = "\
244 Texture2D shaderTextureY;\
245 Texture2D shaderTextureUV;\
246 SamplerState SampleType;\
248 struct PS_INPUT\
250 float4 Position : SV_POSITION;\
251 float2 Texture : TEXCOORD0;\
252 float Opacity : OPACITY;\
255 float4 PS( PS_INPUT In ) : SV_TARGET\
257 float3 yuv;\
258 float4 rgba;\
259 yuv.x = shaderTextureY.Sample(SampleType, In.Texture).x;\
260 yuv.yz = shaderTextureUV.Sample(SampleType, In.Texture).xy;\
261 yuv.x = 1.164383561643836 * (yuv.x-0.0625);\
262 yuv.y = yuv.y - 0.5;\
263 yuv.z = yuv.z - 0.5;\
264 rgba.x = saturate(yuv.x + 1.792741071428571 * yuv.z);\
265 rgba.y = saturate(yuv.x - 0.532909328559444 * yuv.z - 0.21324861427373 * yuv.y);\
266 rgba.z = saturate(yuv.x + 2.112401785714286 * yuv.y);\
267 rgba.a = In.Opacity;\
268 return rgba;\
272 /* RGB-709 to RGB-2020 based on https://www.researchgate.net/publication/258434326_Beyond_BT709 */
273 static const char *globPixelShaderBiplanarYUV_BT2020_2RGB = "\
274 Texture2D shaderTextureY;\
275 Texture2D shaderTextureUV;\
276 SamplerState SampleType;\
278 struct PS_INPUT\
280 float4 Position : SV_POSITION;\
281 float2 Texture : TEXCOORD0;\
282 float Opacity : OPACITY;\
285 float4 PS( PS_INPUT In ) : SV_TARGET\
287 float3 yuv;\
288 float4 rgba;\
289 yuv.x = shaderTextureY.Sample(SampleType, In.Texture).x;\
290 yuv.yz = shaderTextureUV.Sample(SampleType, In.Texture).xy;\
291 yuv.x = 1.164383561643836 * (yuv.x-0.0625);\
292 yuv.y = yuv.y - 0.5;\
293 yuv.z = yuv.z - 0.5;\
294 rgba.x = yuv.x + 1.792741071428571 * yuv.z;\
295 rgba.y = yuv.x - 0.532909328559444 * yuv.z - 0.21324861427373 * yuv.y;\
296 rgba.z = yuv.x + 2.112401785714286 * yuv.y;\
297 rgba.x = saturate( 1.661 * rgba.x - 0.588 * rgba.y - 0.073 * rgba.z);\
298 rgba.y = saturate(-0.125 * rgba.x + 1.133 * rgba.y - 0.008 * rgba.z);\
299 rgba.z = saturate(-0.018 * rgba.x - 0.101 * rgba.y + 1.119 * rgba.z);\
300 rgba.a = In.Opacity;\
301 return rgba;\
305 static const char *globPixelShaderBiplanarYUYV_BT709_2RGB = "\
306 Texture2D shaderTextureYUYV;\
307 SamplerState SampleType;\
309 struct PS_INPUT\
311 float4 Position : SV_POSITION;\
312 float2 Texture : TEXCOORD0;\
313 float Opacity : OPACITY;\
316 float4 PS( PS_INPUT In ) : SV_TARGET\
318 float3 yuv;\
319 float4 rgba;\
320 yuv.x = shaderTextureYUYV.Sample(SampleType, In.Texture).x;\
321 yuv.y = shaderTextureYUYV.Sample(SampleType, In.Texture).y;\
322 yuv.z = shaderTextureYUYV.Sample(SampleType, In.Texture).a;\
323 yuv.x = 1.164383561643836 * (yuv.x-0.0625);\
324 yuv.y = yuv.y - 0.5;\
325 yuv.z = yuv.z - 0.5;\
326 rgba.x = saturate(yuv.x + 1.792741071428571 * yuv.z);\
327 rgba.y = saturate(yuv.x - 0.532909328559444 * yuv.z - 0.21324861427373 * yuv.y);\
328 rgba.z = saturate(yuv.x + 2.112401785714286 * yuv.y);\
329 rgba.a = In.Opacity;\
330 return rgba;\
334 static const char *globPixelShaderBiplanarYUYV_BT601_2RGB = "\
335 Texture2D shaderTextureYUYV;\
336 SamplerState SampleType;\
338 struct PS_INPUT\
340 float4 Position : SV_POSITION;\
341 float2 Texture : TEXCOORD0;\
342 float Opacity : OPACITY;\
345 float4 PS( PS_INPUT In ) : SV_TARGET\
347 float3 yuv;\
348 float4 rgba;\
349 yuv.x = shaderTextureYUYV.Sample(SampleType, In.Texture).x;\
350 yuv.y = shaderTextureYUYV.Sample(SampleType, In.Texture).y;\
351 yuv.z = shaderTextureYUYV.Sample(SampleType, In.Texture).a;\
352 yuv.x = 1.164383561643836 * (yuv.x-0.0625);\
353 yuv.y = yuv.y - 0.5;\
354 yuv.z = yuv.z - 0.5;\
355 rgba.x = saturate(yuv.x + 1.596026785714286 * yuv.z);\
356 rgba.y = saturate(yuv.x - 0.812967647237771 * yuv.z - 0.391762290094914 * yuv.y);\
357 rgba.z = saturate(yuv.x + 2.017232142857142 * yuv.y);\
358 rgba.a = In.Opacity;\
359 return rgba;\
363 #if !VLC_WINSTORE_APP
364 static int OpenHwnd(vout_display_t *vd)
366 HINSTANCE hd3d11_dll = LoadLibrary(TEXT("D3D11.DLL"));
367 if (!hd3d11_dll) {
368 msg_Warn(vd, "cannot load d3d11.dll, aborting");
369 return VLC_EGENERIC;
372 HINSTANCE hd3dcompiler_dll = Direct3D11LoadShaderLibrary();
373 if (!hd3dcompiler_dll) {
374 msg_Err(vd, "cannot load d3dcompiler.dll, aborting");
375 Direct3D11Destroy(vd);
376 return VLC_EGENERIC;
379 # if USE_DXGI
380 HINSTANCE hdxgi_dll = LoadLibrary(TEXT("DXGI.DLL"));
381 if (!hdxgi_dll) {
382 msg_Warn(vd, "cannot load dxgi.dll, aborting");
383 Direct3D11Destroy(vd);
384 return VLC_EGENERIC;
386 # endif
388 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
389 if (!sys)
390 return VLC_ENOMEM;
392 sys->hd3d11_dll = hd3d11_dll;
393 sys->hd3dcompiler_dll = hd3dcompiler_dll;
395 sys->OurD3DCompile = (void *)GetProcAddress(sys->hd3dcompiler_dll, "D3DCompile");
396 if (!sys->OurD3DCompile) {
397 msg_Err(vd, "Cannot locate reference to D3DCompile in d3dcompiler DLL");
398 Direct3D11Destroy(vd);
399 return VLC_EGENERIC;
402 # if USE_DXGI
403 sys->hdxgi_dll = hdxgi_dll;
405 /* TODO : enable all dxgi versions from 1.3 -> 1.1 */
406 PFN_CREATE_DXGI_FACTORY OurCreateDXGIFactory =
407 (void *)GetProcAddress(hdxgi_dll, "CreateDXGIFactory");
408 if (!OurCreateDXGIFactory) {
409 msg_Err(vd, "Cannot locate reference to CreateDXGIFactory in dxgi DLL");
410 Direct3D11Destroy(vd);
411 return VLC_EGENERIC;
414 UINT i_factory_flags = 0;
415 #ifndef NDEBUG
416 i_factory_flags |= DXGI_CREATE_FACTORY_DEBUG;
417 #endif
419 /* TODO : detect the directx version supported and use IID_IDXGIFactory3 or 2 */
420 HRESULT hr = OurCreateDXGIFactory(&IID_IDXGIFactory2, (void **)&sys->dxgifactory);
421 if (FAILED(hr)) {
422 msg_Err(vd, "Could not create dxgi factory. (hr=0x%lX)", hr);
423 Direct3D11Destroy(vd);
424 return VLC_EGENERIC;
427 sys->OurD3D11CreateDeviceAndSwapChain =
428 (void *)GetProcAddress(hd3d11_dll, "D3D11CreateDeviceAndSwapChain");
429 if (!sys->OurD3D11CreateDeviceAndSwapChain) {
430 msg_Err(vd, "Cannot locate reference to D3D11CreateDeviceAndSwapChain in d3d11 DLL");
431 Direct3D11Destroy(vd);
432 return VLC_EGENERIC;
435 # else
436 sys->OurD3D11CreateDevice =
437 (void *)GetProcAddress(hd3d11_dll, "D3D11CreateDevice");
438 if (!sys->OurD3D11CreateDevice) {
439 msg_Err(vd, "Cannot locate reference to D3D11CreateDevice in d3d11 DLL");
440 Direct3D11Destroy(vd);
441 return VLC_EGENERIC;
443 # endif
444 return VLC_SUCCESS;
446 #else
447 static int OpenCoreW(vout_display_t *vd)
449 IDXGISwapChain1* dxgiswapChain = var_InheritInteger(vd, "winrt-swapchain");
450 if (!dxgiswapChain)
451 return VLC_EGENERIC;
452 ID3D11Device* d3ddevice = var_InheritInteger(vd, "winrt-d3ddevice");
453 if (!d3ddevice)
454 return VLC_EGENERIC;
455 ID3D11DeviceContext* d3dcontext = var_InheritInteger(vd, "winrt-d3dcontext");
456 if (!d3dcontext)
457 return VLC_EGENERIC;
459 vout_display_sys_t *sys = vd->sys = calloc(1, sizeof(vout_display_sys_t));
460 if (!sys)
461 return VLC_ENOMEM;
463 sys->dxgiswapChain = dxgiswapChain;
464 sys->d3ddevice = d3ddevice;
465 sys->d3dcontext = d3dcontext;
466 IDXGISwapChain_AddRef (sys->dxgiswapChain);
467 ID3D11Device_AddRef (sys->d3ddevice);
468 ID3D11DeviceContext_AddRef(sys->d3dcontext);
470 return VLC_SUCCESS;
472 #endif
474 static bool is_d3d11_opaque(vlc_fourcc_t chroma)
476 switch (chroma)
478 case VLC_CODEC_D3D11_OPAQUE:
479 case VLC_CODEC_D3D11_OPAQUE_10B:
480 return true;
481 default:
482 return false;
486 static int Open(vlc_object_t *object)
488 vout_display_t *vd = (vout_display_t *)object;
490 #if !VLC_WINSTORE_APP
491 int ret = OpenHwnd(vd);
492 #else
493 int ret = OpenCoreW(vd);
494 #endif
496 if (ret != VLC_SUCCESS)
497 return ret;
499 if (CommonInit(vd))
500 goto error;
502 video_format_t fmt;
503 if (Direct3D11Open(vd, &fmt)) {
504 msg_Err(vd, "Direct3D11 could not be opened");
505 goto error;
508 vout_display_info_t info = vd->info;
509 info.is_slow = !is_d3d11_opaque(fmt.i_chroma);
510 info.has_double_click = true;
511 info.has_hide_mouse = false;
512 info.has_event_thread = true;
513 info.has_pictures_invalid = !is_d3d11_opaque(fmt.i_chroma);
515 if (var_InheritBool(vd, "direct3d11-hw-blending") &&
516 vd->sys->d3dregion_format != DXGI_FORMAT_UNKNOWN)
517 info.subpicture_chromas = vd->sys->pSubpictureChromas;
518 else
519 info.subpicture_chromas = NULL;
521 video_format_Clean(&vd->fmt);
522 video_format_Copy(&vd->fmt, &fmt);
523 vd->info = info;
525 vd->pool = Pool;
526 vd->prepare = Prepare;
527 vd->display = Display;
528 vd->control = Control;
529 vd->manage = Manage;
531 msg_Dbg(vd, "Direct3D11 Open Succeeded");
533 return VLC_SUCCESS;
535 error:
536 Close(object);
537 return VLC_EGENERIC;
540 static void Close(vlc_object_t *object)
542 vout_display_t * vd = (vout_display_t *)object;
544 Direct3D11Close(vd);
545 CommonClean(vd);
546 Direct3D11Destroy(vd);
547 free(vd->sys);
550 static picture_pool_t *Pool(vout_display_t *vd, unsigned pool_size)
552 if ( vd->sys->pool != NULL )
553 return vd->sys->pool;
555 #ifdef HAVE_ID3D11VIDEODECODER
556 picture_t** pictures = NULL;
557 unsigned picture_count = 0;
558 HRESULT hr;
560 ID3D10Multithread *pMultithread;
561 hr = ID3D11Device_QueryInterface( vd->sys->d3ddevice, &IID_ID3D10Multithread, (void **)&pMultithread);
562 if (SUCCEEDED(hr)) {
563 ID3D10Multithread_SetMultithreadProtected(pMultithread, TRUE);
564 ID3D10Multithread_Release(pMultithread);
567 pictures = calloc(pool_size, sizeof(*pictures));
568 if (!pictures)
569 goto error;
571 D3D11_TEXTURE2D_DESC texDesc;
572 ZeroMemory(&texDesc, sizeof(texDesc));
573 texDesc.Width = vd->fmt.i_width;
574 texDesc.Height = vd->fmt.i_height;
575 texDesc.MipLevels = 1;
576 texDesc.Format = vd->sys->picQuadConfig.textureFormat;
577 texDesc.SampleDesc.Count = 1;
578 texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
579 texDesc.Usage = D3D11_USAGE_DEFAULT;
580 texDesc.BindFlags = D3D11_BIND_DECODER;
581 texDesc.CPUAccessFlags = 0;
583 texDesc.ArraySize = pool_size;
585 ID3D11Texture2D *texture;
586 hr = ID3D11Device_CreateTexture2D( vd->sys->d3ddevice, &texDesc, NULL, &texture );
587 if (FAILED(hr)) {
588 msg_Err(vd, "CreateTexture2D failed for the %d pool. (hr=0x%0lx)", pool_size, hr);
589 goto error;
592 for (picture_count = 0; picture_count < pool_size; picture_count++) {
593 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
594 if (unlikely(picsys == NULL))
595 goto error;
597 ID3D11Texture2D_AddRef(texture);
598 picsys->texture = texture;
599 picsys->slice_index = picture_count;
600 picsys->context = vd->sys->d3dcontext;
602 picture_resource_t resource = {
603 .p_sys = picsys,
604 .pf_destroy = DestroyDisplayPoolPicture,
607 picture_t *picture = picture_NewFromResource(&vd->fmt, &resource);
608 if (unlikely(picture == NULL)) {
609 free(picsys);
610 msg_Err( vd, "Failed to create picture %d in the pool.", picture_count );
611 goto error;
614 pictures[picture_count] = picture;
615 /* each picture_t holds a ref to the context and release it on Destroy */
616 ID3D11DeviceContext_AddRef(picsys->context);
618 ID3D11Texture2D_Release(texture);
620 msg_Dbg(vd, "ID3D11VideoDecoderOutputView succeed with %d surfaces (%dx%d) texture 0x%p context 0x%p",
621 pool_size, vd->fmt.i_width, vd->fmt.i_height, texture, vd->sys->d3dcontext);
623 picture_pool_configuration_t pool_cfg;
624 memset(&pool_cfg, 0, sizeof(pool_cfg));
625 pool_cfg.picture_count = pool_size;
626 pool_cfg.picture = pictures;
628 vd->sys->pool = picture_pool_NewExtended( &pool_cfg );
630 error:
631 if (vd->sys->pool ==NULL && pictures) {
632 msg_Dbg(vd, "Failed to create the picture d3d11 pool");
633 for (unsigned i=0;i<picture_count; ++i)
634 DestroyDisplayPoolPicture(pictures[i]);
635 free(pictures);
637 /* create an empty pool to avoid crashing */
638 picture_pool_configuration_t pool_cfg;
639 memset( &pool_cfg, 0, sizeof( pool_cfg ) );
640 pool_cfg.picture_count = 0;
642 vd->sys->pool = picture_pool_NewExtended( &pool_cfg );
644 #endif
645 return vd->sys->pool;
648 #ifdef HAVE_ID3D11VIDEODECODER
649 static void DestroyDisplayPoolPicture(picture_t *picture)
651 picture_sys_t *p_sys = (picture_sys_t*) picture->p_sys;
653 if (p_sys->texture)
654 ID3D11Texture2D_Release(p_sys->texture);
655 if (p_sys->context)
656 ID3D11DeviceContext_Release(p_sys->context);
658 free(p_sys);
659 free(picture);
661 #endif
663 static void DestroyDisplayPicture(picture_t *picture)
665 picture_sys_pool_t *p_sys = (picture_sys_pool_t*) picture->p_sys;
667 if (p_sys->texture)
668 ID3D11Texture2D_Release(p_sys->texture);
670 free(p_sys);
671 free(picture);
674 static HRESULT UpdateBackBuffer(vout_display_t *vd)
676 vout_display_sys_t *sys = vd->sys;
677 HRESULT hr;
678 ID3D11Texture2D* pDepthStencil;
679 ID3D11Texture2D* pBackBuffer;
680 uint32_t i_width = RECTWidth(sys->rect_dest_clipped);
681 uint32_t i_height = RECTHeight(sys->rect_dest_clipped);
682 #if VLC_WINSTORE_APP
683 UINT dataSize = sizeof(i_width);
684 hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_WIDTH, &dataSize, &i_width);
685 if (FAILED(hr)) {
686 msg_Err(vd, "Can't get swapchain width, size %d. (hr=0x%lX)", hr, dataSize);
687 return hr;
689 dataSize = sizeof(i_height);
690 hr = IDXGISwapChain_GetPrivateData(sys->dxgiswapChain, &GUID_SWAPCHAIN_HEIGHT, &dataSize, &i_height);
691 if (FAILED(hr)) {
692 msg_Err(vd, "Can't get swapchain height, size %d. (hr=0x%lX)", hr, dataSize);
693 return hr;
695 #endif
697 if (sys->d3drenderTargetView) {
698 ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
699 sys->d3drenderTargetView = NULL;
701 if (sys->d3ddepthStencilView) {
702 ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
703 sys->d3ddepthStencilView = NULL;
706 hr = IDXGISwapChain_ResizeBuffers(sys->dxgiswapChain, 0, i_width, i_height,
707 DXGI_FORMAT_UNKNOWN, 0);
708 if (FAILED(hr)) {
709 msg_Err(vd, "Failed to resize the backbuffer. (hr=0x%lX)", hr);
710 return hr;
713 hr = IDXGISwapChain_GetBuffer(sys->dxgiswapChain, 0, &IID_ID3D11Texture2D, (LPVOID *)&pBackBuffer);
714 if (FAILED(hr)) {
715 msg_Err(vd, "Could not get the backbuffer for the Swapchain. (hr=0x%lX)", hr);
716 return hr;
719 hr = ID3D11Device_CreateRenderTargetView(sys->d3ddevice, (ID3D11Resource *)pBackBuffer, NULL, &sys->d3drenderTargetView);
720 ID3D11Texture2D_Release(pBackBuffer);
721 if (FAILED(hr)) {
722 msg_Err(vd, "Failed to create the target view. (hr=0x%lX)", hr);
723 return hr;
726 D3D11_TEXTURE2D_DESC deptTexDesc;
727 memset(&deptTexDesc, 0,sizeof(deptTexDesc));
728 deptTexDesc.ArraySize = 1;
729 deptTexDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
730 deptTexDesc.CPUAccessFlags = 0;
731 deptTexDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
732 deptTexDesc.Width = i_width;
733 deptTexDesc.Height = i_height;
734 deptTexDesc.MipLevels = 1;
735 deptTexDesc.MiscFlags = 0;
736 deptTexDesc.SampleDesc.Count = 1;
737 deptTexDesc.SampleDesc.Quality = 0;
738 deptTexDesc.Usage = D3D11_USAGE_DEFAULT;
740 hr = ID3D11Device_CreateTexture2D(sys->d3ddevice, &deptTexDesc, NULL, &pDepthStencil);
741 if (FAILED(hr)) {
742 msg_Err(vd, "Could not create the depth stencil texture. (hr=0x%lX)", hr);
743 return hr;
746 D3D11_DEPTH_STENCIL_VIEW_DESC depthViewDesc;
747 memset(&depthViewDesc, 0, sizeof(depthViewDesc));
749 depthViewDesc.Format = deptTexDesc.Format;
750 depthViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
751 depthViewDesc.Texture2D.MipSlice = 0;
753 hr = ID3D11Device_CreateDepthStencilView(sys->d3ddevice, (ID3D11Resource *)pDepthStencil, &depthViewDesc, &sys->d3ddepthStencilView);
754 ID3D11Texture2D_Release(pDepthStencil);
756 if (FAILED(hr)) {
757 msg_Err(vd, "Could not create the depth stencil view. (hr=0x%lX)", hr);
758 return hr;
761 return S_OK;
764 static void CropStagingFormat(vout_display_t *vd, video_format_t *backup_fmt)
766 if ( vd->sys->stagingQuad.pTexture == NULL )
767 return;
769 video_format_Copy( backup_fmt, &vd->source );
770 /* the texture we display is a cropped version of the source */
771 vd->source.i_x_offset = 0;
772 vd->source.i_y_offset = 0;
773 vd->source.i_width = vd->source.i_visible_width;
774 vd->source.i_height = vd->source.i_visible_height;
777 static void UncropStagingFormat(vout_display_t *vd, video_format_t *backup_fmt)
779 if ( vd->sys->stagingQuad.pTexture == NULL )
780 return;
781 video_format_Copy( &vd->source, backup_fmt );
784 static int Control(vout_display_t *vd, int query, va_list args)
786 video_format_t core_source;
787 CropStagingFormat( vd, &core_source );
788 int res = CommonControl( vd, query, args );
789 UncropStagingFormat( vd, &core_source );
790 return res;
793 static void Manage(vout_display_t *vd)
795 vout_display_sys_t *sys = vd->sys;
796 RECT size_before = sys->rect_dest_clipped;
798 video_format_t core_source;
799 CropStagingFormat( vd, &core_source );
800 CommonManage(vd);
802 if (RECTWidth(size_before) != RECTWidth(sys->rect_dest_clipped) ||
803 RECTHeight(size_before) != RECTHeight(sys->rect_dest_clipped))
805 #if defined(HAVE_ID3D11VIDEODECODER)
806 if( sys->context_lock != INVALID_HANDLE_VALUE )
808 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
810 #endif
811 msg_Dbg(vd, "Manage detected size change %dx%d", RECTWidth(sys->rect_dest_clipped),
812 RECTHeight(sys->rect_dest_clipped));
814 UpdateBackBuffer(vd);
816 UpdatePicQuadPosition(vd);
817 #if defined(HAVE_ID3D11VIDEODECODER)
818 if( sys->context_lock != INVALID_HANDLE_VALUE )
820 ReleaseMutex( sys->context_lock );
822 #endif
824 UncropStagingFormat( vd, &core_source );
827 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
829 vout_display_sys_t *sys = vd->sys;
831 if ( !is_d3d11_opaque(picture->format.i_chroma) &&
832 sys->stagingQuad.pTexture != NULL )
834 Direct3D11UnmapTexture(picture);
836 D3D11_BOX box;
837 box.left = picture->format.i_x_offset;
838 /* box.right = picture->format.i_x_offset + picture->format.i_visible_width; */
839 box.top = picture->format.i_y_offset;
840 /* box.bottom = picture->format.i_y_offset + picture->format.i_visible_height; */
841 box.back = 1;
842 box.front = 0;
844 D3D11_TEXTURE2D_DESC dstDesc;
845 ID3D11Texture2D_GetDesc(sys->picQuad.pTexture, &dstDesc);
846 box.bottom = box.top + dstDesc.Height;
847 box.right = box.left + dstDesc.Width;
849 ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
850 (ID3D11Resource*) sys->picQuad.pTexture,
851 0, 0, 0, 0,
852 (ID3D11Resource*) sys->stagingQuad.pTexture,
853 0, &box);
856 #ifdef HAVE_ID3D11VIDEODECODER
857 if (is_d3d11_opaque(picture->format.i_chroma)) {
858 if( sys->context_lock != INVALID_HANDLE_VALUE )
860 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
862 D3D11_BOX box;
863 picture_sys_t *p_sys = picture->p_sys;
864 D3D11_TEXTURE2D_DESC texDesc;
865 ID3D11Texture2D_GetDesc( p_sys->texture, &texDesc );
866 if (texDesc.Format == DXGI_FORMAT_NV12 || texDesc.Format == DXGI_FORMAT_P010)
868 box.left = (picture->format.i_x_offset + 1) & ~1;
869 box.right = (picture->format.i_x_offset + picture->format.i_visible_width) & ~1;
870 box.top = (picture->format.i_y_offset + 1) & ~1;
871 box.bottom = (picture->format.i_y_offset + picture->format.i_visible_height) & ~1;
873 else
875 box.left = picture->format.i_x_offset;
876 box.right = picture->format.i_x_offset + picture->format.i_visible_width;
877 box.top = picture->format.i_y_offset;
878 box.bottom = picture->format.i_y_offset + picture->format.i_visible_height;
880 box.back = 1;
881 box.front = 0;
883 ID3D11DeviceContext_CopySubresourceRegion(sys->d3dcontext,
884 (ID3D11Resource*) sys->picQuad.pTexture,
885 0, 0, 0, 0,
886 (ID3D11Resource*) p_sys->texture,
887 p_sys->slice_index, &box);
889 #endif
891 if (subpicture) {
892 int subpicture_region_count = 0;
893 picture_t **subpicture_regions = NULL;
894 Direct3D11MapSubpicture(vd, &subpicture_region_count, &subpicture_regions, subpicture);
895 Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
896 sys->d3dregion_count = subpicture_region_count;
897 sys->d3dregions = subpicture_regions;
901 static void DisplayD3DPicture(vout_display_sys_t *sys, d3d_quad_t *quad)
903 UINT stride = sizeof(d3d_vertex_t);
904 UINT offset = 0;
906 /* Render the quad */
907 ID3D11DeviceContext_RSSetViewports(sys->d3dcontext, 1, &quad->cropViewport);
908 ID3D11DeviceContext_PSSetShader(sys->d3dcontext, quad->d3dpixelShader, NULL, 0);
909 ID3D11DeviceContext_PSSetShaderResources(sys->d3dcontext, 0, 1, &quad->d3dresViewY);
911 if( quad->d3dresViewUV )
912 ID3D11DeviceContext_PSSetShaderResources(sys->d3dcontext, 1, 1, &quad->d3dresViewUV);
914 ID3D11DeviceContext_IASetVertexBuffers(sys->d3dcontext, 0, 1, &quad->pVertexBuffer, &stride, &offset);
915 ID3D11DeviceContext_DrawIndexed(sys->d3dcontext, 6, 0, 0);
918 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
920 vout_display_sys_t *sys = vd->sys;
922 FLOAT blackRGBA[4] = {0.0f, 0.0f, 0.0f, 1.0f};
923 ID3D11DeviceContext_ClearRenderTargetView(sys->d3dcontext, sys->d3drenderTargetView, blackRGBA);
925 /* no ID3D11Device operations should come here */
927 ID3D11DeviceContext_OMSetRenderTargets(sys->d3dcontext, 1, &sys->d3drenderTargetView, sys->d3ddepthStencilView);
929 ID3D11DeviceContext_ClearDepthStencilView(sys->d3dcontext, sys->d3ddepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
931 if ( !is_d3d11_opaque(picture->format.i_chroma) &&
932 sys->stagingQuad.pTexture == NULL )
933 Direct3D11UnmapTexture(picture);
935 /* Render the quad */
936 DisplayD3DPicture(sys, &sys->picQuad);
938 if (subpicture) {
939 // draw the additional vertices
940 for (int i = 0; i < sys->d3dregion_count; ++i) {
941 if (sys->d3dregions[i])
942 DisplayD3DPicture(sys, (d3d_quad_t *) sys->d3dregions[i]->p_sys);
946 DXGI_PRESENT_PARAMETERS presentParams;
947 memset(&presentParams, 0, sizeof(presentParams));
948 HRESULT hr = IDXGISwapChain1_Present1(sys->dxgiswapChain, 0, 0, &presentParams);
949 if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
951 /* TODO device lost */
952 msg_Dbg(vd, "SwapChain Present failed. (hr=0x%lX)", hr);
954 #if defined(HAVE_ID3D11VIDEODECODER)
955 if( is_d3d11_opaque(picture->format.i_chroma) && sys->context_lock != INVALID_HANDLE_VALUE) {
956 ReleaseMutex( sys->context_lock );
958 #endif
960 picture_Release(picture);
961 if (subpicture)
962 subpicture_Delete(subpicture);
964 CommonDisplay(vd);
967 static void Direct3D11Destroy(vout_display_t *vd)
969 #if !VLC_WINSTORE_APP
970 vout_display_sys_t *sys = vd->sys;
972 # if USE_DXGI
973 if (sys->hdxgi_dll)
974 FreeLibrary(sys->hdxgi_dll);
975 # endif
977 if (sys->hd3d11_dll)
978 FreeLibrary(sys->hd3d11_dll);
979 if (sys->hd3dcompiler_dll)
980 FreeLibrary(sys->hd3dcompiler_dll);
982 sys->OurD3D11CreateDevice = NULL;
983 sys->OurD3D11CreateDeviceAndSwapChain = NULL;
984 sys->OurD3DCompile = NULL;
985 sys->hdxgi_dll = NULL;
986 sys->hd3d11_dll = NULL;
987 sys->hd3dcompiler_dll = NULL;
988 #else
989 VLC_UNUSED(vd);
990 #endif
993 #if !VLC_WINSTORE_APP
994 static HINSTANCE Direct3D11LoadShaderLibrary(void)
996 HINSTANCE instance = NULL;
997 /* d3dcompiler_47 is the latest on windows 8.1 */
998 for (int i = 47; i > 41; --i) {
999 TCHAR filename[19];
1000 _sntprintf(filename, 19, TEXT("D3DCOMPILER_%d.dll"), i);
1001 instance = LoadLibrary(filename);
1002 if (instance) break;
1004 return instance;
1006 #endif
1009 static int Direct3D11Open(vout_display_t *vd, video_format_t *fmt)
1011 vout_display_sys_t *sys = vd->sys;
1012 *fmt = vd->source;
1014 #if !VLC_WINSTORE_APP
1016 UINT creationFlags = 0;
1017 HRESULT hr = S_OK;
1019 # if !defined(NDEBUG) && defined(_MSC_VER)
1020 creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
1021 # endif
1023 DXGI_SWAP_CHAIN_DESC1 scd;
1024 memset(&scd, 0, sizeof(scd));
1025 scd.BufferCount = 2;
1026 scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1027 scd.SampleDesc.Count = 1;
1028 scd.SampleDesc.Quality = 0;
1029 scd.Width = fmt->i_visible_width;
1030 scd.Height = fmt->i_visible_height;
1031 switch(fmt->i_chroma)
1033 case VLC_CODEC_D3D11_OPAQUE_10B:
1034 scd.Format = DXGI_FORMAT_R10G10B10A2_UNORM;
1035 break;
1036 default:
1037 scd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; /* TODO: use DXGI_FORMAT_NV12 */
1038 break;
1040 //scd.Flags = 512; // DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1041 scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1043 IDXGIAdapter *dxgiadapter;
1044 static const D3D_FEATURE_LEVEL featureLevels[] =
1046 0xc000 /* D3D_FEATURE_LEVEL_12_1 */,
1047 0xc100 /* D3D_FEATURE_LEVEL_12_0 */,
1048 D3D_FEATURE_LEVEL_11_1,
1049 D3D_FEATURE_LEVEL_11_0,
1050 D3D_FEATURE_LEVEL_10_1,
1051 D3D_FEATURE_LEVEL_10_0,
1052 D3D_FEATURE_LEVEL_9_3,
1053 D3D_FEATURE_LEVEL_9_2,
1054 D3D_FEATURE_LEVEL_9_1,
1057 # if USE_DXGI
1058 /* TODO : list adapters for the user to choose from */
1059 hr = IDXGIFactory2_EnumAdapters(sys->dxgifactory, 0, &dxgiadapter);
1060 if (FAILED(hr)) {
1061 msg_Err(vd, "Could not create find factory. (hr=0x%lX)", hr);
1062 return VLC_EGENERIC;
1065 IDXGIOutput* output;
1066 hr = IDXGIAdapter_EnumOutputs(dxgiadapter, 0, &output);
1067 if (FAILED(hr)) {
1068 msg_Err(vd, "Could not Enumerate DXGI Outputs. (hr=0x%lX)", hr);
1069 IDXGIAdapter_Release(dxgiadapter);
1070 return VLC_EGENERIC;
1073 DXGI_MODE_DESC md;
1074 memset(&md, 0, sizeof(md));
1075 md.Width = fmt->i_visible_width;
1076 md.Height = fmt->i_visible_height;
1077 md.Format = scd.BufferDesc.Format;
1078 md.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
1080 hr = IDXGIOutput_FindClosestMatchingMode(output, &md, &scd.BufferDesc, NULL);
1081 if (FAILED(hr)) {
1082 msg_Err(vd, "Failed to find a supported video mode. (hr=0x%lX)", hr);
1083 IDXGIAdapter_Release(dxgiadapter);
1084 return VLC_EGENERIC;
1087 /* mode desc doesn't carry over the width and height*/
1088 scd.BufferDesc.Width = fmt->i_visible_width;
1089 scd.BufferDesc.Height = fmt->i_visible_height;
1091 hr = D3D11CreateDeviceAndSwapChain(dxgiadapter,
1092 D3D_DRIVER_TYPE_UNKNOWN, NULL, creationFlags,
1093 featureLevels, ARRAYSIZE(featureLevels),
1094 D3D11_SDK_VERSION, &scd, &sys->dxgiswapChain,
1095 &sys->d3ddevice, NULL, &sys->d3dcontext);
1096 IDXGIAdapter_Release(dxgiadapter);
1097 if (FAILED(hr)) {
1098 msg_Err(vd, "Could not Create the D3D11 device and SwapChain. (hr=0x%lX)", hr);
1099 return VLC_EGENERIC;
1102 # else
1104 static const D3D_DRIVER_TYPE driverAttempts[] = {
1105 D3D_DRIVER_TYPE_HARDWARE,
1106 D3D_DRIVER_TYPE_WARP,
1107 #ifndef NDEBUG
1108 D3D_DRIVER_TYPE_REFERENCE,
1109 #endif
1112 for (UINT driver = 0; driver < ARRAYSIZE(driverAttempts); driver++) {
1113 D3D_FEATURE_LEVEL i_feature_level;
1114 hr = D3D11CreateDevice(NULL, driverAttempts[driver], NULL, creationFlags,
1115 featureLevels, 9, D3D11_SDK_VERSION,
1116 &sys->d3ddevice, &i_feature_level, &sys->d3dcontext);
1117 if (SUCCEEDED(hr)) {
1118 #ifndef NDEBUG
1119 msg_Dbg(vd, "Created the D3D11 device 0x%p ctx 0x%p type %d level %x.",
1120 (void *)sys->d3ddevice, (void *)sys->d3dcontext,
1121 driverAttempts[driver], i_feature_level);
1122 #endif
1123 break;
1127 if (FAILED(hr)) {
1128 msg_Err(vd, "Could not Create the D3D11 device. (hr=0x%lX)", hr);
1129 return VLC_EGENERIC;
1132 IDXGIDevice *pDXGIDevice = NULL;
1133 hr = ID3D11Device_QueryInterface(sys->d3ddevice, &IID_IDXGIDevice, (void **)&pDXGIDevice);
1134 if (FAILED(hr)) {
1135 msg_Err(vd, "Could not Query DXGI Interface. (hr=0x%lX)", hr);
1136 return VLC_EGENERIC;
1139 hr = IDXGIDevice_GetAdapter(pDXGIDevice, &dxgiadapter);
1140 IDXGIAdapter_Release(pDXGIDevice);
1141 if (FAILED(hr)) {
1142 msg_Err(vd, "Could not get the DXGI Adapter. (hr=0x%lX)", hr);
1143 return VLC_EGENERIC;
1146 hr = IDXGIAdapter_GetParent(dxgiadapter, &IID_IDXGIFactory2, (void **)&sys->dxgifactory);
1147 IDXGIAdapter_Release(dxgiadapter);
1148 if (FAILED(hr)) {
1149 msg_Err(vd, "Could not get the DXGI Factory. (hr=0x%lX)", hr);
1150 return VLC_EGENERIC;
1153 hr = IDXGIFactory2_CreateSwapChainForHwnd(sys->dxgifactory, (IUnknown *)sys->d3ddevice,
1154 sys->hvideownd, &scd, NULL, NULL, &sys->dxgiswapChain);
1155 IDXGIFactory2_Release(sys->dxgifactory);
1156 if (FAILED(hr)) {
1157 msg_Err(vd, "Could not create the SwapChain. (hr=0x%lX)", hr);
1158 return VLC_EGENERIC;
1161 # endif
1162 #endif
1164 vlc_fourcc_t i_src_chroma = fmt->i_chroma;
1165 fmt->i_chroma = 0;
1167 // look for the requested pixel format first
1168 UINT i_quadSupportFlags = D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_SHADER_LOAD;
1169 UINT i_formatSupport;
1170 for (const d3d_format_t *output_format = GetRenderFormatList();
1171 output_format->name != NULL; ++output_format)
1173 if( i_src_chroma == output_format->fourcc )
1175 if( SUCCEEDED( ID3D11Device_CheckFormatSupport(sys->d3ddevice,
1176 output_format->formatTexture,
1177 &i_formatSupport)) &&
1178 ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags )
1180 msg_Dbg( vd, "Using pixel format %s from chroma %4.4s", output_format->name,
1181 (char *)&i_src_chroma );
1182 fmt->i_chroma = output_format->fourcc;
1183 DxgiFormatMask( output_format->formatTexture, fmt );
1184 sys->picQuadConfig.textureFormat = output_format->formatTexture;
1185 sys->picQuadConfig.resourceFormatYRGB = output_format->formatY;
1186 sys->picQuadConfig.resourceFormatUV = output_format->formatUV;
1187 break;
1192 // look for any pixel format that we can handle with enough pixels per channel
1193 if ( !fmt->i_chroma )
1195 uint8_t bits_per_channel;
1196 switch (i_src_chroma)
1198 case VLC_CODEC_D3D11_OPAQUE:
1199 bits_per_channel = 8;
1200 break;
1201 case VLC_CODEC_D3D11_OPAQUE_10B:
1202 bits_per_channel = 10;
1203 break;
1204 default:
1206 const vlc_chroma_description_t *p_format = vlc_fourcc_GetChromaDescription(i_src_chroma);
1207 bits_per_channel = p_format == NULL || p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits;
1209 break;
1212 for (const d3d_format_t *output_format = GetRenderFormatList();
1213 output_format->name != NULL; ++output_format)
1215 if( bits_per_channel <= output_format->bitsPerChannel &&
1216 !is_d3d11_opaque(output_format->fourcc) )
1218 if( SUCCEEDED( ID3D11Device_CheckFormatSupport(sys->d3ddevice,
1219 output_format->formatTexture,
1220 &i_formatSupport)) &&
1221 ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags )
1223 msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", output_format->name,
1224 (char *)&i_src_chroma );
1225 fmt->i_chroma = output_format->fourcc;
1226 DxgiFormatMask( output_format->formatTexture, fmt );
1227 sys->picQuadConfig.textureFormat = output_format->formatTexture;
1228 sys->picQuadConfig.resourceFormatYRGB = output_format->formatY;
1229 sys->picQuadConfig.resourceFormatUV = output_format->formatUV;
1230 break;
1235 // look for any pixel format that we can handle
1236 if ( !fmt->i_chroma )
1238 for (const d3d_format_t *output_format = GetRenderFormatList();
1239 output_format->name != NULL; ++output_format)
1241 if( SUCCEEDED( ID3D11Device_CheckFormatSupport(sys->d3ddevice,
1242 output_format->formatTexture,
1243 &i_formatSupport)) &&
1244 ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags &&
1245 !is_d3d11_opaque(output_format->fourcc) )
1247 msg_Dbg( vd, "Using pixel format %s for chroma %4.4s", output_format->name,
1248 (char *)&i_src_chroma );
1249 fmt->i_chroma = output_format->fourcc;
1250 DxgiFormatMask( output_format->formatTexture, fmt );
1251 sys->picQuadConfig.textureFormat = output_format->formatTexture;
1252 sys->picQuadConfig.resourceFormatYRGB = output_format->formatY;
1253 sys->picQuadConfig.resourceFormatUV = output_format->formatUV;
1254 break;
1258 if ( !fmt->i_chroma )
1260 msg_Err(vd, "Could not get a suitable texture pixel format");
1261 return VLC_EGENERIC;
1264 /* check the region pixel format */
1265 i_quadSupportFlags |= D3D11_FORMAT_SUPPORT_BLENDABLE;
1266 if( SUCCEEDED( ID3D11Device_CheckFormatSupport(sys->d3ddevice,
1267 DXGI_FORMAT_R8G8B8A8_UNORM,
1268 &i_formatSupport)) &&
1269 ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags) {
1270 sys->d3dregion_format = DXGI_FORMAT_R8G8B8A8_UNORM;
1271 sys->pSubpictureChromas[0] = VLC_CODEC_RGBA;
1272 sys->pSubpictureChromas[1] = 0;
1273 } else if( SUCCEEDED( ID3D11Device_CheckFormatSupport(sys->d3ddevice,
1274 DXGI_FORMAT_B8G8R8A8_UNORM,
1275 &i_formatSupport)) &&
1276 ( i_formatSupport & i_quadSupportFlags ) == i_quadSupportFlags) {
1277 sys->d3dregion_format = DXGI_FORMAT_B8G8R8A8_UNORM;
1278 sys->pSubpictureChromas[0] = VLC_CODEC_BGRA;
1279 sys->pSubpictureChromas[1] = 0;
1280 } else {
1281 sys->d3dregion_format = DXGI_FORMAT_UNKNOWN;
1284 if (sys->picQuadConfig.resourceFormatYRGB == DXGI_FORMAT_R8_UNORM ||
1285 sys->picQuadConfig.resourceFormatYRGB == DXGI_FORMAT_R16_UNORM)
1287 if (vd->fmt.space == COLOR_SPACE_BT2020)
1288 sys->d3dPxShader = globPixelShaderBiplanarYUV_BT2020_2RGB;
1289 else if (vd->fmt.space == COLOR_SPACE_BT709)
1290 sys->d3dPxShader = globPixelShaderBiplanarYUV_BT709_2RGB;
1291 else if (vd->fmt.space == COLOR_SPACE_BT601)
1292 sys->d3dPxShader = globPixelShaderBiplanarYUV_BT601_2RGB;
1293 else if( fmt->i_height > 576 )
1294 sys->d3dPxShader = globPixelShaderBiplanarYUV_BT709_2RGB;
1295 else
1296 sys->d3dPxShader = globPixelShaderBiplanarYUV_BT601_2RGB;
1298 else
1299 if (fmt->i_chroma == VLC_CODEC_YUYV)
1301 if( fmt->i_height > 576 )
1302 sys->d3dPxShader = globPixelShaderBiplanarYUYV_BT709_2RGB;
1303 else
1304 sys->d3dPxShader = globPixelShaderBiplanarYUYV_BT601_2RGB;
1306 else
1307 sys->d3dPxShader = globPixelShaderDefault;
1309 if (sys->d3dregion_format != DXGI_FORMAT_UNKNOWN)
1310 sys->psz_rgbaPxShader = globPixelShaderDefault;
1311 else
1312 sys->psz_rgbaPxShader = NULL;
1314 if ( fmt->i_height != fmt->i_visible_height || fmt->i_width != fmt->i_visible_width )
1316 msg_Dbg( vd, "use a staging texture to crop to visible size" );
1317 AllocQuad( vd, fmt, &sys->stagingQuad, &sys->picQuadConfig, NULL, false );
1320 video_format_t core_source;
1321 CropStagingFormat( vd, &core_source );
1322 UpdateRects(vd, NULL, NULL, true);
1323 UncropStagingFormat( vd, &core_source );
1325 #if defined(HAVE_ID3D11VIDEODECODER)
1326 if( sys->context_lock != INVALID_HANDLE_VALUE )
1328 WaitForSingleObjectEx( sys->context_lock, INFINITE, FALSE );
1330 #endif
1331 if (Direct3D11CreateResources(vd, fmt)) {
1332 #if defined(HAVE_ID3D11VIDEODECODER)
1333 if( sys->context_lock != INVALID_HANDLE_VALUE )
1335 ReleaseMutex( sys->context_lock );
1337 #endif
1338 msg_Err(vd, "Failed to allocate resources");
1339 Direct3D11DestroyResources(vd);
1340 return VLC_EGENERIC;
1342 #if defined(HAVE_ID3D11VIDEODECODER)
1343 if( sys->context_lock != INVALID_HANDLE_VALUE )
1345 ReleaseMutex( sys->context_lock );
1347 #endif
1349 #if !VLC_WINSTORE_APP
1350 EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D11 output)");
1351 #endif
1353 msg_Dbg(vd, "Direct3D11 device adapter successfully initialized");
1354 return VLC_SUCCESS;
1357 static void Direct3D11Close(vout_display_t *vd)
1359 vout_display_sys_t *sys = vd->sys;
1361 Direct3D11DestroyResources(vd);
1362 if (sys->d3dcontext)
1364 ID3D11DeviceContext_Flush(sys->d3dcontext);
1365 ID3D11DeviceContext_Release(sys->d3dcontext);
1366 sys->d3dcontext = NULL;
1368 if (sys->d3ddevice)
1370 ID3D11Device_Release(sys->d3ddevice);
1371 sys->d3ddevice = NULL;
1373 if (sys->dxgiswapChain)
1375 IDXGISwapChain_Release(sys->dxgiswapChain);
1376 sys->dxgiswapChain = NULL;
1379 msg_Dbg(vd, "Direct3D11 device adapter closed");
1382 static void UpdatePicQuadPosition(vout_display_t *vd)
1384 vout_display_sys_t *sys = vd->sys;
1385 int i_width = RECTWidth(sys->rect_dest_clipped);
1386 int i_height = RECTHeight(sys->rect_dest_clipped);
1388 int i_top = sys->rect_src_clipped.top * i_height;
1389 i_top /= vd->source.i_visible_height;
1390 i_top -= sys->rect_dest_clipped.top;
1391 int i_left = sys->rect_src_clipped.left * i_width;
1392 i_left /= vd->source.i_visible_width;
1393 i_left -= sys->rect_dest_clipped.left;
1395 sys->picQuad.cropViewport.Width = (FLOAT) vd->source.i_width * i_width / vd->source.i_visible_width;
1396 sys->picQuad.cropViewport.Height = (FLOAT) vd->source.i_height * i_height / vd->source.i_visible_height;
1397 sys->picQuad.cropViewport.TopLeftX = -i_left;
1398 sys->picQuad.cropViewport.TopLeftY = -i_top;
1400 sys->picQuad.cropViewport.MinDepth = 0.0f;
1401 sys->picQuad.cropViewport.MaxDepth = 1.0f;
1403 #ifndef NDEBUG
1404 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 );
1405 #endif
1408 /* TODO : handle errors better
1409 TODO : seperate out into smaller functions like createshaders */
1410 static int Direct3D11CreateResources(vout_display_t *vd, video_format_t *fmt)
1412 vout_display_sys_t *sys = vd->sys;
1413 HRESULT hr;
1415 #if defined(HAVE_ID3D11VIDEODECODER)
1416 sys->context_lock = CreateMutexEx( NULL, NULL, 0, SYNCHRONIZE );
1417 ID3D11Device_SetPrivateData( sys->d3ddevice, &GUID_CONTEXT_MUTEX, sizeof( sys->context_lock ), &sys->context_lock );
1418 #endif
1420 hr = UpdateBackBuffer(vd);
1421 if (FAILED(hr)) {
1422 msg_Err(vd, "Could not update the backbuffer. (hr=0x%lX)", hr);
1423 return VLC_EGENERIC;
1426 ID3D11BlendState *pSpuBlendState;
1427 D3D11_BLEND_DESC spuBlendDesc = { 0 };
1428 spuBlendDesc.RenderTarget[0].BlendEnable = TRUE;
1429 spuBlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
1430 spuBlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
1431 spuBlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
1433 spuBlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
1434 spuBlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
1435 spuBlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1437 spuBlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
1439 spuBlendDesc.RenderTarget[1].BlendEnable = TRUE;
1440 spuBlendDesc.RenderTarget[1].SrcBlend = D3D11_BLEND_ONE;
1441 spuBlendDesc.RenderTarget[1].DestBlend = D3D11_BLEND_ZERO;
1442 spuBlendDesc.RenderTarget[1].BlendOp = D3D11_BLEND_OP_ADD;
1444 spuBlendDesc.RenderTarget[1].SrcBlendAlpha = D3D11_BLEND_ONE;
1445 spuBlendDesc.RenderTarget[1].DestBlendAlpha = D3D11_BLEND_ZERO;
1446 spuBlendDesc.RenderTarget[1].BlendOpAlpha = D3D11_BLEND_OP_ADD;
1448 spuBlendDesc.RenderTarget[1].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
1449 hr = ID3D11Device_CreateBlendState(sys->d3ddevice, &spuBlendDesc, &pSpuBlendState);
1450 if (FAILED(hr)) {
1451 msg_Err(vd, "Could not create SPU blend state. (hr=0x%lX)", hr);
1452 return VLC_EGENERIC;
1454 ID3D11DeviceContext_OMSetBlendState(sys->d3dcontext, pSpuBlendState, NULL, 0xFFFFFFFF);
1455 ID3D11BlendState_Release(pSpuBlendState);
1457 /* disable depth testing as we're only doing 2D
1458 * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
1459 * see http://rastertek.com/dx11tut11.html
1461 D3D11_DEPTH_STENCIL_DESC stencilDesc;
1462 ZeroMemory(&stencilDesc, sizeof(stencilDesc));
1464 ID3D11DepthStencilState *pDepthStencilState;
1465 hr = ID3D11Device_CreateDepthStencilState(sys->d3ddevice, &stencilDesc, &pDepthStencilState );
1466 if (SUCCEEDED(hr)) {
1467 ID3D11DeviceContext_OMSetDepthStencilState(sys->d3dcontext, pDepthStencilState, 0);
1468 ID3D11DepthStencilState_Release(pDepthStencilState);
1471 ID3DBlob* pVSBlob = NULL;
1473 /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1474 hr = D3DCompile(globVertexShaderDefault, strlen(globVertexShaderDefault),
1475 NULL, NULL, NULL, "VS", "vs_4_0_level_9_1", 0, 0, &pVSBlob, NULL);
1477 if( FAILED(hr)) {
1478 msg_Err(vd, "The Vertex Shader is invalid. (hr=0x%lX)", hr);
1479 return VLC_EGENERIC;
1482 ID3D11VertexShader *d3dvertexShader;
1483 hr = ID3D11Device_CreateVertexShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
1484 ID3D10Blob_GetBufferSize(pVSBlob), NULL, &d3dvertexShader);
1486 if(FAILED(hr)) {
1487 ID3D11Device_Release(pVSBlob);
1488 msg_Err(vd, "Failed to create the vertex shader. (hr=0x%lX)", hr);
1489 return VLC_EGENERIC;
1491 ID3D11DeviceContext_VSSetShader(sys->d3dcontext, d3dvertexShader, NULL, 0);
1492 ID3D11VertexShader_Release(d3dvertexShader);
1494 D3D11_INPUT_ELEMENT_DESC layout[] = {
1495 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
1496 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
1497 { "OPACITY", 0, DXGI_FORMAT_R32_FLOAT, 0, 20, D3D11_INPUT_PER_VERTEX_DATA, 0},
1500 ID3D11InputLayout* pVertexLayout = NULL;
1501 hr = ID3D11Device_CreateInputLayout(sys->d3ddevice, layout, 3, (void *)ID3D10Blob_GetBufferPointer(pVSBlob),
1502 ID3D10Blob_GetBufferSize(pVSBlob), &pVertexLayout);
1504 ID3D10Blob_Release(pVSBlob);
1506 if(FAILED(hr)) {
1507 msg_Err(vd, "Failed to create the vertex input layout. (hr=0x%lX)", hr);
1508 return VLC_EGENERIC;
1511 ID3D11DeviceContext_IASetInputLayout(sys->d3dcontext, pVertexLayout);
1512 ID3D11SamplerState_Release(pVertexLayout);
1514 /* create the index of the vertices */
1515 WORD indices[] = {
1516 3, 1, 0,
1517 2, 1, 3,
1520 D3D11_BUFFER_DESC quadDesc = {
1521 .Usage = D3D11_USAGE_DEFAULT,
1522 .ByteWidth = sizeof(WORD) * 6,
1523 .BindFlags = D3D11_BIND_INDEX_BUFFER,
1524 .CPUAccessFlags = 0,
1527 D3D11_SUBRESOURCE_DATA quadIndicesInit = {
1528 .pSysMem = indices,
1531 ID3D11Buffer* pIndexBuffer = NULL;
1532 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &quadDesc, &quadIndicesInit, &pIndexBuffer);
1533 if(FAILED(hr)) {
1534 msg_Err(vd, "Could not Create the common quad indices. (hr=0x%lX)", hr);
1535 return VLC_EGENERIC;
1537 ID3D11DeviceContext_IASetIndexBuffer(sys->d3dcontext, pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
1538 ID3D11Buffer_Release(pIndexBuffer);
1540 ID3D11DeviceContext_IASetPrimitiveTopology(sys->d3dcontext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
1542 ID3DBlob* pPSBlob = NULL;
1544 /* TODO : Match the version to the D3D_FEATURE_LEVEL */
1545 hr = D3DCompile(sys->d3dPxShader, strlen(sys->d3dPxShader),
1546 NULL, NULL, NULL, "PS", "ps_4_0_level_9_1", 0, 0, &pPSBlob, NULL);
1549 if( FAILED(hr)) {
1550 msg_Err(vd, "The Pixel Shader is invalid. (hr=0x%lX)", hr );
1551 return VLC_EGENERIC;
1554 ID3D11PixelShader *pPicQuadShader;
1555 hr = ID3D11Device_CreatePixelShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pPSBlob),
1556 ID3D10Blob_GetBufferSize(pPSBlob), NULL, &pPicQuadShader);
1558 ID3D10Blob_Release(pPSBlob);
1560 if(FAILED(hr)) {
1561 msg_Err(vd, "Failed to create the pixel shader. (hr=0x%lX)", hr);
1562 return VLC_EGENERIC;
1565 if (sys->psz_rgbaPxShader != NULL)
1567 hr = D3DCompile(sys->psz_rgbaPxShader, strlen(sys->psz_rgbaPxShader),
1568 NULL, NULL, NULL, "PS", "ps_4_0_level_9_1", 0, 0, &pPSBlob, NULL);
1569 if( FAILED(hr)) {
1570 ID3D11PixelShader_Release(pPicQuadShader);
1571 msg_Err(vd, "The RGBA Pixel Shader is invalid. (hr=0x%lX)", hr );
1572 return VLC_EGENERIC;
1575 hr = ID3D11Device_CreatePixelShader(sys->d3ddevice, (void *)ID3D10Blob_GetBufferPointer(pPSBlob),
1576 ID3D10Blob_GetBufferSize(pPSBlob), NULL, &sys->pSPUPixelShader);
1578 ID3D10Blob_Release(pPSBlob);
1580 if(FAILED(hr)) {
1581 ID3D11PixelShader_Release(pPicQuadShader);
1582 msg_Err(vd, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr);
1583 return VLC_EGENERIC;
1587 if (AllocQuad( vd, fmt, &sys->picQuad, &sys->picQuadConfig, pPicQuadShader, true) != VLC_SUCCESS) {
1588 ID3D11PixelShader_Release(pPicQuadShader);
1589 msg_Err(vd, "Could not Create the main quad picture. (hr=0x%lX)", hr);
1590 return VLC_EGENERIC;
1592 ID3D11PixelShader_Release(pPicQuadShader);
1594 video_format_t core_source;
1595 CropStagingFormat( vd, &core_source );
1596 UpdatePicQuadPosition(vd);
1597 UncropStagingFormat( vd, &core_source );
1599 D3D11_SAMPLER_DESC sampDesc;
1600 memset(&sampDesc, 0, sizeof(sampDesc));
1601 sampDesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
1602 sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
1603 sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
1604 sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
1605 sampDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
1606 sampDesc.MinLOD = 0;
1607 sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
1609 ID3D11SamplerState *d3dsampState;
1610 hr = ID3D11Device_CreateSamplerState(sys->d3ddevice, &sampDesc, &d3dsampState);
1612 if (FAILED(hr)) {
1613 msg_Err(vd, "Could not Create the D3d11 Sampler State. (hr=0x%lX)", hr);
1614 return VLC_EGENERIC;
1616 ID3D11DeviceContext_PSSetSamplers(sys->d3dcontext, 0, 1, &d3dsampState);
1617 ID3D11SamplerState_Release(d3dsampState);
1619 if (Direct3D11CreatePool(vd, fmt))
1621 msg_Err(vd, "Direct3D picture pool initialization failed");
1622 return VLC_EGENERIC;
1625 msg_Dbg(vd, "Direct3D11 resources created");
1626 return VLC_SUCCESS;
1629 static int Direct3D11CreatePool(vout_display_t *vd, video_format_t *fmt)
1631 vout_display_sys_t *sys = vd->sys;
1633 if ( is_d3d11_opaque(fmt->i_chroma) )
1634 /* a D3D11VA pool will be created when needed */
1635 return VLC_SUCCESS;
1637 picture_sys_pool_t *picsys = calloc(1, sizeof(*picsys));
1638 if (unlikely(picsys == NULL)) {
1639 return VLC_ENOMEM;
1642 if ( sys->stagingQuad.pTexture != NULL )
1643 picsys->texture = sys->stagingQuad.pTexture;
1644 else
1645 picsys->texture = sys->picQuad.pTexture;
1646 picsys->vd = vd;
1648 picture_resource_t resource = {
1649 .p_sys = (picture_sys_t*) picsys,
1650 .pf_destroy = DestroyDisplayPicture,
1653 picture_t *picture = picture_NewFromResource(fmt, &resource);
1654 if (!picture) {
1655 free(picsys);
1656 return VLC_ENOMEM;
1658 ID3D11Texture2D_AddRef(picsys->texture);
1660 picture_pool_configuration_t pool_cfg;
1661 memset(&pool_cfg, 0, sizeof(pool_cfg));
1662 pool_cfg.picture_count = 1;
1663 pool_cfg.picture = &picture;
1664 pool_cfg.lock = Direct3D11MapTexture;
1665 //pool_cfg.unlock = Direct3D11UnmapTexture;
1667 sys->pool = picture_pool_NewExtended(&pool_cfg);
1668 if (!sys->pool) {
1669 picture_Release(picture);
1670 return VLC_ENOMEM;
1673 return VLC_SUCCESS;
1676 static void Direct3D11DestroyPool(vout_display_t *vd)
1678 vout_display_sys_t *sys = vd->sys;
1680 if (sys->pool)
1681 picture_pool_Release(sys->pool);
1682 sys->pool = NULL;
1685 static int AllocQuad(vout_display_t *vd, const video_format_t *fmt, d3d_quad_t *quad,
1686 d3d_quad_cfg_t *cfg, ID3D11PixelShader *d3dpixelShader, bool b_visible)
1688 vout_display_sys_t *sys = vd->sys;
1689 D3D11_MAPPED_SUBRESOURCE mappedResource;
1690 HRESULT hr;
1691 quad->vertexCount = 4;
1693 D3D11_BUFFER_DESC bd;
1694 memset(&bd, 0, sizeof(bd));
1695 bd.Usage = D3D11_USAGE_DYNAMIC;
1696 bd.ByteWidth = sizeof(d3d_vertex_t) * quad->vertexCount;
1697 bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
1698 bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1700 hr = ID3D11Device_CreateBuffer(sys->d3ddevice, &bd, NULL, &quad->pVertexBuffer);
1701 if(FAILED(hr)) {
1702 msg_Err(vd, "Failed to create vertex buffer. (hr=%lX)", hr);
1703 goto error;
1706 D3D11_TEXTURE2D_DESC texDesc;
1707 memset(&texDesc, 0, sizeof(texDesc));
1708 texDesc.Width = b_visible ? fmt->i_visible_width : fmt->i_width;
1709 texDesc.Height = b_visible ? fmt->i_visible_height : fmt->i_height;
1710 texDesc.MipLevels = texDesc.ArraySize = 1;
1711 texDesc.Format = cfg->textureFormat;
1712 texDesc.SampleDesc.Count = 1;
1713 texDesc.Usage = D3D11_USAGE_DYNAMIC;
1714 texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
1715 texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1716 texDesc.MiscFlags = 0;
1718 /* remove half pixels, we don't want green lines */
1719 const vlc_chroma_description_t *p_chroma_desc = vlc_fourcc_GetChromaDescription( fmt->i_chroma );
1720 for (unsigned plane = 0; plane < p_chroma_desc->plane_count; ++plane)
1722 unsigned i_extra;
1723 i_extra = (texDesc.Width * p_chroma_desc->p[plane].w.num) % p_chroma_desc->p[plane].w.den;
1724 if ( i_extra )
1725 texDesc.Width -= p_chroma_desc->p[plane].w.den / p_chroma_desc->p[plane].w.num - i_extra;
1726 i_extra = (texDesc.Height * p_chroma_desc->p[plane].h.num) % p_chroma_desc->p[plane].h.den;
1727 if ( i_extra )
1728 texDesc.Height -= p_chroma_desc->p[plane].h.den / p_chroma_desc->p[plane].h.num - i_extra;
1730 if (texDesc.Format == DXGI_FORMAT_NV12 || texDesc.Format == DXGI_FORMAT_P010)
1732 texDesc.Width &= ~1;
1733 texDesc.Height &= ~1;
1736 hr = ID3D11Device_CreateTexture2D(sys->d3ddevice, &texDesc, NULL, &quad->pTexture);
1737 if (FAILED(hr)) {
1738 msg_Err(vd, "Could not Create the D3d11 Texture. (hr=0x%lX)", hr);
1739 goto error;
1742 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
1743 if( FAILED(hr) ) {
1744 msg_Err(vd, "The texture cannot be mapped. (hr=0x%lX)", hr);
1745 goto error;
1747 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pTexture, 0);
1748 if (mappedResource.RowPitch < p_chroma_desc->pixel_size * texDesc.Width) {
1749 msg_Err( vd, "The texture row pitch is too small (%d instead of %d)", mappedResource.RowPitch,
1750 p_chroma_desc->pixel_size * texDesc.Width );
1751 goto error;
1754 D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc;
1755 memset(&resviewDesc, 0, sizeof(resviewDesc));
1756 resviewDesc.Format = cfg->resourceFormatYRGB;
1757 resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
1758 resviewDesc.Texture2D.MipLevels = texDesc.MipLevels;
1760 hr = ID3D11Device_CreateShaderResourceView(sys->d3ddevice, (ID3D11Resource *)quad->pTexture, &resviewDesc, &quad->d3dresViewY);
1761 if (FAILED(hr)) {
1762 msg_Err(vd, "Could not Create the Y/RGB D3d11 Texture ResourceView. (hr=0x%lX)", hr);
1763 goto error;
1766 if( cfg->resourceFormatUV )
1768 resviewDesc.Format = cfg->resourceFormatUV;
1769 hr = ID3D11Device_CreateShaderResourceView(sys->d3ddevice, (ID3D11Resource *)quad->pTexture, &resviewDesc, &quad->d3dresViewUV);
1770 if (FAILED(hr)) {
1771 msg_Err(vd, "Could not Create the UV D3d11 Texture ResourceView. (hr=0x%lX)", hr);
1772 goto error;
1776 if ( d3dpixelShader != NULL )
1778 quad->d3dpixelShader = d3dpixelShader;
1779 ID3D11PixelShader_AddRef(quad->d3dpixelShader);
1781 float right = 1.0f;
1782 float left = -1.0f;
1783 float top = 1.0f;
1784 float bottom = -1.0f;
1786 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
1787 if (SUCCEEDED(hr)) {
1788 d3d_vertex_t *dst_data = mappedResource.pData;
1790 // bottom left
1791 dst_data[0].position.x = left;
1792 dst_data[0].position.y = bottom;
1793 dst_data[0].position.z = 0.0f;
1794 dst_data[0].texture.x = 0.0f;
1795 dst_data[0].texture.y = 1.0f;
1796 dst_data[0].opacity = 1.0f;
1798 // bottom right
1799 dst_data[1].position.x = right;
1800 dst_data[1].position.y = bottom;
1801 dst_data[1].position.z = 0.0f;
1802 dst_data[1].texture.x = 1.0f;
1803 dst_data[1].texture.y = 1.0f;
1804 dst_data[1].opacity = 1.0f;
1806 // top right
1807 dst_data[2].position.x = right;
1808 dst_data[2].position.y = top;
1809 dst_data[2].position.z = 0.0f;
1810 dst_data[2].texture.x = 1.0f;
1811 dst_data[2].texture.y = 0.0f;
1812 dst_data[2].opacity = 1.0f;
1814 // top left
1815 dst_data[3].position.x = left;
1816 dst_data[3].position.y = top;
1817 dst_data[3].position.z = 0.0f;
1818 dst_data[3].texture.x = 0.0f;
1819 dst_data[3].texture.y = 0.0f;
1820 dst_data[3].opacity = 1.0f;
1822 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
1824 else {
1825 msg_Err(vd, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr);
1829 return VLC_SUCCESS;
1831 error:
1832 ReleaseQuad(quad);
1833 return VLC_EGENERIC;
1836 static void ReleaseQuad(d3d_quad_t *quad)
1838 if (quad->pVertexBuffer)
1840 ID3D11Buffer_Release(quad->pVertexBuffer);
1841 quad->pVertexBuffer = NULL;
1843 if (quad->pTexture)
1845 ID3D11Texture2D_Release(quad->pTexture);
1846 quad->pTexture = NULL;
1848 if (quad->d3dresViewY)
1850 ID3D11ShaderResourceView_Release(quad->d3dresViewY);
1851 quad->d3dresViewY = NULL;
1853 if (quad->d3dresViewUV)
1855 ID3D11ShaderResourceView_Release(quad->d3dresViewUV);
1856 quad->d3dresViewUV = NULL;
1858 if (quad->d3dpixelShader)
1860 ID3D11VertexShader_Release(quad->d3dpixelShader);
1861 quad->d3dpixelShader = NULL;
1865 static void Direct3D11DestroyResources(vout_display_t *vd)
1867 vout_display_sys_t *sys = vd->sys;
1869 Direct3D11DestroyPool(vd);
1871 if ( sys->stagingQuad.pTexture )
1872 ReleaseQuad(&sys->stagingQuad);
1873 ReleaseQuad(&sys->picQuad);
1874 Direct3D11DeleteRegions(sys->d3dregion_count, sys->d3dregions);
1875 sys->d3dregion_count = 0;
1877 if (sys->d3drenderTargetView)
1879 ID3D11RenderTargetView_Release(sys->d3drenderTargetView);
1880 sys->d3drenderTargetView = NULL;
1882 if (sys->d3ddepthStencilView)
1884 ID3D11DepthStencilView_Release(sys->d3ddepthStencilView);
1885 sys->d3ddepthStencilView = NULL;
1887 if (sys->pSPUPixelShader)
1889 ID3D11VertexShader_Release(sys->pSPUPixelShader);
1890 sys->pSPUPixelShader = NULL;
1892 #if defined(HAVE_ID3D11VIDEODECODER)
1893 if( sys->context_lock != INVALID_HANDLE_VALUE )
1895 CloseHandle( sys->context_lock );
1896 sys->context_lock = INVALID_HANDLE_VALUE;
1898 #endif
1900 msg_Dbg(vd, "Direct3D11 resources destroyed");
1903 static int Direct3D11MapTexture(picture_t *picture)
1905 picture_sys_pool_t *p_sys = (picture_sys_pool_t*) picture->p_sys;
1906 vout_display_t *vd = p_sys->vd;
1907 D3D11_MAPPED_SUBRESOURCE mappedResource;
1908 HRESULT hr;
1909 hr = ID3D11DeviceContext_Map(vd->sys->d3dcontext, (ID3D11Resource *)p_sys->texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
1910 if( FAILED(hr) )
1912 msg_Dbg( vd, "failed to map the texture (hr=0x%lX)", hr );
1913 return VLC_EGENERIC;
1915 return CommonUpdatePicture(picture, NULL, mappedResource.pData, mappedResource.RowPitch);
1918 static int Direct3D11UnmapTexture(picture_t *picture)
1920 picture_sys_pool_t *p_sys = (picture_sys_pool_t*)picture->p_sys;
1921 vout_display_t *vd = p_sys->vd;
1922 ID3D11DeviceContext_Unmap(vd->sys->d3dcontext, (ID3D11Resource *)p_sys->texture, 0);
1923 return VLC_SUCCESS;
1926 static void Direct3D11DeleteRegions(int count, picture_t **region)
1928 for (int i = 0; i < count; ++i) {
1929 if (region[i]) {
1930 picture_Release(region[i]);
1933 free(region);
1936 static void DestroyPictureQuad(picture_t *p_picture)
1938 ReleaseQuad( (d3d_quad_t *) p_picture->p_sys );
1939 free( p_picture );
1942 static void UpdateQuadOpacity(vout_display_t *vd, const d3d_quad_t *quad, float opacity)
1944 vout_display_sys_t *sys = vd->sys;
1945 D3D11_MAPPED_SUBRESOURCE mappedResource;
1947 HRESULT hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource);
1948 if (SUCCEEDED(hr)) {
1949 d3d_vertex_t *dst_data = mappedResource.pData;
1951 for (size_t i=0; i<quad->vertexCount; ++i)
1952 dst_data[i].opacity = opacity;
1954 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)quad->pVertexBuffer, 0);
1956 else {
1957 msg_Err(vd, "Failed to lock the subpicture vertex buffer (hr=0x%lX)", hr);
1961 static int Direct3D11MapSubpicture(vout_display_t *vd, int *subpicture_region_count,
1962 picture_t ***region, subpicture_t *subpicture)
1964 vout_display_sys_t *sys = vd->sys;
1965 D3D11_MAPPED_SUBRESOURCE mappedResource;
1966 D3D11_TEXTURE2D_DESC texDesc;
1967 HRESULT hr;
1968 int err;
1970 int count = 0;
1971 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1972 count++;
1974 *region = calloc(count, sizeof(picture_t *));
1975 if (unlikely(*region==NULL))
1976 return VLC_ENOMEM;
1977 *subpicture_region_count = count;
1979 int i = 0;
1980 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
1981 if (!r->fmt.i_width || !r->fmt.i_height)
1982 continue; // won't render anything, keep the cache for later
1984 for (int j = 0; j < sys->d3dregion_count; j++) {
1985 picture_t *cache = sys->d3dregions[j];
1986 if (cache != NULL && ((d3d_quad_t *) cache->p_sys)->pTexture) {
1987 ID3D11Texture2D_GetDesc( ((d3d_quad_t *) cache->p_sys)->pTexture, &texDesc );
1988 if (texDesc.Format == sys->d3dregion_format &&
1989 texDesc.Width == r->fmt.i_visible_width &&
1990 texDesc.Height == r->fmt.i_visible_height) {
1991 (*region)[i] = cache;
1992 memset(&sys->d3dregions[j], 0, sizeof(cache)); // do not reuse this cached value
1993 break;
1998 picture_t *quad_picture = (*region)[i];
1999 if (quad_picture == NULL) {
2000 d3d_quad_t *d3dquad = calloc(1, sizeof(*d3dquad));
2001 if (unlikely(d3dquad==NULL)) {
2002 continue;
2004 d3d_quad_cfg_t rgbaCfg = {
2005 .textureFormat = sys->d3dregion_format,
2006 .resourceFormatYRGB = sys->d3dregion_format,
2008 err = AllocQuad(vd, &r->fmt, d3dquad, &rgbaCfg, sys->pSPUPixelShader, false);
2009 if (err != VLC_SUCCESS) {
2010 msg_Err(vd, "Failed to create %dx%d texture for OSD",
2011 r->fmt.i_visible_width, r->fmt.i_visible_height);
2012 free(d3dquad);
2013 continue;
2015 picture_resource_t picres = {
2016 .p_sys = (picture_sys_t *) d3dquad,
2017 .pf_destroy = DestroyPictureQuad,
2019 (*region)[i] = picture_NewFromResource(&r->fmt, &picres);
2020 if ((*region)[i] == NULL) {
2021 msg_Err(vd, "Failed to create %dx%d picture for OSD",
2022 r->fmt.i_width, r->fmt.i_height);
2023 ReleaseQuad(d3dquad);
2024 continue;
2026 quad_picture = (*region)[i];
2029 hr = ID3D11DeviceContext_Map(sys->d3dcontext, (ID3D11Resource *)((d3d_quad_t *) quad_picture->p_sys)->pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
2030 if( SUCCEEDED(hr) ) {
2031 err = CommonUpdatePicture(quad_picture, NULL, mappedResource.pData, mappedResource.RowPitch);
2032 if (err != VLC_SUCCESS) {
2033 msg_Err(vd, "Failed to set the buffer on the SPU picture" );
2034 picture_Release(quad_picture);
2035 continue;
2038 picture_CopyPixels(quad_picture, r->p_picture);
2040 ID3D11DeviceContext_Unmap(sys->d3dcontext, (ID3D11Resource *)((d3d_quad_t *) quad_picture->p_sys)->pTexture, 0);
2041 } else {
2042 msg_Err(vd, "Failed to map the SPU texture (hr=0x%lX)", hr );
2043 picture_Release(quad_picture);
2044 continue;
2047 d3d_quad_t *quad = (d3d_quad_t *) quad_picture->p_sys;
2049 quad->cropViewport.Width = (FLOAT) r->fmt.i_visible_width * RECTWidth(sys->rect_dest) / subpicture->i_original_picture_width;
2050 quad->cropViewport.Height = (FLOAT) r->fmt.i_visible_height * RECTHeight(sys->rect_dest) / subpicture->i_original_picture_height;
2051 quad->cropViewport.MinDepth = 0.0f;
2052 quad->cropViewport.MaxDepth = 1.0f;
2053 quad->cropViewport.TopLeftX = sys->rect_dest.left + (FLOAT) r->i_x * RECTWidth(sys->rect_dest) / subpicture->i_original_picture_width;
2054 quad->cropViewport.TopLeftY = sys->rect_dest.top + (FLOAT) r->i_y * RECTHeight(sys->rect_dest) / subpicture->i_original_picture_height;
2056 UpdateQuadOpacity(vd, quad, r->i_alpha / 255.0f );
2058 return VLC_SUCCESS;