qt: playlist: use item title if available
[vlc.git] / modules / video_output / win32 / direct3d11.c
blobdb921eb7321f3a1ebc7f8999756048231499e83d
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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0601 // _WIN32_WINNT_WIN7
29 # undef _WIN32_WINNT
30 # define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
31 #endif
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>
43 #include <assert.h>
44 #include <math.h>
46 #define COBJMACROS
47 #include <initguid.h>
48 #include <d3d11.h>
49 #ifdef HAVE_D3D11_4_H
50 #include <d3d11_4.h>
51 #endif
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"
62 #include "common.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.")
74 vlc_module_begin ()
75 set_shortname("Direct3D11")
76 set_description(N_("Direct3D11 video output"))
77 set_help(D3D11_HELP)
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)
83 #if VLC_WINSTORE_APP
84 add_integer("winrt-swapchain", 0x0, NULL, NULL, true) /* IDXGISwapChain1* */
85 #endif
87 add_shortcut("direct3d11")
88 set_callback_display(Open, 300)
89 vlc_module_end ()
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;
96 /* Sensors */
97 void *p_sensors;
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;
110 UINT64 renderFence;
111 HANDLE renderFinished;
112 #endif
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 */
122 bool legacy_shader;
124 // SPU
125 vlc_fourcc_t pSubpictureChromas[2];
126 d3d11_quad_t regionQuad;
127 int d3dregion_count;
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:
167 cfg.bitdepth = 8;
168 break;
169 case VLC_CODEC_D3D11_OPAQUE_RGBA:
170 case VLC_CODEC_D3D11_OPAQUE_BGRA:
171 cfg.bitdepth = 8;
172 break;
173 case VLC_CODEC_D3D11_OPAQUE_10B:
174 cfg.bitdepth = 10;
175 break;
176 default:
178 const vlc_chroma_description_t *p_format = vlc_fourcc_GetChromaDescription(fmt->i_chroma);
179 if (p_format == NULL)
181 cfg.bitdepth = 8;
183 else
185 cfg.bitdepth = p_format->pixel_bits == 0 ? 8 : p_format->pixel_bits /
186 (p_format->plane_count==1 ? p_format->pixel_size : 1);
189 break;
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);
203 return VLC_EGENERIC;
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;
215 break;
218 if (unlikely(new_display.pixelFormat == NULL))
220 msg_Err(vd, "Could not find the output format.");
221 return VLC_EGENERIC;
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;
235 break;
236 case TRANSFER_FUNC_SMPTE_ST2084:
237 new_display.luminance_peak = MAX_PQ_BRIGHTNESS;
238 break;
239 default:
240 new_display.luminance_peak = DEFAULT_SRGB_BRIGHTNESS;
241 break;
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");
256 return VLC_EGENERIC;
260 return VLC_SUCCESS;
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);
271 RECT rect_dst = {
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 );
280 RECT source_rect = {
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 );
296 #ifndef NDEBUG
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 );
300 #endif
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 );
313 return VLC_SUCCESS;
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");
333 return VLC_EGENERIC;
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");
340 return VLC_EGENERIC;
343 for (unsigned plane = 0; plane < DXGI_MAX_SHADER_VIEW; plane++)
344 sys->stagingSys.texture[plane] = textures[plane];
346 #endif
347 return VLC_SUCCESS;
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));
358 if (!sys)
359 return VLC_ENOMEM;
361 int ret = D3D_InitShaderCompiler(VLC_OBJECT(vd), &sys->shaders);
362 if (ret != VLC_SUCCESS)
363 goto error;
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.");
381 goto error;
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))
394 goto error;
397 #endif /* !VLC_WINSTORE_APP */
399 /* use our internal swapchain callbacks */
400 #ifdef HAVE_DCOMP_H
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);
403 else
404 #endif
405 sys->outside_opaque = D3D11_CreateLocalSwapchainHandleHwnd(VLC_OBJECT(vd), sys->sys.hvideownd, sys->d3d_dev);
406 if (unlikely(sys->outside_opaque == NULL))
407 goto error;
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");
422 goto error;
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;
437 else
438 vd->info.subpicture_chromas = NULL;
440 vd->ops = &ops;
442 msg_Dbg(vd, "Direct3D11 Open Succeeded");
444 return VLC_SUCCESS;
446 error:
447 Close(vd);
448 return VLC_EGENERIC;
451 static void Close(vout_display_t *vd)
453 D3D_ReleaseShaderCompiler(&vd->sys->shaders);
454 Direct3D11Close(vd);
455 #if !VLC_WINSTORE_APP
456 UnhookWindowsSensors(vd->sys->p_sensors);
457 CommonWindowClean(&vd->sys->sys);
458 #endif
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 )
467 UpdateSize(vd);
468 sys->area.place_changed =false;
471 return res;
474 static bool SelectRenderPlane(void *opaque, size_t plane, ID3D11RenderTargetView **targetView)
476 vout_display_sys_t *sys = opaque;
477 if (!sys->selectPlaneCb)
479 *targetView = NULL;
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,
486 vlc_tick_t date)
488 vout_display_sys_t *sys = vd->sys;
490 if (sys->picQuad.generic.textureFormat->formatTexture == DXGI_FORMAT_UNKNOWN)
492 D3D11_MAPPED_SUBRESOURCE mappedResource;
493 int i;
494 HRESULT hr;
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)) )
502 while (i-- > 0)
503 ID3D11DeviceContext_Unmap(sys->d3d_dev->d3dcontext, sys->stagingSys.resource[i], 0);
504 b_mapped = false;
505 break;
507 sys->stagingPlanes[i].i_pitch = mappedResource.RowPitch;
508 sys->stagingPlanes[i].p_pixels = mappedResource.pData;
511 if (b_mapped)
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;
523 HRESULT hr;
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);
529 else
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);
545 else
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);
553 D3D11_BOX box = {
554 .top = 0,
555 .bottom = __MIN(srcDesc.Height, texDesc.Height),
556 .left = 0,
557 .right = __MIN(srcDesc.Width, texDesc.Width),
558 .back = 1,
560 ID3D11DeviceContext_CopySubresourceRegion(sys->d3d_dev->d3dcontext,
561 sys->stagingSys.resource[KNOWN_DXGI_INDEX],
562 0, 0, 0, 0,
563 p_sys->resource[KNOWN_DXGI_INDEX],
564 p_sys->slice_index, &box);
566 else
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);
585 UpdateSize(vd);
590 if (subpicture) {
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;
608 else {
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);
616 if (subpicture) {
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;
633 else
634 sys->renderFence++;
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);
642 else
643 #endif
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
656 break;
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 );
670 #if VLC_WINSTORE_APP
671 if ( sys->swapCb == D3D11_LocalSwapchainSwap )
673 /* legacy UWP mode, the width/height was set in GUID_SWAPCHAIN_WIDTH/HEIGHT */
674 uint32_t i_width;
675 uint32_t i_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);
682 #endif
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;
714 VLC_UNUSED(picture);
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))
725 if (start == 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
729 SleepEx(2, TRUE);
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,
757 bool from_processor,
758 int rgb_yuv)
760 UINT supportFlags = D3D11_FORMAT_SUPPORT_SHADER_LOAD;
761 if (from_processor)
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;
777 video_format_t fmt;
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
784 && vd->obj.force
785 #endif
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)
794 continue;
795 err = SetupOutputFormat(vd, &fmt, NULL);
796 if (err == VLC_SUCCESS)
797 break;
800 if (err != VLC_SUCCESS)
802 if ( sys->swapCb == D3D11_LocalSwapchainSwap )
803 D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
804 return err;
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");
823 return err;
826 if (Direct3D11CreateGenericResources(vd)) {
827 msg_Err(vd, "Failed to allocate resources");
828 if ( sys->swapCb == D3D11_LocalSwapchainSwap )
829 D3D11_LocalSwapchainCleanupDevice( sys->outside_opaque );
830 return VLC_EGENERIC;
833 video_format_Clean(fmtp);
834 *fmtp = fmt;
836 return VLC_SUCCESS;
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;
854 break;
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;
875 break;
876 case VLC_CODEC_D3D11_OPAQUE_RGBA:
877 case VLC_CODEC_D3D11_OPAQUE_BGRA:
878 bits_per_channel = 8;
879 widthDenominator = heightDenominator = 1;
880 break;
881 case VLC_CODEC_D3D11_OPAQUE_10B:
882 case VLC_CODEC_NVDEC_OPAQUE_10B:
883 bits_per_channel = 10;
884 widthDenominator = heightDenominator = 2;
885 break;
886 case VLC_CODEC_NVDEC_OPAQUE_16B:
887 bits_per_channel = 16;
888 widthDenominator = heightDenominator = 2;
889 break;
890 case VLC_CODEC_NVDEC_OPAQUE_444:
891 bits_per_channel = 8;
892 widthDenominator = heightDenominator = 1;
893 break;
894 case VLC_CODEC_NVDEC_OPAQUE_444_16B:
895 bits_per_channel = 16;
896 widthDenominator = heightDenominator = 1;
897 break;
898 default:
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;
906 else
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;
920 break;
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);
926 else
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");
948 return VLC_EGENERIC;
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);
962 return VLC_SUCCESS;
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
983 (void) vd;
984 return false;
985 #else
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");
995 return false;
998 // xx.xx.1000.xxx drivers can't happen here for WDDM > 2.0
999 struct wddm_version WDDM_build = {
1000 .revision = 162,
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");
1005 return false;
1008 return true;
1009 #endif
1012 static bool BogusZeroCopy(const vout_display_t *vd)
1014 if (vd->sys->d3d_dev->adapterDesc.VendorId != GPU_MANUFACTURER_AMD)
1015 return false;
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;
1028 default:
1029 return false;
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;
1038 HRESULT hr;
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);
1048 if (FAILED(hr))
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);
1055 if (FAILED(hr))
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)
1105 HRESULT hr;
1106 sys->renderFinished = CreateEvent(NULL, TRUE, FALSE, NULL);
1107 if (unlikely(sys->renderFinished == NULL))
1108 return S_FALSE;
1109 ID3D11Device5 *d3ddev5 = NULL;
1110 hr = ID3D11DeviceContext_QueryInterface(sys->d3d_dev->d3dcontext, &IID_ID3D11DeviceContext4, (void**)&sys->d3dcontext4);
1111 if (FAILED(hr))
1112 goto error;
1113 hr = ID3D11Device_QueryInterface(sys->d3d_dev->d3ddevice, &IID_ID3D11Device5, (void**)&d3ddev5);
1114 if (FAILED(hr))
1115 goto error;
1116 hr = ID3D11Device5_CreateFence(d3ddev5, sys->renderFence, D3D11_FENCE_FLAG_NONE, &IID_ID3D11Fence, (void**)&sys->d3dRenderFence);
1117 if (FAILED(hr))
1118 goto error;
1119 ID3D11Device5_Release(d3ddev5);
1120 return hr;
1121 error:
1122 if (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);
1132 return hr;
1134 #endif // HAVE_D3D11_4_H
1136 static int Direct3D11CreateGenericResources(vout_display_t *vd)
1138 vout_display_sys_t *sys = vd->sys;
1139 HRESULT hr;
1141 #ifdef HAVE_D3D11_4_H
1142 hr = InitRenderFence(sys);
1143 if (SUCCEEDED(hr))
1145 msg_Dbg(vd, "using GPU render fence");
1147 else
1148 #endif
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;
1159 /* output colors */
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) */
1163 /* output alpha */
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);
1169 if (FAILED(hr)) {
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);
1196 if (FAILED(hr))
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);
1203 if (FAILED(hr))
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);
1212 if(FAILED(hr)) {
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);
1217 if(FAILED(hr)) {
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);
1224 if(FAILED(hr)) {
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);
1229 if(FAILED(hr)) {
1230 msg_Err(vd, "Failed to create the projection vertex shader. (hr=0x%lX)", hr);
1231 return VLC_EGENERIC;
1234 RECT rect_dst = {
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 );
1243 #ifndef NDEBUG
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 );
1247 #endif
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");
1253 return VLC_SUCCESS;
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;
1280 else
1281 #endif
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) {
1294 if (region[i]) {
1295 picture_Release(region[i]);
1298 free(region);
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;
1312 HRESULT hr;
1313 int err;
1315 if (sys->regionQuad.generic.textureFormat == NULL)
1316 return VLC_EGENERIC;
1318 int count = 0;
1319 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1320 count++;
1322 *region = calloc(count, sizeof(picture_t *));
1323 if (unlikely(*region==NULL))
1324 return VLC_ENOMEM;
1325 *subpicture_region_count = count;
1327 int i = 0;
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
1341 break;
1346 picture_t *quad_picture = (*region)[i];
1347 d3d11_quad_t *quad;
1348 if (quad_picture != NULL)
1349 quad = quad_picture->p_sys;
1350 else
1352 d3d11_quad_t *d3dquad = calloc(1, sizeof(*d3dquad));
1353 if (unlikely(d3dquad==NULL)) {
1354 continue;
1356 quad = d3dquad;
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]);
1363 free(d3dquad);
1364 continue;
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);
1372 free(d3dquad);
1373 continue;
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);
1384 free(d3dquad);
1385 continue;
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);
1392 free(d3dquad);
1393 continue;
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);
1404 continue;
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;
1427 continue;
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);
1433 } else {
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;
1438 continue;
1441 RECT output;
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);
1449 RECT spuViewport;
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 );
1476 return VLC_SUCCESS;