1 /*****************************************************************************
2 * direct3d.c: Windows Direct3D video output module
3 *****************************************************************************
4 * Copyright (C) 2006-2009 VLC authors and VideoLAN
7 * Authors: Damien Fouilleul <damienf@videolan.org>
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 /*****************************************************************************
27 * This plugin will use YUV surface if supported, using YUV will result in
28 * the best video quality (hardware filering when rescaling the picture)
29 * and the fastest display as it requires less processing.
31 * If YUV overlay is not supported this plugin will use RGB offscreen video
32 * surfaces that will be blitted onto the primary surface (display) to
33 * effectively display the pictures.
35 *****************************************************************************/
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_playlist.h>
43 #include <vlc_vout_display.h>
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Open(vlc_object_t *);
54 static void Close(vlc_object_t *);
56 #define DESKTOP_TEXT N_("Enable desktop mode ")
57 #define DESKTOP_LONGTEXT N_(\
58 "The desktop mode allows you to display the video on the desktop.")
60 #define HW_BLENDING_TEXT N_("Use hardware blending support")
61 #define HW_BLENDING_LONGTEXT N_(\
62 "Try to use hardware acceleration for subtitles/OSD blending.")
64 #define D3D_HELP N_("Recommended video output for Windows Vista and later versions")
67 set_shortname("Direct3D")
68 set_description(N_("Direct3D video output"))
70 set_category(CAT_VIDEO)
71 set_subcategory(SUBCAT_VIDEO_VOUT)
73 add_bool("direct3d-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
75 set_capability("vout display", 240)
76 add_shortcut("direct3d")
77 set_callbacks(Open, Close)
81 /*****************************************************************************
83 *****************************************************************************/
84 static const vlc_fourcc_t d3d_subpicture_chromas[] = {
91 LPDIRECT3DSURFACE9 surface;
95 static int Open(vlc_object_t *);
97 static picture_pool_t *Pool (vout_display_t *, unsigned);
98 static void Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
99 static void Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
100 static int Control(vout_display_t *, int, va_list);
101 static void Manage (vout_display_t *);
103 static int Direct3DCreate (vout_display_t *);
104 static int Direct3DReset (vout_display_t *);
105 static void Direct3DDestroy(vout_display_t *);
107 static int Direct3DOpen (vout_display_t *, video_format_t *);
108 static void Direct3DClose(vout_display_t *);
113 FLOAT x,y,z; // vertex untransformed position
114 FLOAT rhw; // eye distance
115 D3DCOLOR diffuse; // diffuse color
116 FLOAT tu, tv; // texture relative coordinates
118 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
120 typedef struct d3d_region_t {
124 CUSTOMVERTEX vertex[4];
125 LPDIRECT3DTEXTURE9 texture;
128 static void Direct3DDeleteRegions(int, d3d_region_t *);
130 static int Direct3DImportPicture(vout_display_t *vd, d3d_region_t *, LPDIRECT3DSURFACE9 surface);
131 static void Direct3DImportSubpicture(vout_display_t *vd, int *, d3d_region_t **, subpicture_t *);
133 static void Direct3DRenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_region_t *);
136 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
139 * It creates a Direct3D vout display.
141 static int Open(vlc_object_t *object)
143 vout_display_t *vd = (vout_display_t *)object;
144 vout_display_sys_t *sys;
146 /* Allocate structure */
147 vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
151 if (Direct3DCreate(vd)) {
152 msg_Err(vd, "Direct3D could not be initialized");
158 sys->use_desktop = var_CreateGetBool(vd, "video-wallpaper");
159 sys->reset_device = false;
160 sys->reset_device = false;
161 sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
162 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
163 sys->desktop_save.is_on_top = false;
164 sys->desktop_save.win.left = var_InheritInteger(vd, "video-x");
165 sys->desktop_save.win.right = vd->cfg->display.width;
166 sys->desktop_save.win.top = var_InheritInteger(vd, "video-y");
167 sys->desktop_save.win.bottom = vd->cfg->display.height;
174 if (Direct3DOpen(vd, &fmt)) {
175 msg_Err(vd, "Direct3D could not be opened");
180 vout_display_info_t info = vd->info;
182 info.has_double_click = true;
183 info.has_hide_mouse = false;
184 info.has_pictures_invalid = true;
185 info.has_event_thread = true;
186 if (var_InheritBool(vd, "direct3d-hw-blending") &&
187 sys->d3dregion_format != D3DFMT_UNKNOWN &&
188 (sys->d3dcaps.SrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) &&
189 (sys->d3dcaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
190 (sys->d3dcaps.TextureCaps & D3DPTEXTURECAPS_ALPHA) &&
191 (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
192 (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
193 info.subpicture_chromas = d3d_subpicture_chromas;
195 info.subpicture_chromas = NULL;
198 vlc_mutex_init(&sys->lock);
199 sys->ch_desktop = false;
200 sys->desktop_requested = sys->use_desktop;
203 val.psz_string = _("Desktop");
204 var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
205 var_AddCallback(vd, "video-wallpaper", DesktopCallback, NULL);
207 /* Setup vout_display now that everything is fine */
212 vd->prepare = Prepare;
213 vd->display = Display;
214 vd->control = Control;
217 /* Fix state in case of desktop mode */
218 if (sys->use_desktop && vd->cfg->is_fullscreen)
219 vout_display_SendEventFullscreen(vd, false);
231 * It destroyes a Direct3D vout display.
233 static void Close(vlc_object_t *object)
235 vout_display_t * vd = (vout_display_t *)object;
237 var_DelCallback(vd, "video-wallpaper", DesktopCallback, NULL);
238 vlc_mutex_destroy(&vd->sys->lock);
250 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
253 return vd->sys->pool;
256 static int Direct3DLockSurface(picture_t *);
257 static void Direct3DUnlockSurface(picture_t *);
259 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
261 vout_display_sys_t *sys = vd->sys;
262 LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
264 picture_Release(picture);
265 VLC_UNUSED(subpicture);
267 /* FIXME it is a bit ugly, we need the surface to be unlocked for
269 * The clean way would be to release the picture (and ensure that
270 * the vout doesn't keep a reference). But because of the vout
271 * wrapper, we can't */
273 Direct3DUnlockSurface(picture);
274 VLC_UNUSED(subpicture);
277 /* check if device is still available */
278 HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
280 if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
281 vout_display_SendEventPicturesInvalid(vd);
282 sys->reset_device = true;
287 d3d_region_t picture_region;
288 if (!Direct3DImportPicture(vd, &picture_region, surface)) {
289 int subpicture_region_count = 0;
290 d3d_region_t *subpicture_region = NULL;
292 Direct3DImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
295 Direct3DRenderScene(vd, &picture_region,
296 subpicture_region_count, subpicture_region);
298 Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
299 sys->d3dregion_count = subpicture_region_count;
300 sys->d3dregion = subpicture_region;
304 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
306 vout_display_sys_t *sys = vd->sys;
307 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
309 // Present the back buffer contents to the display
310 // No stretching should happen here !
311 const RECT src = sys->rect_dest_clipped;
312 const RECT dst = sys->rect_dest_clipped;
313 HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
315 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
320 VLC_UNUSED(subpicture);
322 /* XXX See Prepare() */
323 Direct3DLockSurface(picture);
324 picture_Release(picture);
327 subpicture_Delete(subpicture);
331 static int ControlResetDevice(vout_display_t *vd)
333 return Direct3DReset(vd);
335 static int ControlReopenDevice(vout_display_t *vd)
337 vout_display_sys_t *sys = vd->sys;
339 if (!sys->use_desktop) {
340 /* Save non-desktop state */
341 sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
342 sys->desktop_save.is_on_top = sys->is_on_top;
344 WINDOWPLACEMENT wp = { .length = sizeof(wp), };
345 GetWindowPlacement(sys->hparent ? sys->hparent : sys->hwnd, &wp);
346 sys->desktop_save.win = wp.rcNormalPosition;
351 EventThreadStop(sys->event);
354 vlc_mutex_lock(&sys->lock);
355 sys->use_desktop = sys->desktop_requested;
356 sys->ch_desktop = false;
357 vlc_mutex_unlock(&sys->lock);
361 memset(&cfg, 0, sizeof(cfg));
362 cfg.use_desktop = sys->use_desktop;
363 if (!sys->use_desktop) {
364 cfg.win.type = VOUT_WINDOW_TYPE_HWND;
365 cfg.win.x = sys->desktop_save.win.left;
366 cfg.win.y = sys->desktop_save.win.top;
367 cfg.win.width = sys->desktop_save.win.right - sys->desktop_save.win.left;
368 cfg.win.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
372 if (EventThreadStart(sys->event, &hwnd, &cfg)) {
373 msg_Err(vd, "Failed to restart event thread");
376 sys->parent_window = hwnd.parent_window;
377 sys->hparent = hwnd.hparent;
378 sys->hwnd = hwnd.hwnd;
379 sys->hvideownd = hwnd.hvideownd;
380 sys->hfswnd = hwnd.hfswnd;
381 SetRectEmpty(&sys->rect_parent);
385 if (Direct3DOpen(vd, &fmt)) {
387 msg_Err(vd, "Failed to reopen device");
391 sys->is_first_display = true;
393 if (sys->use_desktop) {
394 /* Disable fullscreen/on_top while using desktop */
395 if (sys->desktop_save.is_fullscreen)
396 vout_display_SendEventFullscreen(vd, false);
397 if (sys->desktop_save.is_on_top)
398 vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
400 /* Restore fullscreen/on_top */
401 if (sys->desktop_save.is_fullscreen)
402 vout_display_SendEventFullscreen(vd, true);
403 if (sys->desktop_save.is_on_top)
404 vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
408 static int Control(vout_display_t *vd, int query, va_list args)
410 vout_display_sys_t *sys = vd->sys;
413 case VOUT_DISPLAY_RESET_PICTURES:
414 /* FIXME what to do here in case of failure */
415 if (sys->reset_device) {
416 if (ControlResetDevice(vd)) {
417 msg_Err(vd, "Failed to reset device");
420 sys->reset_device = false;
421 } else if(sys->reopen_device) {
422 if (ControlReopenDevice(vd)) {
423 msg_Err(vd, "Failed to reopen device");
426 sys->reopen_device = false;
430 return CommonControl(vd, query, args);
433 static void Manage (vout_display_t *vd)
435 vout_display_sys_t *sys = vd->sys;
439 /* Desktop mode change */
440 vlc_mutex_lock(&sys->lock);
441 const bool ch_desktop = sys->ch_desktop;
442 sys->ch_desktop = false;
443 vlc_mutex_unlock(&sys->lock);
446 sys->reopen_device = true;
447 vout_display_SendEventPicturesInvalid(vd);
450 /* Position Change */
451 if (sys->changes & DX_POSITION_CHANGE) {
452 #if 0 /* need that when bicubic filter is available */
456 GetClientRect(p_sys->hvideownd, &rect);
457 width = rect.right-rect.left;
458 height = rect.bottom-rect.top;
460 if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
462 msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
463 // need to reset D3D device to resize back buffer
464 if (VLC_SUCCESS != Direct3DResetDevice(vd, width, height))
468 sys->clear_scene = true;
469 sys->changes &= ~DX_POSITION_CHANGE;
474 * It initializes an instance of Direct3D9
476 static int Direct3DCreate(vout_display_t *vd)
478 vout_display_sys_t *sys = vd->sys;
480 sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
481 if (!sys->hd3d9_dll) {
482 msg_Warn(vd, "cannot load d3d9.dll, aborting");
486 LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
488 (void *)GetProcAddress(sys->hd3d9_dll, TEXT("Direct3DCreate9"));
489 if (!OurDirect3DCreate9) {
490 msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
494 /* Create the D3D object. */
495 LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
497 msg_Err(vd, "Could not create Direct3D9 instance.");
500 sys->d3dobj = d3dobj;
503 ** Get device capabilities
505 ZeroMemory(&sys->d3dcaps, sizeof(sys->d3dcaps));
506 HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
508 msg_Err(vd, "Could not read adapter capabilities. (hr=0x%lX)", hr);
512 /* TODO: need to test device capabilities and select the right render function */
513 if (!(sys->d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) ||
514 !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MAGFLINEAR)) ||
515 !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR))) {
516 msg_Err(vd, "Device does not support stretching from textures.");
524 * It releases an instance of Direct3D9
526 static void Direct3DDestroy(vout_display_t *vd)
528 vout_display_sys_t *sys = vd->sys;
531 IDirect3D9_Release(sys->d3dobj);
533 FreeLibrary(sys->hd3d9_dll);
536 sys->hd3d9_dll = NULL;
541 * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
542 * from the default adapter.
544 static int Direct3DFillPresentationParameters(vout_display_t *vd)
546 vout_display_sys_t *sys = vd->sys;
549 ** Get the current desktop display mode, so we can set up a back
550 ** buffer of the same format
552 D3DDISPLAYMODE d3ddm;
553 HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
554 D3DADAPTER_DEFAULT, &d3ddm);
556 msg_Err(vd, "Could not read adapter display mode. (hr=0x%lX)", hr);
560 /* Set up the structure used to create the D3DDevice. */
561 D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
562 ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
563 d3dpp->Flags = D3DPRESENTFLAG_VIDEO;
564 d3dpp->Windowed = TRUE;
565 d3dpp->hDeviceWindow = vd->sys->hvideownd;
566 d3dpp->BackBufferWidth = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN),
568 d3dpp->BackBufferHeight = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN),
570 d3dpp->SwapEffect = D3DSWAPEFFECT_COPY;
571 d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE;
572 d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
573 d3dpp->BackBufferFormat = d3ddm.Format;
574 d3dpp->BackBufferCount = 1;
575 d3dpp->EnableAutoDepthStencil = FALSE;
578 RECT *display = &vd->sys->rect_display;
581 display->right = d3dpp->BackBufferWidth;
582 display->bottom = d3dpp->BackBufferHeight;
588 static int Direct3DCreateResources (vout_display_t *, video_format_t *);
589 static void Direct3DDestroyResources(vout_display_t *);
592 * It creates a Direct3D device and the associated resources.
594 static int Direct3DOpen(vout_display_t *vd, video_format_t *fmt)
596 vout_display_sys_t *sys = vd->sys;
597 LPDIRECT3D9 d3dobj = sys->d3dobj;
599 if (Direct3DFillPresentationParameters(vd))
602 // Create the D3DDevice
603 LPDIRECT3DDEVICE9 d3ddev;
605 UINT AdapterToUse = D3DADAPTER_DEFAULT;
606 D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
609 // Look for 'NVIDIA PerfHUD' adapter
610 // If it is present, override default settings
611 for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
612 D3DADAPTER_IDENTIFIER9 Identifier;
614 Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
615 if (strstr(Identifier.Description,"PerfHUD") != 0) {
616 AdapterToUse = Adapter;
617 DeviceType = D3DDEVTYPE_REF;
624 D3DADAPTER_IDENTIFIER9 d3dai;
625 if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0, &d3dai))) {
626 msg_Warn(vd, "IDirect3D9_GetAdapterIdentifier failed");
628 msg_Dbg(vd, "Direct3d Device: %s %lu %lu %lu", d3dai.Description,
629 d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
632 HRESULT hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
633 DeviceType, sys->hvideownd,
634 D3DCREATE_SOFTWARE_VERTEXPROCESSING|
635 D3DCREATE_MULTITHREADED,
636 &sys->d3dpp, &d3ddev);
638 msg_Err(vd, "Could not create the D3D device! (hr=0x%lX)", hr);
641 sys->d3ddev = d3ddev;
643 UpdateRects(vd, NULL, NULL, true);
645 if (Direct3DCreateResources(vd, fmt)) {
646 msg_Err(vd, "Failed to allocate resources");
650 /* Change the window title bar text */
651 EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D output)");
653 msg_Dbg(vd, "Direct3D device adapter successfully initialized");
658 * It releases the Direct3D9 device and its resources.
660 static void Direct3DClose(vout_display_t *vd)
662 vout_display_sys_t *sys = vd->sys;
664 Direct3DDestroyResources(vd);
667 IDirect3DDevice9_Release(sys->d3ddev);
673 * It reset the Direct3D9 device and its resources.
675 static int Direct3DReset(vout_display_t *vd)
677 vout_display_sys_t *sys = vd->sys;
678 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
680 if (Direct3DFillPresentationParameters(vd))
683 /* release all D3D objects */
684 Direct3DDestroyResources(vd);
687 HRESULT hr = IDirect3DDevice9_Reset(d3ddev, &sys->d3dpp);
689 msg_Err(vd, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
693 UpdateRects(vd, NULL, NULL, true);
696 if (Direct3DCreateResources(vd, &vd->fmt)) {
697 msg_Dbg(vd, "%s failed !", __FUNCTION__);
704 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt);
705 static void Direct3DDestroyPool(vout_display_t *vd);
707 static int Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt);
708 static void Direct3DDestroyScene(vout_display_t *vd);
711 * It creates the picture and scene resources.
713 static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
715 vout_display_sys_t *sys = vd->sys;
717 if (Direct3DCreatePool(vd, fmt)) {
718 msg_Err(vd, "Direct3D picture pool initialization failed");
721 if (Direct3DCreateScene(vd, fmt)) {
722 msg_Err(vd, "Direct3D scene initialization failed !");
725 sys->d3dregion_format = D3DFMT_UNKNOWN;
726 for (int i = 0; i < 2; i++) {
727 D3DFORMAT fmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
728 if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->d3dobj,
731 sys->d3dpp.BackBufferFormat,
735 sys->d3dregion_format = fmt;
742 * It destroys the picture and scene resources.
744 static void Direct3DDestroyResources(vout_display_t *vd)
746 Direct3DDestroyScene(vd);
747 Direct3DDestroyPool(vd);
751 * It tests if the conversion from src to dst is supported.
753 static int Direct3DCheckConversion(vout_display_t *vd,
754 D3DFORMAT src, D3DFORMAT dst)
756 vout_display_sys_t *sys = vd->sys;
757 LPDIRECT3D9 d3dobj = sys->d3dobj;
760 /* test whether device can create a surface of that format */
761 hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
762 D3DDEVTYPE_HAL, dst, 0,
763 D3DRTYPE_SURFACE, src);
765 /* test whether device can perform color-conversion
766 ** from that format to target format
768 hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
773 if (!SUCCEEDED(hr)) {
774 if (D3DERR_NOTAVAILABLE != hr)
775 msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
784 D3DFORMAT format; /* D3D format */
785 vlc_fourcc_t fourcc; /* VLC fourcc */
791 static const d3d_format_t d3d_formats[] = {
792 /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
793 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_YV12, 0,0,0 },
794 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_I420, 0,0,0 },
795 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_J420, 0,0,0 },
796 { "UYVY", D3DFMT_UYVY, VLC_CODEC_UYVY, 0,0,0 },
797 { "YUY2", D3DFMT_YUY2, VLC_CODEC_YUYV, 0,0,0 },
798 { "X8R8G8B8", D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
799 { "A8R8G8B8", D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
800 { "8G8B8", D3DFMT_R8G8B8, VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
801 { "R5G6B5", D3DFMT_R5G6B5, VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5, 0x1f<<0 },
802 { "X1R5G5B5", D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5, 0x1f<<0 },
808 * It returns the format (closest to chroma) that can be converted to target */
809 static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
811 vout_display_sys_t *sys = vd->sys;
813 for (unsigned pass = 0; pass < 2; pass++) {
814 const vlc_fourcc_t *list;
816 if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
817 list = vlc_fourcc_GetYUVFallback(chroma);
819 list = vlc_fourcc_GetRGBFallback(chroma);
823 for (unsigned i = 0; list[i] != 0; i++) {
824 for (unsigned j = 0; d3d_formats[j].name; j++) {
825 const d3d_format_t *format = &d3d_formats[j];
827 if (format->fourcc != list[i])
830 msg_Warn(vd, "trying surface pixel format: %s",
832 if (!Direct3DCheckConversion(vd, format->format, target)) {
833 msg_Dbg(vd, "selected surface pixel format is %s",
844 * It locks the surface associated to the picture and get the surface
845 * descriptor which amongst other things has the pointer to the picture
846 * data and its pitch.
848 static int Direct3DLockSurface(picture_t *picture)
850 /* Lock the surface to get a valid pointer to the picture buffer */
851 D3DLOCKED_RECT d3drect;
852 HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
854 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
855 return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
858 CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
862 * It unlocks the surface associated to the picture.
864 static void Direct3DUnlockSurface(picture_t *picture)
866 /* Unlock the Surface */
867 HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
869 //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
874 * It creates the pool of picture (only 1).
876 * Each picture has an associated offscreen surface in video memory
877 * depending on hardware capabilities the picture chroma will be as close
878 * as possible to the orginal render chroma to reduce CPU conversion overhead
879 * and delegate this work to video card GPU
881 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt)
883 vout_display_sys_t *sys = vd->sys;
884 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
889 /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
890 * the requested chroma which is usable by the hardware in an offscreen surface, as they
891 * typically support more formats than textures */
892 const d3d_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, sys->d3dpp.BackBufferFormat);
894 msg_Err(vd, "surface pixel format is not supported.");
897 fmt->i_chroma = d3dfmt->fourcc;
898 fmt->i_rmask = d3dfmt->rmask;
899 fmt->i_gmask = d3dfmt->gmask;
900 fmt->i_bmask = d3dfmt->bmask;
902 /* We create one picture.
903 * It is useless to create more as we can't be used for direct rendering */
905 /* Create a surface */
906 LPDIRECT3DSURFACE9 surface;
907 HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
915 msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
918 /* fill surface with black color */
919 IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
921 /* Create the associated picture */
922 picture_resource_t *rsc = &sys->resource;
923 rsc->p_sys = malloc(sizeof(*rsc->p_sys));
925 IDirect3DSurface9_Release(surface);
928 rsc->p_sys->surface = surface;
929 rsc->p_sys->fallback = NULL;
930 for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
931 rsc->p[i].p_pixels = NULL;
932 rsc->p[i].i_pitch = 0;
933 rsc->p[i].i_lines = fmt->i_height / (i > 0 ? 2 : 1);
935 picture_t *picture = picture_NewFromResource(fmt, rsc);
937 IDirect3DSurface9_Release(surface);
942 /* Wrap it into a picture pool */
943 picture_pool_configuration_t pool_cfg;
944 memset(&pool_cfg, 0, sizeof(pool_cfg));
945 pool_cfg.picture_count = 1;
946 pool_cfg.picture = &picture;
947 pool_cfg.lock = Direct3DLockSurface;
948 pool_cfg.unlock = Direct3DUnlockSurface;
950 sys->pool = picture_pool_NewExtended(&pool_cfg);
952 picture_Release(picture);
953 IDirect3DSurface9_Release(surface);
959 * It destroys the pool of picture and its resources.
961 static void Direct3DDestroyPool(vout_display_t *vd)
963 vout_display_sys_t *sys = vd->sys;
966 picture_resource_t *rsc = &sys->resource;
967 IDirect3DSurface9_Release(rsc->p_sys->surface);
968 if (rsc->p_sys->fallback)
969 picture_Release(rsc->p_sys->fallback);
970 picture_pool_Delete(sys->pool);
976 * It allocates and initializes the resources needed to render the scene.
978 static int Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt)
980 vout_display_sys_t *sys = vd->sys;
981 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
985 * Create a texture for use when rendering a scene
986 * for performance reason, texture format is identical to backbuffer
987 * which would usually be a RGB format
989 LPDIRECT3DTEXTURE9 d3dtex;
990 hr = IDirect3DDevice9_CreateTexture(d3ddev,
994 D3DUSAGE_RENDERTARGET,
995 sys->d3dpp.BackBufferFormat,
1000 msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
1001 return VLC_EGENERIC;
1005 ** Create a vertex buffer for use when rendering scene
1007 LPDIRECT3DVERTEXBUFFER9 d3dvtc;
1008 hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
1009 sizeof(CUSTOMVERTEX)*4,
1010 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1011 D3DFVF_CUSTOMVERTEX,
1016 msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1017 IDirect3DTexture9_Release(d3dtex);
1018 return VLC_EGENERIC;
1022 sys->d3dtex = d3dtex;
1023 sys->d3dvtc = d3dvtc;
1025 sys->d3dregion_count = 0;
1026 sys->d3dregion = NULL;
1028 sys->clear_scene = true;
1030 // Texture coordinates outside the range [0.0, 1.0] are set
1031 // to the texture color at 0.0 or 1.0, respectively.
1032 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1033 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1035 // Set linear filtering quality
1036 if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
1037 msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
1038 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1040 msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
1041 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
1043 if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
1044 msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
1045 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1047 msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
1048 IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
1051 // set maximum ambient light
1052 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1055 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1057 // Turn off the zbuffer
1058 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1061 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
1064 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
1067 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
1070 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1071 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1072 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1074 if (sys->d3dcaps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
1075 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1076 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
1077 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1080 // Set texture states
1081 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
1082 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1084 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
1085 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1086 IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1088 msg_Dbg(vd, "Direct3D scene created successfully");
1094 * It releases the scene resources.
1096 static void Direct3DDestroyScene(vout_display_t *vd)
1098 vout_display_sys_t *sys = vd->sys;
1100 Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
1102 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1104 IDirect3DVertexBuffer9_Release(d3dvtc);
1106 LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1108 IDirect3DTexture9_Release(d3dtex);
1113 sys->d3dregion_count = 0;
1114 sys->d3dregion = NULL;
1116 msg_Dbg(vd, "Direct3D scene released successfully");
1119 static void Direct3DSetupVertices(CUSTOMVERTEX *vertices,
1120 const RECT src_full,
1121 const RECT src_crop,
1125 const float src_full_width = src_full.right - src_full.left;
1126 const float src_full_height = src_full.bottom - src_full.top;
1127 vertices[0].x = dst.left;
1128 vertices[0].y = dst.top;
1129 vertices[0].tu = src_crop.left / src_full_width;
1130 vertices[0].tv = src_crop.top / src_full_height;
1132 vertices[1].x = dst.right;
1133 vertices[1].y = dst.top;
1134 vertices[1].tu = src_crop.right / src_full_width;
1135 vertices[1].tv = src_crop.top / src_full_height;
1137 vertices[2].x = dst.right;
1138 vertices[2].y = dst.bottom;
1139 vertices[2].tu = src_crop.right / src_full_width;
1140 vertices[2].tv = src_crop.bottom / src_full_height;
1142 vertices[3].x = dst.left;
1143 vertices[3].y = dst.bottom;
1144 vertices[3].tu = src_crop.left / src_full_width;
1145 vertices[3].tv = src_crop.bottom / src_full_height;
1147 for (int i = 0; i < 4; i++) {
1148 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1149 /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1150 vertices[i].x -= 0.5;
1151 vertices[i].y -= 0.5;
1153 vertices[i].z = 0.0f;
1154 vertices[i].rhw = 1.0f;
1155 vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
1160 * It copies picture surface into a texture and setup the associated d3d_region_t.
1162 static int Direct3DImportPicture(vout_display_t *vd,
1163 d3d_region_t *region,
1164 LPDIRECT3DSURFACE9 source)
1166 vout_display_sys_t *sys = vd->sys;
1170 msg_Dbg(vd, "no surface to render ?");
1171 return VLC_EGENERIC;
1174 /* retrieve texture top-level surface */
1175 LPDIRECT3DSURFACE9 destination;
1176 hr = IDirect3DTexture9_GetSurfaceLevel(sys->d3dtex, 0, &destination);
1178 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1179 return VLC_EGENERIC;
1182 /* Copy picture surface into texture surface
1183 * color space conversion happen here */
1184 hr = IDirect3DDevice9_StretchRect(sys->d3ddev, source, NULL, destination, NULL, D3DTEXF_LINEAR);
1185 IDirect3DSurface9_Release(destination);
1187 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1188 return VLC_EGENERIC;
1192 region->texture = sys->d3dtex;
1193 Direct3DSetupVertices(region->vertex,
1195 vd->sys->rect_src_clipped,
1196 vd->sys->rect_dest_clipped, 255);
1200 static void Direct3DDeleteRegions(int count, d3d_region_t *region)
1202 for (int i = 0; i < count; i++) {
1203 if (region[i].texture)
1204 IDirect3DTexture9_Release(region[i].texture);
1209 static void Direct3DImportSubpicture(vout_display_t *vd,
1210 int *count_ptr, d3d_region_t **region,
1211 subpicture_t *subpicture)
1213 vout_display_sys_t *sys = vd->sys;
1216 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1220 *region = calloc(count, sizeof(**region));
1221 if (*region == NULL) {
1227 for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
1228 d3d_region_t *d3dr = &(*region)[i];
1231 d3dr->texture = NULL;
1232 for (int j = 0; j < sys->d3dregion_count; j++) {
1233 d3d_region_t *cache = &sys->d3dregion[j];
1234 if (cache->texture &&
1235 cache->format == sys->d3dregion_format &&
1236 cache->width == r->fmt.i_visible_width &&
1237 cache->height == r->fmt.i_visible_height) {
1239 msg_Dbg(vd, "Reusing %dx%d texture for OSD",
1240 cache->width, cache->height);
1243 memset(cache, 0, sizeof(*cache));
1246 if (!d3dr->texture) {
1247 d3dr->format = sys->d3dregion_format;
1248 d3dr->width = r->fmt.i_visible_width;
1249 d3dr->height = r->fmt.i_visible_height;
1250 hr = IDirect3DDevice9_CreateTexture(sys->d3ddev,
1251 d3dr->width, d3dr->height,
1259 d3dr->texture = NULL;
1260 msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%0lX)",
1261 d3dr->width, d3dr->height, hr);
1264 msg_Dbg(vd, "Created %dx%d texture for OSD",
1265 r->fmt.i_visible_width, r->fmt.i_visible_height);
1268 D3DLOCKED_RECT lock;
1269 hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
1270 if (SUCCEEDED(hr)) {
1271 uint8_t *dst_data = lock.pBits;
1272 int dst_pitch = lock.Pitch;
1273 const int src_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
1274 r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1275 uint8_t *src_data = &r->p_picture->p->p_pixels[src_offset];
1276 int src_pitch = r->p_picture->p->i_pitch;
1277 for (unsigned y = 0; y < r->fmt.i_visible_height; y++) {
1278 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_visible_pitch);
1279 if (d3dr->format == D3DFMT_A8B8G8R8) {
1280 memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch],
1283 for (int x = 0; x < copy_pitch; x += 4) {
1284 dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
1285 dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
1286 dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
1287 dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
1291 hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
1293 msg_Err(vd, "Failed to unlock the texture");
1295 msg_Err(vd, "Failed to lock the texture");
1298 /* Map the subpicture to sys->rect_dest */
1301 src.right = src.left + r->fmt.i_visible_width;
1303 src.bottom = src.top + r->fmt.i_visible_height;
1305 const RECT video = sys->rect_dest;
1306 const float scale_w = (float)(video.right - video.left) / subpicture->i_original_picture_width;
1307 const float scale_h = (float)(video.bottom - video.top) / subpicture->i_original_picture_height;
1310 dst.left = video.left + scale_w * r->i_x,
1311 dst.right = dst.left + scale_w * r->fmt.i_visible_width,
1312 dst.top = video.top + scale_h * r->i_y,
1313 dst.bottom = dst.top + scale_h * r->fmt.i_visible_height,
1314 Direct3DSetupVertices(d3dr->vertex,
1316 subpicture->i_alpha * r->i_alpha / 255);
1320 static int Direct3DRenderRegion(vout_display_t *vd,
1321 d3d_region_t *region)
1323 vout_display_sys_t *sys = vd->sys;
1325 LPDIRECT3DDEVICE9 d3ddev = vd->sys->d3ddev;
1327 LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1328 LPDIRECT3DTEXTURE9 d3dtex = region->texture;
1332 /* Import vertices */
1334 hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertex, D3DLOCK_DISCARD);
1336 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1339 memcpy(vertex, region->vertex, sizeof(region->vertex));
1340 hr = IDirect3DVertexBuffer9_Unlock(d3dvtc);
1342 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1346 // Setup our texture. Using textures introduces the texture stage states,
1347 // which govern how textures get blended together (in the case of multiple
1348 // textures) and lighting information. In this case, we are modulating
1349 // (blending) our texture with the diffuse color of the vertices.
1350 hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1352 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1356 // Render the vertex buffer contents
1357 hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1359 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1363 // we use FVF instead of vertex shader
1364 hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1366 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1371 hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1373 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1380 * It renders the scene.
1382 * This function is intented for higher end 3D cards, with pixel shader support
1383 * and at least 64 MiB of video RAM.
1385 static void Direct3DRenderScene(vout_display_t *vd,
1386 d3d_region_t *picture,
1387 int subpicture_count,
1388 d3d_region_t *subpicture)
1390 vout_display_sys_t *sys = vd->sys;
1391 LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1394 if (sys->clear_scene) {
1395 /* Clear the backbuffer and the zbuffer */
1396 hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1397 D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1399 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1402 sys->clear_scene = false;
1406 hr = IDirect3DDevice9_BeginScene(d3ddev);
1408 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1412 Direct3DRenderRegion(vd, picture);
1414 if (subpicture_count > 0)
1415 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1416 for (int i = 0; i < subpicture_count; i++) {
1417 d3d_region_t *r = &subpicture[i];
1419 Direct3DRenderRegion(vd, r);
1421 if (subpicture_count > 0)
1422 IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1425 hr = IDirect3DDevice9_EndScene(d3ddev);
1427 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1432 /*****************************************************************************
1433 * DesktopCallback: desktop mode variable callback
1434 *****************************************************************************/
1435 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1436 vlc_value_t oldval, vlc_value_t newval,
1439 vout_display_t *vd = (vout_display_t *)object;
1440 vout_display_sys_t *sys = vd->sys;
1441 VLC_UNUSED(psz_cmd);
1445 vlc_mutex_lock(&sys->lock);
1446 const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1447 sys->ch_desktop |= ch_desktop;
1448 sys->desktop_requested = newval.b_bool;
1449 vlc_mutex_unlock(&sys->lock);
1451 /* FIXME we should have a way to export variable to be saved */
1453 playlist_t *p_playlist = pl_Get(vd);
1454 /* Modify playlist as well because the vout might have to be
1456 var_Create(p_playlist, "video-wallpaper", VLC_VAR_BOOL);
1457 var_SetBool(p_playlist, "video-wallpaper", newval.b_bool);