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 *****************************************************************************/
28 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0601 // _WIN32_WINNT_WIN7
30 # define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
37 #include <vlc/libvlc.h>
38 #include <vlc/libvlc_picture.h>
39 #include <vlc/libvlc_media.h>
40 #include <vlc/libvlc_renderer_discoverer.h>
41 #include <vlc/libvlc_media_player.h>
53 /* avoided until we can pass ISwapchainPanel without c++/cx mode
54 # include <windows.ui.xaml.media.dxinterop.h> */
56 #include "../../video_chroma/d3d11_fmt.h"
58 #include "d3d11_quad.h"
59 #include "d3d11_shaders.h"
60 #include "d3d11_swapchain.h"
63 #include "../../video_chroma/copy.h"
65 static int Open(vout_display_t
*, const vout_display_cfg_t
*,
66 video_format_t
*, vlc_video_context
*);
67 static void Close(vout_display_t
*);
69 #define D3D11_HELP N_("Recommended video output for Windows 8 and later versions")
70 #define HW_BLENDING_TEXT N_("Use hardware blending support")
71 #define HW_BLENDING_LONGTEXT N_(\
72 "Try to use hardware acceleration for subtitle/OSD blending.")
75 set_shortname("Direct3D11")
76 set_description(N_("Direct3D11 video output"))
78 set_category(CAT_VIDEO
)
79 set_subcategory(SUBCAT_VIDEO_VOUT
)
81 add_bool("direct3d11-hw-blending", true, HW_BLENDING_TEXT
, HW_BLENDING_LONGTEXT
, true)
84 add_integer("winrt-swapchain", 0x0, NULL
, NULL
, true) /* IDXGISwapChain1* */
87 add_shortcut("direct3d11")
88 set_callback_display(Open
, 300)
91 struct vout_display_sys_t
93 vout_display_sys_win32_t sys
; /* only use if sys.event is not NULL */
94 display_win32_area_t area
;
99 display_info_t display
;
101 d3d11_device_t
*d3d_dev
;
102 d3d11_decoder_device_t
*local_d3d_dev
; // when opened without a video context
103 d3d_shader_compiler_t shaders
;
104 d3d11_quad_t picQuad
;
106 ID3D11Asynchronous
*prepareWait
;
107 #ifdef HAVE_D3D11_4_H
108 ID3D11Fence
*d3dRenderFence
;
109 ID3D11DeviceContext4
*d3dcontext4
;
111 HANDLE renderFinished
;
114 picture_sys_d3d11_t stagingSys
;
115 plane_t stagingPlanes
[PICTURE_PLANE_MAX
];
117 d3d11_vertex_shader_t projectionVShader
;
118 d3d11_vertex_shader_t flatVShader
;
120 /* copy from the decoder pool into picSquad before display
121 * Uses a Texture2D with slices rather than a Texture2DArray for the decoder */
125 vlc_fourcc_t pSubpictureChromas
[2];
126 d3d11_quad_t regionQuad
;
128 picture_t
**d3dregions
;
130 /* outside rendering */
131 void *outside_opaque
;
132 libvlc_video_update_output_cb updateOutputCb
;
133 libvlc_video_swap_cb swapCb
;
134 libvlc_video_makeCurrent_cb startEndRenderingCb
;
135 libvlc_video_frameMetadata_cb sendMetadataCb
;
136 libvlc_video_output_select_plane_cb selectPlaneCb
;
139 static void Prepare(vout_display_t
*, picture_t
*, subpicture_t
*subpicture
, vlc_tick_t
);
140 static void Display(vout_display_t
*, picture_t
*);
142 static int Direct3D11Open (vout_display_t
*, video_format_t
*, vlc_video_context
*);
143 static void Direct3D11Close(vout_display_t
*);
145 static int SetupOutputFormat(vout_display_t
*, video_format_t
*, vlc_video_context
*);
146 static int Direct3D11CreateFormatResources (vout_display_t
*, const video_format_t
*);
147 static int Direct3D11CreateGenericResources(vout_display_t
*);
148 static void Direct3D11DestroyResources(vout_display_t
*);
150 static void Direct3D11DeleteRegions(int, picture_t
**);
151 static int Direct3D11MapSubpicture(vout_display_t
*, int *, picture_t
***, subpicture_t
*);
153 static int Control(vout_display_t
*, int);
156 static int UpdateDisplayFormat(vout_display_t
*vd
, const video_format_t
*fmt
)
158 vout_display_sys_t
*sys
= vd
->sys
;
159 libvlc_video_render_cfg_t cfg
;
161 cfg
.width
= vd
->cfg
->display
.width
;
162 cfg
.height
= vd
->cfg
->display
.height
;
164 switch (fmt
->i_chroma
)
166 case VLC_CODEC_D3D11_OPAQUE
:
169 case VLC_CODEC_D3D11_OPAQUE_RGBA
:
170 case VLC_CODEC_D3D11_OPAQUE_BGRA
:
173 case VLC_CODEC_D3D11_OPAQUE_10B
:
178 const vlc_chroma_description_t
*p_format
= vlc_fourcc_GetChromaDescription(fmt
->i_chroma
);
179 if (p_format
== NULL
)
185 cfg
.bitdepth
= p_format
->pixel_bits
== 0 ? 8 : p_format
->pixel_bits
/
186 (p_format
->plane_count
==1 ? p_format
->pixel_size
: 1);
191 cfg
.full_range
= fmt
->color_range
== COLOR_RANGE_FULL
||
192 /* the YUV->RGB conversion already output full range */
193 is_d3d11_opaque(fmt
->i_chroma
) ||
194 vlc_fourcc_IsYUV(fmt
->i_chroma
);
195 cfg
.primaries
= (libvlc_video_color_primaries_t
) fmt
->primaries
;
196 cfg
.colorspace
= (libvlc_video_color_space_t
) fmt
->space
;
197 cfg
.transfer
= (libvlc_video_transfer_func_t
) fmt
->transfer
;
199 libvlc_video_output_cfg_t out
;
200 if (!sys
->updateOutputCb( sys
->outside_opaque
, &cfg
, &out
))
202 msg_Err(vd
, "Failed to set format %dx%d %d bits on output", cfg
.width
, cfg
.height
, cfg
.bitdepth
);
206 display_info_t new_display
= { 0 };
208 for (const d3d_format_t
*output_format
= DxgiGetRenderFormatList();
209 output_format
->name
!= NULL
; ++output_format
)
211 if (output_format
->formatTexture
== (DXGI_FORMAT
)out
.dxgi_format
&&
212 !is_d3d11_opaque(output_format
->fourcc
))
214 new_display
.pixelFormat
= output_format
;
218 if (unlikely(new_display
.pixelFormat
== NULL
))
220 msg_Err(vd
, "Could not find the output format.");
224 new_display
.color
= (video_color_space_t
) out
.colorspace
;
225 new_display
.transfer
= (video_transfer_func_t
) out
.transfer
;
226 new_display
.primaries
= (video_color_primaries_t
) out
.primaries
;
227 new_display
.b_full_range
= out
.full_range
;
229 /* guestimate the display peak luminance */
230 switch (out
.transfer
)
232 case TRANSFER_FUNC_LINEAR
:
233 case TRANSFER_FUNC_SRGB
:
234 new_display
.luminance_peak
= DEFAULT_SRGB_BRIGHTNESS
;
236 case TRANSFER_FUNC_SMPTE_ST2084
:
237 new_display
.luminance_peak
= MAX_PQ_BRIGHTNESS
;
240 new_display
.luminance_peak
= DEFAULT_SRGB_BRIGHTNESS
;
244 if ( sys
->display
.pixelFormat
== NULL
||
245 ( sys
->display
.pixelFormat
!= new_display
.pixelFormat
||
246 sys
->display
.luminance_peak
!= new_display
.luminance_peak
||
247 sys
->display
.color
!= new_display
.color
||
248 sys
->display
.transfer
!= new_display
.transfer
||
249 sys
->display
.primaries
!= new_display
.primaries
||
250 sys
->display
.b_full_range
!= new_display
.b_full_range
))
252 sys
->display
= new_display
;
253 /* TODO release the pixel shaders if the format changed */
254 if (Direct3D11CreateFormatResources(vd
, fmt
)) {
255 msg_Err(vd
, "Failed to allocate format resources");
263 static void UpdateSize(vout_display_t
*vd
)
265 vout_display_sys_t
*sys
= vd
->sys
;
266 msg_Dbg(vd
, "Detected size change %dx%d", sys
->area
.place
.width
,
267 sys
->area
.place
.height
);
269 UpdateDisplayFormat(vd
, vd
->fmt
);
272 .left
= sys
->area
.place
.x
,
273 .right
= sys
->area
.place
.x
+ sys
->area
.place
.width
,
274 .top
= sys
->area
.place
.y
,
275 .bottom
= sys
->area
.place
.y
+ sys
->area
.place
.height
278 D3D11_UpdateViewport( &sys
->picQuad
, &rect_dst
, sys
->display
.pixelFormat
);
281 .left
= vd
->source
->i_x_offset
,
282 .right
= vd
->source
->i_x_offset
+ vd
->source
->i_visible_width
,
283 .top
= vd
->source
->i_y_offset
,
284 .bottom
= vd
->source
->i_y_offset
+ vd
->source
->i_visible_height
,
286 d3d11_device_lock( sys
->d3d_dev
);
288 D3D11_UpdateQuadPosition(vd
, sys
->d3d_dev
, &sys
->picQuad
, &source_rect
,
289 vd
->source
->orientation
);
291 D3D11_UpdateViewpoint( vd
, sys
->d3d_dev
, &sys
->picQuad
, &vd
->cfg
->viewpoint
,
292 (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
);
294 d3d11_device_unlock( sys
->d3d_dev
);
297 msg_Dbg( vd
, "picQuad position (%.02f,%.02f) %.02fx%.02f",
298 sys
->picQuad
.cropViewport
[0].TopLeftX
, sys
->picQuad
.cropViewport
[0].TopLeftY
,
299 sys
->picQuad
.cropViewport
[0].Width
, sys
->picQuad
.cropViewport
[0].Height
);
303 static int SetViewpoint(vout_display_t
*vd
, const vlc_viewpoint_t
*viewpoint
)
305 vout_display_sys_t
*sys
= vd
->sys
;
306 if ( sys
->picQuad
.viewpointShaderConstant
)
308 d3d11_device_lock( sys
->d3d_dev
);
309 D3D11_UpdateViewpoint( vd
, sys
->d3d_dev
, &sys
->picQuad
, viewpoint
,
310 (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
);
311 d3d11_device_unlock( sys
->d3d_dev
);
316 static int UpdateStaging(vout_display_t
*vd
, const video_format_t
*fmt
)
318 vout_display_sys_t
*sys
= vd
->sys
;
319 #ifdef HAVE_ID3D11VIDEODECODER
320 if (sys
->legacy_shader
)
322 /* we need a staging texture */
323 ID3D11Texture2D
*textures
[DXGI_MAX_SHADER_VIEW
] = {0};
324 video_format_t texture_fmt
= *vd
->source
;
325 texture_fmt
.i_width
= sys
->picQuad
.generic
.i_width
;
326 texture_fmt
.i_height
= sys
->picQuad
.generic
.i_height
;
327 if (!is_d3d11_opaque(fmt
->i_chroma
))
328 texture_fmt
.i_chroma
= sys
->picQuad
.generic
.textureFormat
->fourcc
;
330 if (AllocateTextures(vd
, sys
->d3d_dev
, sys
->picQuad
.generic
.textureFormat
, &texture_fmt
, textures
, sys
->stagingPlanes
))
332 msg_Err(vd
, "Failed to allocate the staging texture");
336 if (D3D11_AllocateResourceView(vd
, sys
->d3d_dev
->d3ddevice
, sys
->picQuad
.generic
.textureFormat
,
337 textures
, 0, sys
->stagingSys
.renderSrc
))
339 msg_Err(vd
, "Failed to allocate the staging shader view");
343 for (unsigned plane
= 0; plane
< DXGI_MAX_SHADER_VIEW
; plane
++)
344 sys
->stagingSys
.texture
[plane
] = textures
[plane
];
350 static const struct vlc_display_operations ops
= {
351 Close
, Prepare
, Display
, Control
, NULL
, SetViewpoint
,
354 static int Open(vout_display_t
*vd
, const vout_display_cfg_t
*cfg
,
355 video_format_t
*fmtp
, vlc_video_context
*context
)
357 vout_display_sys_t
*sys
= vd
->sys
= vlc_obj_calloc(VLC_OBJECT(vd
), 1, sizeof(vout_display_sys_t
));
361 int ret
= D3D_InitShaderCompiler(VLC_OBJECT(vd
), &sys
->shaders
);
362 if (ret
!= VLC_SUCCESS
)
365 CommonInit(&sys
->area
);
367 sys
->outside_opaque
= var_InheritAddress( vd
, "vout-cb-opaque" );
368 sys
->updateOutputCb
= var_InheritAddress( vd
, "vout-cb-update-output" );
369 sys
->swapCb
= var_InheritAddress( vd
, "vout-cb-swap" );
370 sys
->startEndRenderingCb
= var_InheritAddress( vd
, "vout-cb-make-current" );
371 sys
->sendMetadataCb
= var_InheritAddress( vd
, "vout-cb-metadata" );
372 sys
->selectPlaneCb
= var_InheritAddress( vd
, "vout-cb-select-plane" );
374 d3d11_decoder_device_t
*dev_sys
= GetD3D11OpaqueContext( context
);
375 if ( dev_sys
== NULL
)
377 // No d3d11 device, we create one
378 sys
->local_d3d_dev
= D3D11_CreateDevice(vd
, NULL
, false, vd
->obj
.force
);
379 if (sys
->local_d3d_dev
== NULL
) {
380 msg_Err(vd
, "Could not Create the D3D11 device.");
383 dev_sys
= sys
->local_d3d_dev
;
385 sys
->d3d_dev
= &dev_sys
->d3d_dev
;
387 if ( sys
->swapCb
== NULL
|| sys
->startEndRenderingCb
== NULL
|| sys
->updateOutputCb
== NULL
)
389 #if !VLC_WINSTORE_APP
390 if (cfg
->window
->type
== VOUT_WINDOW_TYPE_HWND
)
392 if (CommonWindowInit(vd
, &sys
->area
, &sys
->sys
,
393 vd
->source
->projection_mode
!= PROJECTION_MODE_RECTANGULAR
))
397 #endif /* !VLC_WINSTORE_APP */
399 /* use our internal swapchain callbacks */
401 if (cfg
->window
->type
== VOUT_WINDOW_TYPE_DCOMP
)
402 sys
->outside_opaque
= D3D11_CreateLocalSwapchainHandleDComp(VLC_OBJECT(vd
), cfg
->window
->display
.dcomp_device
, cfg
->window
->handle
.dcomp_visual
, sys
->d3d_dev
);
405 sys
->outside_opaque
= D3D11_CreateLocalSwapchainHandleHwnd(VLC_OBJECT(vd
), sys
->sys
.hvideownd
, sys
->d3d_dev
);
406 if (unlikely(sys
->outside_opaque
== NULL
))
408 sys
->updateOutputCb
= D3D11_LocalSwapchainUpdateOutput
;
409 sys
->swapCb
= D3D11_LocalSwapchainSwap
;
410 sys
->startEndRenderingCb
= D3D11_LocalSwapchainStartEndRendering
;
411 sys
->sendMetadataCb
= D3D11_LocalSwapchainSetMetadata
;
412 sys
->selectPlaneCb
= D3D11_LocalSwapchainSelectPlane
;
415 #if !VLC_WINSTORE_APP
416 if (vd
->source
->projection_mode
!= PROJECTION_MODE_RECTANGULAR
&& sys
->sys
.hvideownd
)
417 sys
->p_sensors
= HookWindowsSensors(vd
, sys
->sys
.hvideownd
);
418 #endif // !VLC_WINSTORE_APP
420 if (Direct3D11Open(vd
, fmtp
, context
)) {
421 msg_Err(vd
, "Direct3D11 could not be opened");
425 vout_window_SetTitle(cfg
->window
, VOUT_TITLE
" (Direct3D11 output)");
426 msg_Dbg(vd
, "Direct3D11 display adapter successfully initialized");
428 vd
->info
.can_scale_spu
= true;
430 if (var_InheritBool(vd
, "direct3d11-hw-blending") &&
431 sys
->regionQuad
.generic
.textureFormat
!= NULL
)
433 sys
->pSubpictureChromas
[0] = sys
->regionQuad
.generic
.textureFormat
->fourcc
;
434 sys
->pSubpictureChromas
[1] = 0;
435 vd
->info
.subpicture_chromas
= sys
->pSubpictureChromas
;
438 vd
->info
.subpicture_chromas
= NULL
;
442 msg_Dbg(vd
, "Direct3D11 Open Succeeded");
451 static void Close(vout_display_t
*vd
)
453 D3D_ReleaseShaderCompiler(&vd
->sys
->shaders
);
455 #if !VLC_WINSTORE_APP
456 UnhookWindowsSensors(vd
->sys
->p_sensors
);
457 CommonWindowClean(&vd
->sys
->sys
);
460 static int Control(vout_display_t
*vd
, int query
)
462 vout_display_sys_t
*sys
= vd
->sys
;
463 int res
= CommonControl( vd
, &sys
->area
, &sys
->sys
, query
);
465 if ( sys
->area
.place_changed
)
468 sys
->area
.place_changed
=false;
474 static bool SelectRenderPlane(void *opaque
, size_t plane
, ID3D11RenderTargetView
**targetView
)
476 vout_display_sys_t
*sys
= opaque
;
477 if (!sys
->selectPlaneCb
)
480 return plane
== 0; // we only support one packed RGBA plane by default
482 return sys
->selectPlaneCb(sys
->outside_opaque
, plane
, (void*)targetView
);
485 static void PreparePicture(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*subpicture
,
488 vout_display_sys_t
*sys
= vd
->sys
;
490 if (sys
->picQuad
.generic
.textureFormat
->formatTexture
== DXGI_FORMAT_UNKNOWN
)
492 D3D11_MAPPED_SUBRESOURCE mappedResource
;
496 bool b_mapped
= true;
497 for (i
= 0; i
< picture
->i_planes
; i
++) {
498 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
->d3dcontext
, sys
->stagingSys
.resource
[i
],
499 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
500 if( unlikely(FAILED(hr
)) )
503 ID3D11DeviceContext_Unmap(sys
->d3d_dev
->d3dcontext
, sys
->stagingSys
.resource
[i
], 0);
507 sys
->stagingPlanes
[i
].i_pitch
= mappedResource
.RowPitch
;
508 sys
->stagingPlanes
[i
].p_pixels
= mappedResource
.pData
;
513 for (i
= 0; i
< picture
->i_planes
; i
++)
514 plane_CopyPixels(&sys
->stagingPlanes
[i
], &picture
->p
[i
]);
516 for (i
= 0; i
< picture
->i_planes
; i
++)
517 ID3D11DeviceContext_Unmap(sys
->d3d_dev
->d3dcontext
, sys
->stagingSys
.resource
[i
], 0);
520 else if (!is_d3d11_opaque(picture
->format
.i_chroma
))
522 D3D11_MAPPED_SUBRESOURCE mappedResource
;
525 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
->d3dcontext
, sys
->stagingSys
.resource
[0],
526 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
527 if( unlikely(FAILED(hr
)) )
528 msg_Err(vd
, "Failed to map the %4.4s staging picture. (hr=0x%lX)", (const char*)&picture
->format
.i_chroma
, hr
);
531 uint8_t *buf
= mappedResource
.pData
;
532 for (int i
= 0; i
< picture
->i_planes
; i
++)
534 sys
->stagingPlanes
[i
].i_pitch
= mappedResource
.RowPitch
;
535 sys
->stagingPlanes
[i
].p_pixels
= buf
;
537 plane_CopyPixels(&sys
->stagingPlanes
[i
], &picture
->p
[i
]);
539 buf
+= sys
->stagingPlanes
[i
].i_pitch
* sys
->stagingPlanes
[i
].i_lines
;
542 ID3D11DeviceContext_Unmap(sys
->d3d_dev
->d3dcontext
, sys
->stagingSys
.resource
[0], 0);
547 picture_sys_d3d11_t
*p_sys
= ActiveD3D11PictureSys(picture
);
549 if (sys
->legacy_shader
) {
550 D3D11_TEXTURE2D_DESC srcDesc
,texDesc
;
551 ID3D11Texture2D_GetDesc(p_sys
->texture
[KNOWN_DXGI_INDEX
], &srcDesc
);
552 ID3D11Texture2D_GetDesc(sys
->stagingSys
.texture
[0], &texDesc
);
555 .bottom
= __MIN(srcDesc
.Height
, texDesc
.Height
),
557 .right
= __MIN(srcDesc
.Width
, texDesc
.Width
),
560 ID3D11DeviceContext_CopySubresourceRegion(sys
->d3d_dev
->d3dcontext
,
561 sys
->stagingSys
.resource
[KNOWN_DXGI_INDEX
],
563 p_sys
->resource
[KNOWN_DXGI_INDEX
],
564 p_sys
->slice_index
, &box
);
568 D3D11_TEXTURE2D_DESC texDesc
;
569 ID3D11Texture2D_GetDesc(p_sys
->texture
[0], &texDesc
);
570 if (texDesc
.BindFlags
& D3D11_BIND_SHADER_RESOURCE
)
572 /* for performance reason we don't want to allocate this during
573 * display, do it preferrably when creating the texture */
574 assert(p_sys
->renderSrc
[0]!=NULL
);
576 if ( sys
->picQuad
.generic
.i_height
!= texDesc
.Height
||
577 sys
->picQuad
.generic
.i_width
!= texDesc
.Width
)
579 /* the decoder produced different sizes than the vout, we need to
580 * adjust the vertex */
581 sys
->picQuad
.generic
.i_height
= texDesc
.Height
;
582 sys
->picQuad
.generic
.i_width
= texDesc
.Width
;
584 CommonPlacePicture(vd
, &sys
->area
);
591 int subpicture_region_count
= 0;
592 picture_t
**subpicture_regions
= NULL
;
593 Direct3D11MapSubpicture(vd
, &subpicture_region_count
, &subpicture_regions
, subpicture
);
594 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
595 sys
->d3dregion_count
= subpicture_region_count
;
596 sys
->d3dregions
= subpicture_regions
;
599 if (picture
->format
.mastering
.max_luminance
)
601 D3D11_UpdateQuadLuminanceScale(vd
, sys
->d3d_dev
, &sys
->picQuad
, (float)sys
->display
.luminance_peak
/ D3D_GetFormatLuminance(VLC_OBJECT(vd
), &picture
->format
));
604 /* Render the quad */
605 ID3D11ShaderResourceView
**renderSrc
;
606 if (sys
->legacy_shader
)
607 renderSrc
= sys
->stagingSys
.renderSrc
;
609 picture_sys_d3d11_t
*p_sys
= ActiveD3D11PictureSys(picture
);
610 renderSrc
= p_sys
->renderSrc
;
612 D3D11_RenderQuad(sys
->d3d_dev
, &sys
->picQuad
,
613 vd
->source
->projection_mode
== PROJECTION_MODE_RECTANGULAR
? &sys
->flatVShader
: &sys
->projectionVShader
,
614 renderSrc
, SelectRenderPlane
, sys
);
617 // draw the additional vertices
618 for (int i
= 0; i
< sys
->d3dregion_count
; ++i
) {
619 if (sys
->d3dregions
[i
])
621 d3d11_quad_t
*quad
= (d3d11_quad_t
*) sys
->d3dregions
[i
]->p_sys
;
622 D3D11_RenderQuad(sys
->d3d_dev
, quad
, &sys
->flatVShader
,
623 quad
->picSys
.renderSrc
, SelectRenderPlane
, sys
);
628 #ifdef HAVE_D3D11_4_H
629 if (sys
->d3dcontext4
)
631 if (sys
->renderFence
== UINT64_MAX
)
632 sys
->renderFence
= 0;
636 ResetEvent(sys
->renderFinished
);
637 ID3D11Fence_SetEventOnCompletion(sys
->d3dRenderFence
, sys
->renderFence
, sys
->renderFinished
);
638 ID3D11DeviceContext4_Signal(sys
->d3dcontext4
, sys
->d3dRenderFence
, sys
->renderFence
);
640 WaitForSingleObject(sys
->renderFinished
, INFINITE
);
644 if (sys
->prepareWait
)
646 ID3D11DeviceContext_End(sys
->d3d_dev
->d3dcontext
, sys
->prepareWait
);
648 while (S_FALSE
== ID3D11DeviceContext_GetData(sys
->d3d_dev
->d3dcontext
,
649 sys
->prepareWait
, NULL
, 0, 0))
651 vlc_tick_t sleep_duration
= (date
- vlc_tick_now()) / 4;
652 if (sleep_duration
<= VLC_TICK_FROM_MS(2))
654 // don't wait any longer, the display will likely be late
655 // we'll finish waiting during the Display call
658 // wait a little until the rendering is done
659 SleepEx(MS_FROM_VLC_TICK(sleep_duration
), TRUE
);
664 static void Prepare(vout_display_t
*vd
, picture_t
*picture
,
665 subpicture_t
*subpicture
, vlc_tick_t date
)
667 vout_display_sys_t
*sys
= vd
->sys
;
669 d3d11_device_lock( sys
->d3d_dev
);
671 if ( sys
->swapCb
== D3D11_LocalSwapchainSwap
)
673 /* legacy UWP mode, the width/height was set in GUID_SWAPCHAIN_WIDTH/HEIGHT */
676 if (D3D11_LocalSwapchainWinstoreSize( sys
->outside_opaque
, &i_width
, &i_height
))
678 if (i_width
!= vd
->cfg
->display
.width
|| i_height
!= vd
->cfg
->display
.height
)
679 vout_display_SetSize(vd
, i_width
, i_height
);
683 if ( sys
->startEndRenderingCb( sys
->outside_opaque
, true ))
685 if ( sys
->sendMetadataCb
&& picture
->format
.mastering
.max_luminance
)
687 libvlc_video_frame_hdr10_metadata_t hdr10
;
688 hdr10
.GreenPrimary
[0] = picture
->format
.mastering
.primaries
[0];
689 hdr10
.GreenPrimary
[1] = picture
->format
.mastering
.primaries
[1];
690 hdr10
.BluePrimary
[0] = picture
->format
.mastering
.primaries
[2];
691 hdr10
.BluePrimary
[1] = picture
->format
.mastering
.primaries
[3];
692 hdr10
.RedPrimary
[0] = picture
->format
.mastering
.primaries
[4];
693 hdr10
.RedPrimary
[1] = picture
->format
.mastering
.primaries
[5];
694 hdr10
.WhitePoint
[0] = picture
->format
.mastering
.white_point
[0];
695 hdr10
.WhitePoint
[1] = picture
->format
.mastering
.white_point
[1];
696 hdr10
.MinMasteringLuminance
= picture
->format
.mastering
.min_luminance
;
697 hdr10
.MaxMasteringLuminance
= picture
->format
.mastering
.max_luminance
;
698 hdr10
.MaxContentLightLevel
= picture
->format
.lighting
.MaxCLL
;
699 hdr10
.MaxFrameAverageLightLevel
= picture
->format
.lighting
.MaxFALL
;
701 sys
->sendMetadataCb( sys
->outside_opaque
, libvlc_video_metadata_frame_hdr10
, &hdr10
);
704 PreparePicture(vd
, picture
, subpicture
, date
);
706 sys
->startEndRenderingCb( sys
->outside_opaque
, false );
708 d3d11_device_unlock( sys
->d3d_dev
);
711 static void Display(vout_display_t
*vd
, picture_t
*picture
)
713 vout_display_sys_t
*sys
= vd
->sys
;
716 d3d11_device_lock( sys
->d3d_dev
);
717 sys
->swapCb(sys
->outside_opaque
);
719 if (sys
->prepareWait
)
721 vlc_tick_t start
= 0;
722 while (S_FALSE
== ID3D11DeviceContext_GetData(sys
->d3d_dev
->d3dcontext
,
723 sys
->prepareWait
, NULL
, 0, 0))
726 start
= vlc_tick_now();
727 // keep waiting until all rendering (plus extra) is known to be finished
728 // to let the vout thread things are not as smooth as it may think
731 if (start
!= 0 && var_InheritInteger(vd
, "verbose") >= 4)
732 msg_Dbg(vd
, "rendering wasn't finished, waited extra %lld ms", MS_FROM_VLC_TICK(vlc_tick_now() - start
));
735 d3d11_device_unlock( sys
->d3d_dev
);
738 static const d3d_format_t
*GetDirectRenderingFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
740 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
741 if (is_d3d11_opaque(i_src_chroma
))
742 supportFlags
|= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
743 return FindD3D11Format( vd
, vd
->sys
->d3d_dev
, i_src_chroma
, DXGI_RGB_FORMAT
|DXGI_YUV_FORMAT
, 0, 0, 0,
744 is_d3d11_opaque(i_src_chroma
) ? DXGI_CHROMA_GPU
: DXGI_CHROMA_CPU
, supportFlags
);
747 static const d3d_format_t
*GetDirectDecoderFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
749 UINT supportFlags
= D3D11_FORMAT_SUPPORT_DECODER_OUTPUT
;
750 return FindD3D11Format( vd
, vd
->sys
->d3d_dev
, i_src_chroma
, DXGI_RGB_FORMAT
|DXGI_YUV_FORMAT
, 0, 0, 0,
751 DXGI_CHROMA_GPU
, supportFlags
);
754 static const d3d_format_t
*GetDisplayFormatByDepth(vout_display_t
*vd
, uint8_t bit_depth
,
755 uint8_t widthDenominator
,
756 uint8_t heightDenominator
,
760 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
;
762 supportFlags
|= D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT
;
763 return FindD3D11Format( vd
, vd
->sys
->d3d_dev
, 0, rgb_yuv
,
764 bit_depth
, widthDenominator
+1, heightDenominator
+1,
765 DXGI_CHROMA_CPU
, supportFlags
);
768 static const d3d_format_t
*GetBlendableFormat(vout_display_t
*vd
, vlc_fourcc_t i_src_chroma
)
770 UINT supportFlags
= D3D11_FORMAT_SUPPORT_SHADER_LOAD
| D3D11_FORMAT_SUPPORT_BLENDABLE
;
771 return FindD3D11Format( vd
, vd
->sys
->d3d_dev
, i_src_chroma
, DXGI_RGB_FORMAT
|DXGI_YUV_FORMAT
, 0, 0, 0, DXGI_CHROMA_CPU
, supportFlags
);
774 static int Direct3D11Open(vout_display_t
*vd
, video_format_t
*fmtp
, vlc_video_context
*vctx
)
776 vout_display_sys_t
*sys
= vd
->sys
;
778 video_format_Copy(&fmt
, vd
->source
);
779 int err
= SetupOutputFormat(vd
, &fmt
, vctx
);
780 if (err
!= VLC_SUCCESS
)
782 if (!is_d3d11_opaque(vd
->source
->i_chroma
)
783 #if !VLC_WINSTORE_APP
788 const vlc_fourcc_t
*list
= vlc_fourcc_IsYUV(vd
->source
->i_chroma
) ?
789 vlc_fourcc_GetYUVFallback(vd
->source
->i_chroma
) :
790 vlc_fourcc_GetRGBFallback(vd
->source
->i_chroma
);
791 for (unsigned i
= 0; list
[i
] != 0; i
++) {
792 fmt
.i_chroma
= list
[i
];
793 if (fmt
.i_chroma
== vd
->source
->i_chroma
)
795 err
= SetupOutputFormat(vd
, &fmt
, NULL
);
796 if (err
== VLC_SUCCESS
)
800 if (err
!= VLC_SUCCESS
)
802 if ( sys
->swapCb
== D3D11_LocalSwapchainSwap
)
803 D3D11_LocalSwapchainCleanupDevice( sys
->outside_opaque
);
808 /* adjust the decoder sizes to have proper padding */
809 sys
->picQuad
.generic
.i_width
= fmt
.i_width
;
810 sys
->picQuad
.generic
.i_height
= fmt
.i_height
;
811 if ( sys
->picQuad
.generic
.textureFormat
->formatTexture
!= DXGI_FORMAT_R8G8B8A8_UNORM
&&
812 sys
->picQuad
.generic
.textureFormat
->formatTexture
!= DXGI_FORMAT_B5G6R5_UNORM
)
814 sys
->picQuad
.generic
.i_width
= (sys
->picQuad
.generic
.i_width
+ 0x01) & ~0x01;
815 sys
->picQuad
.generic
.i_height
= (sys
->picQuad
.generic
.i_height
+ 0x01) & ~0x01;
818 CommonPlacePicture(vd
, &sys
->area
);
820 err
= UpdateDisplayFormat(vd
, &fmt
);
821 if (err
!= VLC_SUCCESS
) {
822 msg_Err(vd
, "Could not update the backbuffer");
826 if (Direct3D11CreateGenericResources(vd
)) {
827 msg_Err(vd
, "Failed to allocate resources");
828 if ( sys
->swapCb
== D3D11_LocalSwapchainSwap
)
829 D3D11_LocalSwapchainCleanupDevice( sys
->outside_opaque
);
833 video_format_Clean(fmtp
);
839 static int SetupOutputFormat(vout_display_t
*vd
, video_format_t
*fmt
, vlc_video_context
*vctx
)
841 vout_display_sys_t
*sys
= vd
->sys
;
843 d3d11_video_context_t
*vtcx_sys
= GetD3D11ContextPrivate(vctx
);
844 if (vtcx_sys
!= NULL
&&
845 D3D11_DeviceSupportsFormat( sys
->d3d_dev
, vtcx_sys
->format
, D3D11_FORMAT_SUPPORT_SHADER_LOAD
))
847 for (const d3d_format_t
*output_format
= DxgiGetRenderFormatList();
848 output_format
->name
!= NULL
; ++output_format
)
850 if (output_format
->formatTexture
== vtcx_sys
->format
&&
851 is_d3d11_opaque(output_format
->fourcc
))
853 sys
->picQuad
.generic
.textureFormat
= output_format
;
859 // look for the requested pixel format first
860 if ( !sys
->picQuad
.generic
.textureFormat
)
861 sys
->picQuad
.generic
.textureFormat
= GetDirectRenderingFormat(vd
, fmt
->i_chroma
);
863 // look for any pixel format that we can handle with enough pixels per channel
864 const d3d_format_t
*decoder_format
= NULL
;
865 if ( !sys
->picQuad
.generic
.textureFormat
)
867 uint8_t bits_per_channel
;
868 uint8_t widthDenominator
, heightDenominator
;
869 switch (fmt
->i_chroma
)
871 case VLC_CODEC_D3D11_OPAQUE
:
872 case VLC_CODEC_NVDEC_OPAQUE
:
873 bits_per_channel
= 8;
874 widthDenominator
= heightDenominator
= 2;
876 case VLC_CODEC_D3D11_OPAQUE_RGBA
:
877 case VLC_CODEC_D3D11_OPAQUE_BGRA
:
878 bits_per_channel
= 8;
879 widthDenominator
= heightDenominator
= 1;
881 case VLC_CODEC_D3D11_OPAQUE_10B
:
882 case VLC_CODEC_NVDEC_OPAQUE_10B
:
883 bits_per_channel
= 10;
884 widthDenominator
= heightDenominator
= 2;
886 case VLC_CODEC_NVDEC_OPAQUE_16B
:
887 bits_per_channel
= 16;
888 widthDenominator
= heightDenominator
= 2;
890 case VLC_CODEC_NVDEC_OPAQUE_444
:
891 bits_per_channel
= 8;
892 widthDenominator
= heightDenominator
= 1;
894 case VLC_CODEC_NVDEC_OPAQUE_444_16B
:
895 bits_per_channel
= 16;
896 widthDenominator
= heightDenominator
= 1;
900 const vlc_chroma_description_t
*p_format
= vlc_fourcc_GetChromaDescription(fmt
->i_chroma
);
901 if (p_format
== NULL
)
903 bits_per_channel
= 8;
904 widthDenominator
= heightDenominator
= 2;
908 bits_per_channel
= p_format
->pixel_bits
== 0 ? 8 : p_format
->pixel_bits
/
909 (p_format
->plane_count
==1 ? p_format
->pixel_size
: 1);
910 widthDenominator
= heightDenominator
= 1;
911 for (size_t i
=0; i
<p_format
->plane_count
; i
++)
913 if (widthDenominator
< p_format
->p
[i
].w
.den
)
914 widthDenominator
= p_format
->p
[i
].w
.den
;
915 if (heightDenominator
< p_format
->p
[i
].h
.den
)
916 heightDenominator
= p_format
->p
[1].h
.den
;
923 /* look for a decoder format that can be decoded but not used in shaders */
924 if ( is_d3d11_opaque(fmt
->i_chroma
) )
925 decoder_format
= GetDirectDecoderFormat(vd
, fmt
->i_chroma
);
927 decoder_format
= sys
->picQuad
.generic
.textureFormat
;
929 bool is_rgb
= !vlc_fourcc_IsYUV(fmt
->i_chroma
);
930 sys
->picQuad
.generic
.textureFormat
= GetDisplayFormatByDepth(vd
, bits_per_channel
,
931 widthDenominator
, heightDenominator
,
932 decoder_format
!=NULL
,
933 is_rgb
? DXGI_RGB_FORMAT
: DXGI_YUV_FORMAT
);
934 if (!sys
->picQuad
.generic
.textureFormat
)
935 sys
->picQuad
.generic
.textureFormat
= GetDisplayFormatByDepth(vd
, bits_per_channel
,
936 widthDenominator
, heightDenominator
,
937 decoder_format
!=NULL
,
938 is_rgb
? DXGI_YUV_FORMAT
: DXGI_RGB_FORMAT
);
941 // look for any pixel format that we can handle
942 if ( !sys
->picQuad
.generic
.textureFormat
)
943 sys
->picQuad
.generic
.textureFormat
= GetDisplayFormatByDepth(vd
, 0, 0, 0, false, false);
945 if ( !sys
->picQuad
.generic
.textureFormat
)
947 msg_Err(vd
, "Could not get a suitable texture pixel format");
951 msg_Dbg( vd
, "Using pixel format %s for chroma %4.4s", sys
->picQuad
.generic
.textureFormat
->name
,
952 (char *)&fmt
->i_chroma
);
954 fmt
->i_chroma
= decoder_format
? decoder_format
->fourcc
: sys
->picQuad
.generic
.textureFormat
->fourcc
;
955 DxgiFormatMask( sys
->picQuad
.generic
.textureFormat
->formatTexture
, fmt
);
957 /* check the region pixel format */
958 sys
->regionQuad
.generic
.textureFormat
= GetBlendableFormat(vd
, VLC_CODEC_RGBA
);
959 if (!sys
->regionQuad
.generic
.textureFormat
)
960 sys
->regionQuad
.generic
.textureFormat
= GetBlendableFormat(vd
, VLC_CODEC_BGRA
);
965 static void Direct3D11Close(vout_display_t
*vd
)
967 vout_display_sys_t
*sys
= vd
->sys
;
969 Direct3D11DestroyResources(vd
);
971 if ( sys
->swapCb
== D3D11_LocalSwapchainSwap
)
972 D3D11_LocalSwapchainCleanupDevice( sys
->outside_opaque
);
974 if (sys
->d3d_dev
&& sys
->d3d_dev
== &sys
->local_d3d_dev
->d3d_dev
)
975 D3D11_ReleaseDevice( sys
->local_d3d_dev
);
977 msg_Dbg(vd
, "Direct3D11 display adapter closed");
980 static bool CanUseTextureArray(vout_display_t
*vd
)
982 #ifndef HAVE_ID3D11VIDEODECODER
986 // 15.200.1062.1004 is wrong - 2015/08/03 - 15.7.1 WHQL
987 // 21.19.144.1281 is wrong -
988 // 22.19.165.3 is good - 2017/05/04 - ReLive Edition 17.5.1
989 struct wddm_version WDDM_os
= {
990 .wddm
= 21, // starting with drivers designed for W10 Anniversary Update
992 if (D3D11CheckDriverVersion(vd
->sys
->d3d_dev
, GPU_MANUFACTURER_AMD
, &WDDM_os
) != VLC_SUCCESS
)
994 msg_Dbg(vd
, "AMD driver too old, fallback to legacy shader mode");
998 // xx.xx.1000.xxx drivers can't happen here for WDDM > 2.0
999 struct wddm_version WDDM_build
= {
1002 if (D3D11CheckDriverVersion(vd
->sys
->d3d_dev
, GPU_MANUFACTURER_AMD
, &WDDM_build
) != VLC_SUCCESS
)
1004 msg_Dbg(vd
, "Bogus AMD driver detected, fallback to legacy shader mode");
1012 static bool BogusZeroCopy(const vout_display_t
*vd
)
1014 if (vd
->sys
->d3d_dev
->adapterDesc
.VendorId
!= GPU_MANUFACTURER_AMD
)
1017 switch (vd
->sys
->d3d_dev
->adapterDesc
.DeviceId
)
1019 case 0x687F: // RX Vega 56/64
1020 case 0x6863: // RX Vega Frontier Edition
1021 case 0x15DD: // RX Vega 8/11 (Ryzen iGPU)
1023 struct wddm_version WDDM
= {
1024 .revision
= 14011, // 18.10.2 - 2018/06/11
1026 return D3D11CheckDriverVersion(vd
->sys
->d3d_dev
, GPU_MANUFACTURER_AMD
, &WDDM
) != VLC_SUCCESS
;
1033 /* TODO : handle errors better
1034 TODO : seperate out into smaller functions like createshaders */
1035 static int Direct3D11CreateFormatResources(vout_display_t
*vd
, const video_format_t
*fmt
)
1037 vout_display_sys_t
*sys
= vd
->sys
;
1040 sys
->legacy_shader
= sys
->d3d_dev
->feature_level
< D3D_FEATURE_LEVEL_10_0
|| !CanUseTextureArray(vd
) ||
1041 BogusZeroCopy(vd
) || !is_d3d11_opaque(fmt
->i_chroma
);
1043 d3d_shader_blob pPSBlob
[DXGI_MAX_RENDER_TARGET
] = { 0 };
1044 hr
= D3D11_CompilePixelShaderBlob(vd
, &sys
->shaders
, sys
->d3d_dev
,
1045 &sys
->display
, fmt
->transfer
,
1046 fmt
->color_range
== COLOR_RANGE_FULL
,
1047 &sys
->picQuad
, pPSBlob
);
1050 msg_Err(vd
, "Failed to compile the pixel shader. (hr=0x%lX)", hr
);
1051 return VLC_EGENERIC
;
1053 hr
= D3D11_SetQuadPixelShader(VLC_OBJECT(vd
), sys
->d3d_dev
, false,
1054 &sys
->picQuad
, pPSBlob
);
1057 msg_Err(vd
, "Failed to set the pixel shader. (hr=0x%lX)", hr
);
1058 return VLC_EGENERIC
;
1061 if (D3D11_AllocateQuad(vd
, sys
->d3d_dev
, vd
->source
->projection_mode
, &sys
->picQuad
) != VLC_SUCCESS
)
1063 msg_Err(vd
, "Could not allocate quad buffers.");
1064 return VLC_EGENERIC
;
1067 if (D3D11_SetupQuad( vd
, sys
->d3d_dev
, vd
->source
, &sys
->picQuad
, &sys
->display
) != VLC_SUCCESS
)
1069 msg_Err(vd
, "Could not Create the main quad picture.");
1070 return VLC_EGENERIC
;
1073 RECT source_rect
= {
1074 .left
= fmt
->i_x_offset
,
1075 .right
= fmt
->i_x_offset
+ fmt
->i_visible_width
,
1076 .top
= fmt
->i_y_offset
,
1077 .bottom
= fmt
->i_y_offset
+ fmt
->i_visible_height
,
1079 if (!D3D11_UpdateQuadPosition(vd
, sys
->d3d_dev
, &sys
->picQuad
, &source_rect
, vd
->source
->orientation
))
1081 msg_Err(vd
, "Could not set quad picture position.");
1082 return VLC_EGENERIC
;
1085 if ( vd
->source
->projection_mode
== PROJECTION_MODE_EQUIRECTANGULAR
||
1086 vd
->source
->projection_mode
== PROJECTION_MODE_CUBEMAP_LAYOUT_STANDARD
)
1087 D3D11_UpdateViewpoint( vd
, sys
->d3d_dev
, &sys
->picQuad
, &vd
->cfg
->viewpoint
,
1088 (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
);
1090 if (is_d3d11_opaque(fmt
->i_chroma
)) {
1091 ID3D10Multithread
*pMultithread
;
1092 hr
= ID3D11Device_QueryInterface( sys
->d3d_dev
->d3ddevice
, &IID_ID3D10Multithread
, (void **)&pMultithread
);
1093 if (SUCCEEDED(hr
)) {
1094 ID3D10Multithread_SetMultithreadProtected(pMultithread
, TRUE
);
1095 ID3D10Multithread_Release(pMultithread
);
1099 return UpdateStaging(vd
, fmt
);
1102 #ifdef HAVE_D3D11_4_H
1103 static HRESULT
InitRenderFence(vout_display_sys_t
*sys
)
1106 sys
->renderFinished
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
1107 if (unlikely(sys
->renderFinished
== NULL
))
1109 ID3D11Device5
*d3ddev5
= NULL
;
1110 hr
= ID3D11DeviceContext_QueryInterface(sys
->d3d_dev
->d3dcontext
, &IID_ID3D11DeviceContext4
, (void**)&sys
->d3dcontext4
);
1113 hr
= ID3D11Device_QueryInterface(sys
->d3d_dev
->d3ddevice
, &IID_ID3D11Device5
, (void**)&d3ddev5
);
1116 hr
= ID3D11Device5_CreateFence(d3ddev5
, sys
->renderFence
, D3D11_FENCE_FLAG_NONE
, &IID_ID3D11Fence
, (void**)&sys
->d3dRenderFence
);
1119 ID3D11Device5_Release(d3ddev5
);
1123 ID3D11Device5_Release(d3ddev5
);
1124 if (sys
->d3dRenderFence
)
1126 ID3D11Fence_Release(sys
->d3dRenderFence
);
1127 sys
->d3dRenderFence
= NULL
;
1129 ID3D11DeviceContext4_Release(sys
->d3dcontext4
);
1130 sys
->d3dcontext4
= NULL
;
1131 CloseHandle(sys
->renderFinished
);
1134 #endif // HAVE_D3D11_4_H
1136 static int Direct3D11CreateGenericResources(vout_display_t
*vd
)
1138 vout_display_sys_t
*sys
= vd
->sys
;
1141 #ifdef HAVE_D3D11_4_H
1142 hr
= InitRenderFence(sys
);
1145 msg_Dbg(vd
, "using GPU render fence");
1150 D3D11_QUERY_DESC query
= { 0 };
1151 query
.Query
= D3D11_QUERY_EVENT
;
1152 ID3D11Device_CreateQuery(sys
->d3d_dev
->d3ddevice
, &query
, (ID3D11Query
**)&sys
->prepareWait
);
1155 ID3D11BlendState
*pSpuBlendState
;
1156 D3D11_BLEND_DESC spuBlendDesc
= { 0 };
1157 spuBlendDesc
.RenderTarget
[0].BlendEnable
= TRUE
;
1158 spuBlendDesc
.RenderTarget
[0].RenderTargetWriteMask
= D3D11_COLOR_WRITE_ENABLE_ALL
;
1160 spuBlendDesc
.RenderTarget
[0].BlendOp
= D3D11_BLEND_OP_ADD
;
1161 spuBlendDesc
.RenderTarget
[0].SrcBlend
= D3D11_BLEND_SRC_ALPHA
; /* keep source intact */
1162 spuBlendDesc
.RenderTarget
[0].DestBlend
= D3D11_BLEND_INV_SRC_ALPHA
; /* RGB colors + inverse alpha (255 is full opaque) */
1164 spuBlendDesc
.RenderTarget
[0].BlendOpAlpha
= D3D11_BLEND_OP_ADD
;
1165 spuBlendDesc
.RenderTarget
[0].SrcBlendAlpha
= D3D11_BLEND_ONE
; /* keep source intact */
1166 spuBlendDesc
.RenderTarget
[0].DestBlendAlpha
= D3D11_BLEND_ZERO
; /* discard */
1168 hr
= ID3D11Device_CreateBlendState(sys
->d3d_dev
->d3ddevice
, &spuBlendDesc
, &pSpuBlendState
);
1170 msg_Err(vd
, "Could not create SPU blend state. (hr=0x%lX)", hr
);
1171 return VLC_EGENERIC
;
1173 ID3D11DeviceContext_OMSetBlendState(sys
->d3d_dev
->d3dcontext
, pSpuBlendState
, NULL
, 0xFFFFFFFF);
1174 ID3D11BlendState_Release(pSpuBlendState
);
1176 /* disable depth testing as we're only doing 2D
1177 * see https://msdn.microsoft.com/en-us/library/windows/desktop/bb205074%28v=vs.85%29.aspx
1178 * see http://rastertek.com/dx11tut11.html
1180 D3D11_DEPTH_STENCIL_DESC stencilDesc
;
1181 ZeroMemory(&stencilDesc
, sizeof(stencilDesc
));
1183 ID3D11DepthStencilState
*pDepthStencilState
;
1184 hr
= ID3D11Device_CreateDepthStencilState(sys
->d3d_dev
->d3ddevice
, &stencilDesc
, &pDepthStencilState
);
1185 if (SUCCEEDED(hr
)) {
1186 ID3D11DeviceContext_OMSetDepthStencilState(sys
->d3d_dev
->d3dcontext
, pDepthStencilState
, 0);
1187 ID3D11DepthStencilState_Release(pDepthStencilState
);
1190 if (sys
->regionQuad
.generic
.textureFormat
!= NULL
)
1192 d3d_shader_blob pPSBlob
[DXGI_MAX_RENDER_TARGET
] = { 0 };
1193 hr
= D3D11_CompilePixelShaderBlob(vd
, &sys
->shaders
, sys
->d3d_dev
,
1194 &sys
->display
, TRANSFER_FUNC_SRGB
, true,
1195 &sys
->regionQuad
, pPSBlob
);
1198 msg_Err(vd
, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr
);
1199 return VLC_EGENERIC
;
1201 hr
= D3D11_SetQuadPixelShader(VLC_OBJECT(vd
), sys
->d3d_dev
, true,
1202 &sys
->regionQuad
, pPSBlob
);
1205 msg_Err(vd
, "Failed to create the SPU pixel shader. (hr=0x%lX)", hr
);
1206 return VLC_EGENERIC
;
1210 d3d_shader_blob VSBlob
= { 0 };
1211 hr
= D3D11_CompileVertexShaderBlob(VLC_OBJECT(vd
), &sys
->shaders
, sys
->d3d_dev
, true, &VSBlob
);
1213 msg_Err(vd
, "Failed to compile the flat vertex shader. (hr=0x%lX)", hr
);
1214 return VLC_EGENERIC
;
1216 hr
= D3D11_CreateVertexShader(vd
, &VSBlob
, sys
->d3d_dev
, &sys
->flatVShader
);
1218 msg_Err(vd
, "Failed to create the vertex input layout. (hr=0x%lX)", hr
);
1219 return VLC_EGENERIC
;
1223 hr
= D3D11_CompileVertexShaderBlob(VLC_OBJECT(vd
), &sys
->shaders
, sys
->d3d_dev
, false, &VSBlob
);
1225 msg_Err(vd
, "Failed to compile the 360 vertex shader. (hr=0x%lX)", hr
);
1226 return VLC_EGENERIC
;
1228 hr
= D3D11_CreateVertexShader(vd
, &VSBlob
, sys
->d3d_dev
, &sys
->projectionVShader
);
1230 msg_Err(vd
, "Failed to create the projection vertex shader. (hr=0x%lX)", hr
);
1231 return VLC_EGENERIC
;
1235 .left
= sys
->area
.place
.x
,
1236 .right
= sys
->area
.place
.x
+ sys
->area
.place
.width
,
1237 .top
= sys
->area
.place
.y
,
1238 .bottom
= sys
->area
.place
.y
+ sys
->area
.place
.height
1241 D3D11_UpdateViewport( &sys
->picQuad
, &rect_dst
, sys
->display
.pixelFormat
);
1244 msg_Dbg( vd
, "picQuad position (%.02f,%.02f) %.02fx%.02f",
1245 sys
->picQuad
.cropViewport
[0].TopLeftX
, sys
->picQuad
.cropViewport
[0].TopLeftY
,
1246 sys
->picQuad
.cropViewport
[0].Width
, sys
->picQuad
.cropViewport
[0].Height
);
1249 D3D11_UpdateViewpoint( vd
, sys
->d3d_dev
, &sys
->picQuad
, &vd
->cfg
->viewpoint
,
1250 (float) vd
->cfg
->display
.width
/ vd
->cfg
->display
.height
);
1252 msg_Dbg(vd
, "Direct3D11 resources created");
1256 static void Direct3D11DestroyResources(vout_display_t
*vd
)
1258 vout_display_sys_t
*sys
= vd
->sys
;
1260 D3D11_ReleaseQuad(&sys
->picQuad
);
1261 Direct3D11DeleteRegions(sys
->d3dregion_count
, sys
->d3dregions
);
1262 sys
->d3dregion_count
= 0;
1263 D3D11_ReleaseQuad(&sys
->regionQuad
);
1265 ReleaseD3D11PictureSys(&sys
->stagingSys
);
1267 D3D11_ReleaseVertexShader(&sys
->flatVShader
);
1268 D3D11_ReleaseVertexShader(&sys
->projectionVShader
);
1270 #ifdef HAVE_D3D11_4_H
1271 if (sys
->d3dcontext4
)
1273 ID3D11Fence_Release(sys
->d3dRenderFence
);
1274 sys
->d3dRenderFence
= NULL
;
1275 ID3D11DeviceContext4_Release(sys
->d3dcontext4
);
1276 sys
->d3dcontext4
= NULL
;
1277 CloseHandle(sys
->renderFinished
);
1278 sys
->renderFinished
= NULL
;
1282 if (sys
->prepareWait
)
1284 ID3D11Query_Release(sys
->prepareWait
);
1285 sys
->prepareWait
= NULL
;
1288 msg_Dbg(vd
, "Direct3D11 resources destroyed");
1291 static void Direct3D11DeleteRegions(int count
, picture_t
**region
)
1293 for (int i
= 0; i
< count
; ++i
) {
1295 picture_Release(region
[i
]);
1301 static void DestroyPictureQuad(picture_t
*p_picture
)
1303 D3D11_ReleaseQuad( (d3d11_quad_t
*) p_picture
->p_sys
);
1306 static int Direct3D11MapSubpicture(vout_display_t
*vd
, int *subpicture_region_count
,
1307 picture_t
***region
, subpicture_t
*subpicture
)
1309 vout_display_sys_t
*sys
= vd
->sys
;
1310 D3D11_MAPPED_SUBRESOURCE mappedResource
;
1311 D3D11_TEXTURE2D_DESC texDesc
;
1315 if (sys
->regionQuad
.generic
.textureFormat
== NULL
)
1316 return VLC_EGENERIC
;
1319 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
)
1322 *region
= calloc(count
, sizeof(picture_t
*));
1323 if (unlikely(*region
==NULL
))
1325 *subpicture_region_count
= count
;
1328 for (subpicture_region_t
*r
= subpicture
->p_region
; r
; r
= r
->p_next
, i
++) {
1329 if (!r
->fmt
.i_visible_width
|| !r
->fmt
.i_visible_height
)
1330 continue; // won't render anything, keep the cache for later
1332 for (int j
= 0; j
< sys
->d3dregion_count
; j
++) {
1333 picture_t
*cache
= sys
->d3dregions
[j
];
1334 if (cache
!= NULL
&& ((d3d11_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
]) {
1335 ID3D11Texture2D_GetDesc( ((d3d11_quad_t
*) cache
->p_sys
)->picSys
.texture
[KNOWN_DXGI_INDEX
], &texDesc
);
1336 if (texDesc
.Format
== sys
->regionQuad
.generic
.textureFormat
->formatTexture
&&
1337 texDesc
.Width
== r
->p_picture
->format
.i_width
&&
1338 texDesc
.Height
== r
->p_picture
->format
.i_height
) {
1339 (*region
)[i
] = cache
;
1340 memset(&sys
->d3dregions
[j
], 0, sizeof(cache
)); // do not reuse this cached value a second time
1346 picture_t
*quad_picture
= (*region
)[i
];
1348 if (quad_picture
!= NULL
)
1349 quad
= quad_picture
->p_sys
;
1352 d3d11_quad_t
*d3dquad
= calloc(1, sizeof(*d3dquad
));
1353 if (unlikely(d3dquad
==NULL
)) {
1357 if (AllocateTextures(vd
, sys
->d3d_dev
, sys
->regionQuad
.generic
.textureFormat
, &r
->p_picture
->format
, d3dquad
->picSys
.texture
, NULL
)) {
1358 msg_Err(vd
, "Failed to allocate %dx%d texture for OSD",
1359 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
1360 for (int j
=0; j
<DXGI_MAX_SHADER_VIEW
; j
++)
1361 if (d3dquad
->picSys
.texture
[j
])
1362 ID3D11Texture2D_Release(d3dquad
->picSys
.texture
[j
]);
1367 if (D3D11_AllocateResourceView(vd
, sys
->d3d_dev
->d3ddevice
, sys
->regionQuad
.generic
.textureFormat
,
1368 d3dquad
->picSys
.texture
, 0,
1369 d3dquad
->picSys
.renderSrc
)) {
1370 msg_Err(vd
, "Failed to create %dx%d shader view for OSD",
1371 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
1375 d3dquad
->generic
.i_width
= r
->fmt
.i_width
;
1376 d3dquad
->generic
.i_height
= r
->fmt
.i_height
;
1378 d3dquad
->generic
.textureFormat
= sys
->regionQuad
.generic
.textureFormat
;
1379 err
= D3D11_AllocateQuad(vd
, sys
->d3d_dev
, PROJECTION_MODE_RECTANGULAR
, d3dquad
);
1380 if (err
!= VLC_SUCCESS
)
1382 msg_Err(vd
, "Failed to allocate %dx%d quad for OSD",
1383 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
1388 err
= D3D11_SetupQuad( vd
, sys
->d3d_dev
, &r
->fmt
, d3dquad
, &sys
->display
);
1389 if (err
!= VLC_SUCCESS
) {
1390 msg_Err(vd
, "Failed to setup %dx%d quad for OSD",
1391 r
->fmt
.i_visible_width
, r
->fmt
.i_visible_height
);
1395 picture_resource_t picres
= {
1396 .p_sys
= (picture_sys_d3d11_t
*) d3dquad
,
1397 .pf_destroy
= DestroyPictureQuad
,
1399 (*region
)[i
] = picture_NewFromResource(&r
->p_picture
->format
, &picres
);
1400 if ((*region
)[i
] == NULL
) {
1401 msg_Err(vd
, "Failed to create %dx%d picture for OSD",
1402 r
->fmt
.i_width
, r
->fmt
.i_height
);
1403 D3D11_ReleaseQuad(d3dquad
);
1406 quad_picture
= (*region
)[i
];
1407 for (size_t j
=0; j
<ARRAY_SIZE(sys
->regionQuad
.d3dpixelShader
); j
++)
1409 /* TODO use something more accurate if we have different formats */
1410 if (sys
->regionQuad
.d3dpixelShader
[j
])
1412 d3dquad
->d3dpixelShader
[j
] = sys
->regionQuad
.d3dpixelShader
[j
];
1413 ID3D11PixelShader_AddRef(d3dquad
->d3dpixelShader
[j
]);
1418 hr
= ID3D11DeviceContext_Map(sys
->d3d_dev
->d3dcontext
, ((d3d11_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0, D3D11_MAP_WRITE_DISCARD
, 0, &mappedResource
);
1419 if( SUCCEEDED(hr
) ) {
1420 err
= picture_UpdatePlanes(quad_picture
, mappedResource
.pData
, mappedResource
.RowPitch
);
1421 if (err
!= VLC_SUCCESS
) {
1422 msg_Err(vd
, "Failed to set the buffer on the SPU picture" );
1423 ID3D11DeviceContext_Unmap(sys
->d3d_dev
->d3dcontext
, ((d3d11_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0);
1424 picture_Release(quad_picture
);
1425 if ((*region
)[i
] == quad_picture
)
1426 (*region
)[i
] = NULL
;
1430 picture_CopyPixels(quad_picture
, r
->p_picture
);
1432 ID3D11DeviceContext_Unmap(sys
->d3d_dev
->d3dcontext
, ((d3d11_quad_t
*) quad_picture
->p_sys
)->picSys
.resource
[KNOWN_DXGI_INDEX
], 0);
1434 msg_Err(vd
, "Failed to map the SPU texture (hr=0x%lX)", hr
);
1435 picture_Release(quad_picture
);
1436 if ((*region
)[i
] == quad_picture
)
1437 (*region
)[i
] = NULL
;
1442 output
.left
= r
->fmt
.i_x_offset
;
1443 output
.right
= r
->fmt
.i_x_offset
+ r
->fmt
.i_visible_width
;
1444 output
.top
= r
->fmt
.i_y_offset
;
1445 output
.bottom
= r
->fmt
.i_y_offset
+ r
->fmt
.i_visible_height
;
1447 D3D11_UpdateQuadPosition(vd
, sys
->d3d_dev
, quad
, &output
, ORIENT_NORMAL
);
1450 spuViewport
.left
= (FLOAT
) r
->i_x
* sys
->area
.place
.width
/ subpicture
->i_original_picture_width
;
1451 spuViewport
.top
= (FLOAT
) r
->i_y
* sys
->area
.place
.height
/ subpicture
->i_original_picture_height
;
1452 spuViewport
.right
= (FLOAT
) (r
->i_x
+ r
->fmt
.i_visible_width
) * sys
->area
.place
.width
/ subpicture
->i_original_picture_width
;
1453 spuViewport
.bottom
= (FLOAT
) (r
->i_y
+ r
->fmt
.i_visible_height
) * sys
->area
.place
.height
/ subpicture
->i_original_picture_height
;
1455 if (r
->zoom_h
.num
!= 0 && r
->zoom_h
.den
!= 0)
1457 spuViewport
.left
= (FLOAT
) spuViewport
.left
* r
->zoom_h
.num
/ r
->zoom_h
.den
;
1458 spuViewport
.right
= (FLOAT
) spuViewport
.right
* r
->zoom_h
.num
/ r
->zoom_h
.den
;
1460 if (r
->zoom_v
.num
!= 0 && r
->zoom_v
.den
!= 0)
1462 spuViewport
.top
= (FLOAT
) spuViewport
.top
* r
->zoom_v
.num
/ r
->zoom_v
.den
;
1463 spuViewport
.bottom
= (FLOAT
) spuViewport
.bottom
* r
->zoom_v
.num
/ r
->zoom_v
.den
;
1466 /* move the SPU inside the video area */
1467 spuViewport
.left
+= sys
->area
.place
.x
;
1468 spuViewport
.right
+= sys
->area
.place
.x
;
1469 spuViewport
.top
+= sys
->area
.place
.y
;
1470 spuViewport
.bottom
+= sys
->area
.place
.y
;
1472 D3D11_UpdateViewport( quad
, &spuViewport
, sys
->display
.pixelFormat
);
1474 D3D11_UpdateQuadOpacity(vd
, sys
->d3d_dev
, quad
, r
->i_alpha
/ 255.0f
);