]> git.sesse.net Git - vlc/blob - modules/video_output/msw/direct3d.c
Used a CPU memory fallback when Direct3DLockSurface/DirectXLock fails.
[vlc] / modules / video_output / msw / direct3d.c
1 /*****************************************************************************
2  * direct3d.c: Windows Direct3D video output module
3  *****************************************************************************
4  * Copyright (C) 2006-2009 the VideoLAN team
5  *$Id$
6  *
7  * Authors: Damien Fouilleul <damienf@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble:
26  *
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.
30  *
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.
34  *
35  *****************************************************************************/
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_playlist.h>
43 #include <vlc_vout_display.h>
44
45 #include <windows.h>
46 #include <d3d9.h>
47
48 #include "common.h"
49
50 /*****************************************************************************
51  * Module descriptor
52  *****************************************************************************/
53 static int  OpenVideoXP(vlc_object_t *);
54 static int  OpenVideoVista(vlc_object_t *);
55 static void Close(vlc_object_t *);
56
57 #define DESKTOP_TEXT N_("Enable desktop mode ")
58 #define DESKTOP_LONGTEXT N_(\
59     "The desktop mode allows you to display the video on the desktop.")
60
61 #define D3D_HELP N_("Recommended video output for Windows Vista and later versions")
62
63 vlc_module_begin ()
64     set_shortname("Direct3D")
65     set_description(N_("Direct3D video output"))
66     set_help(D3D_HELP)
67     set_category(CAT_VIDEO)
68     set_subcategory(SUBCAT_VIDEO_VOUT)
69
70     add_bool("direct3d-desktop", false, NULL, DESKTOP_TEXT, DESKTOP_LONGTEXT, true)
71
72     set_capability("vout display", 150)
73     add_shortcut("direct3d")
74     set_callbacks(OpenVideoVista, Close)
75
76     add_submodule()
77         set_description(N_("Direct3D video output (XP)"))
78         set_capability("vout display", 70)
79         add_shortcut("direct3d_xp")
80         set_callbacks(OpenVideoXP, Close)
81
82 vlc_module_end ()
83
84 /*****************************************************************************
85  * Local prototypes.
86  *****************************************************************************/
87 struct picture_sys_t
88 {
89     LPDIRECT3DSURFACE9 surface;
90     picture_t          *fallback;
91 };
92
93 static int  Open(vlc_object_t *);
94
95 static picture_pool_t *Pool  (vout_display_t *, unsigned);
96 static void           Prepare(vout_display_t *, picture_t *);
97 static void           Display(vout_display_t *, picture_t *);
98 static int            Control(vout_display_t *, int, va_list);
99 static void           Manage (vout_display_t *);
100
101 static int  Direct3DCreate (vout_display_t *);
102 static int  Direct3DReset  (vout_display_t *);
103 static void Direct3DDestroy(vout_display_t *);
104
105 static int  Direct3DOpen (vout_display_t *, video_format_t *);
106 static void Direct3DClose(vout_display_t *);
107
108 static void Direct3DRenderScene(vout_display_t *vd, LPDIRECT3DSURFACE9 surface);
109
110 /* */
111 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
112
113 /**
114  * It creates a Direct3D vout display.
115  */
116 static int Open(vlc_object_t *object)
117 {
118     vout_display_t *vd = (vout_display_t *)object;
119     vout_display_sys_t *sys;
120
121     /* Allocate structure */
122     vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
123     if (!sys)
124         return VLC_ENOMEM;
125
126     if (Direct3DCreate(vd)) {
127         msg_Err(vd, "Direct3D could not be initialized");
128         Direct3DDestroy(vd);
129         free(sys);
130         return VLC_EGENERIC;
131     }
132
133     sys->use_desktop = var_CreateGetBool(vd, "direct3d-desktop");
134     sys->reset_device = false;
135     sys->reset_device = false;
136     sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
137     sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
138     sys->desktop_save.is_on_top     = false;
139     sys->desktop_save.win.left      = var_InheritInteger(vd, "video-x");
140     sys->desktop_save.win.right     = vd->cfg->display.width;
141     sys->desktop_save.win.top       = var_InheritInteger(vd, "video-y");
142     sys->desktop_save.win.bottom    = vd->cfg->display.height;
143
144     if (CommonInit(vd))
145         goto error;
146
147     /* */
148     video_format_t fmt;
149     if (Direct3DOpen(vd, &fmt)) {
150         msg_Err(vd, "Direct3D could not be opened");
151         goto error;
152     }
153
154     /* */
155     vout_display_info_t info = vd->info;
156     info.is_slow = true;
157     info.has_double_click = true;
158     info.has_hide_mouse = false;
159     info.has_pictures_invalid = true;
160     info.has_event_thread = true;
161
162     /* Interaction */
163     vlc_mutex_init(&sys->lock);
164     sys->ch_desktop = false;
165     sys->desktop_requested = sys->use_desktop;
166
167     vlc_value_t val;
168     val.psz_string = _("Desktop");
169     var_Change(vd, "direct3d-desktop", VLC_VAR_SETTEXT, &val, NULL);
170     var_AddCallback(vd, "direct3d-desktop", DesktopCallback, NULL);
171
172     /* Setup vout_display now that everything is fine */
173     vd->fmt  = fmt;
174     vd->info = info;
175
176     vd->pool    = Pool;
177     vd->prepare = Prepare;
178     vd->display = Display;
179     vd->control = Control;
180     vd->manage  = Manage;
181
182     /* Fix state in case of desktop mode */
183     if (sys->use_desktop && vd->cfg->is_fullscreen)
184         vout_display_SendEventFullscreen(vd, false);
185
186     return VLC_SUCCESS;
187 error:
188     Direct3DClose(vd);
189     CommonClean(vd);
190     Direct3DDestroy(vd);
191     free(vd->sys);
192     return VLC_EGENERIC;
193 }
194
195 static bool IsVistaOrAbove(void)
196 {
197     OSVERSIONINFO winVer;
198     winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
199     return GetVersionEx(&winVer) && winVer.dwMajorVersion > 5;
200 }
201
202 static int OpenVideoXP(vlc_object_t *obj)
203 {
204     /* Windows XP or lower, make sure this module isn't the default */
205     return IsVistaOrAbove() ? VLC_EGENERIC : Open(obj);
206 }
207
208 static int OpenVideoVista(vlc_object_t *obj)
209 {
210     /* Windows Vista or above, make this module the default */
211     return IsVistaOrAbove() ? Open(obj) : VLC_EGENERIC;
212 }
213
214 /**
215  * It destroyes a Direct3D vout display.
216  */
217 static void Close(vlc_object_t *object)
218 {
219     vout_display_t * vd = (vout_display_t *)object;
220
221     var_DelCallback(vd, "direct3d-desktop", DesktopCallback, NULL);
222
223     Direct3DClose(vd);
224
225     CommonClean(vd);
226
227     Direct3DDestroy(vd);
228
229     free(vd->sys);
230 }
231
232 /* */
233 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
234 {
235     VLC_UNUSED(count);
236     return vd->sys->pool;
237 }
238
239 static int  Direct3DLockSurface(picture_t *);
240 static void Direct3DUnlockSurface(picture_t *);
241
242 static void Prepare(vout_display_t *vd, picture_t *picture)
243 {
244     LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
245 #if 0
246     picture_Release(picture);
247     Direct3DRenderScene(vd, surface);
248 #else
249     /* FIXME it is a bit ugly, we need the surface to be unlocked for
250      * rendering.
251      *  The clean way would be to release the picture (and ensure that
252      * the vout doesn't keep a reference). But because of the vout
253      * wrapper, we can't */
254
255     Direct3DUnlockSurface(picture);
256
257     Direct3DRenderScene(vd, surface);
258
259     Direct3DLockSurface(picture);
260 #endif
261 }
262
263 static void Display(vout_display_t *vd, picture_t *picture)
264 {
265     vout_display_sys_t *sys = vd->sys;
266     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
267
268     // Present the back buffer contents to the display
269     // No stretching should happen here !
270     const RECT src = sys->rect_dest_clipped;
271     const RECT dst = sys->rect_dest_clipped;
272     HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
273     if (FAILED(hr)) {
274         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
275     }
276 #if 0
277     VLC_UNUSED(picture);
278 #else
279     /* XXX See Prepare() */
280     picture_Release(picture);
281 #endif
282
283     CommonDisplay(vd);
284 }
285 static int ControlResetDevice(vout_display_t *vd)
286 {
287     return Direct3DReset(vd);
288 }
289 static int ControlReopenDevice(vout_display_t *vd)
290 {
291     vout_display_sys_t *sys = vd->sys;
292
293     if (!sys->use_desktop) {
294         /* Save non-desktop state */
295         sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
296         sys->desktop_save.is_on_top     = sys->is_on_top;
297
298         WINDOWPLACEMENT wp = { .length = sizeof(wp), };
299         GetWindowPlacement(sys->hparent ? sys->hparent : sys->hwnd, &wp);
300         sys->desktop_save.win = wp.rcNormalPosition;
301     }
302
303     /* */
304     Direct3DClose(vd);
305     EventThreadStop(sys->event);
306
307     /* */
308     vlc_mutex_lock(&sys->lock);
309     sys->use_desktop = sys->desktop_requested;
310     sys->ch_desktop = false;
311     vlc_mutex_unlock(&sys->lock);
312
313     /* */
314     event_cfg_t cfg;
315     memset(&cfg, 0, sizeof(cfg));
316     cfg.use_desktop = sys->use_desktop;
317     if (!sys->use_desktop) {
318         cfg.win.type   = VOUT_WINDOW_TYPE_HWND;
319         cfg.win.x      = sys->desktop_save.win.left;
320         cfg.win.y      = sys->desktop_save.win.top;
321         cfg.win.width  = sys->desktop_save.win.right  - sys->desktop_save.win.left;
322         cfg.win.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
323     }
324
325     event_hwnd_t hwnd;
326     if (EventThreadStart(sys->event, &hwnd, &cfg)) {
327         msg_Err(vd, "Failed to restart event thread");
328         return VLC_EGENERIC;
329     }
330     sys->parent_window = hwnd.parent_window;
331     sys->hparent       = hwnd.hparent;
332     sys->hwnd          = hwnd.hwnd;
333     sys->hvideownd     = hwnd.hvideownd;
334     sys->hfswnd        = hwnd.hfswnd;
335     SetRectEmpty(&sys->rect_parent);
336
337     /* */
338     video_format_t fmt;
339     if (Direct3DOpen(vd, &fmt)) {
340         CommonClean(vd);
341         msg_Err(vd, "Failed to reopen device");
342         return VLC_EGENERIC;
343     }
344     vd->fmt = fmt;
345     sys->is_first_display = true;
346
347     if (sys->use_desktop) {
348         /* Disable fullscreen/on_top while using desktop */
349         if (sys->desktop_save.is_fullscreen)
350             vout_display_SendEventFullscreen(vd, false);
351         if (sys->desktop_save.is_on_top)
352             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
353     } else {
354         /* Restore fullscreen/on_top */
355         if (sys->desktop_save.is_fullscreen)
356             vout_display_SendEventFullscreen(vd, true);
357         if (sys->desktop_save.is_on_top)
358             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
359     }
360     return VLC_SUCCESS;
361 }
362 static int Control(vout_display_t *vd, int query, va_list args)
363 {
364     vout_display_sys_t *sys = vd->sys;
365
366     switch (query) {
367     case VOUT_DISPLAY_RESET_PICTURES:
368         /* FIXME what to do here in case of failure */
369         if (sys->reset_device) {
370             if (ControlResetDevice(vd)) {
371                 msg_Err(vd, "Failed to reset device");
372                 return VLC_EGENERIC;
373             }
374             sys->reset_device = false;
375         } else if(sys->reopen_device) {
376             if (ControlReopenDevice(vd)) {
377                 msg_Err(vd, "Failed to reopen device");
378                 return VLC_EGENERIC;
379             }
380             sys->reopen_device = false;
381         }
382         return VLC_SUCCESS;
383     default:
384         return CommonControl(vd, query, args);
385     }
386 }
387 static void Manage (vout_display_t *vd)
388 {
389     vout_display_sys_t *sys = vd->sys;
390
391     CommonManage(vd);
392
393     /* Desktop mode change */
394     vlc_mutex_lock(&sys->lock);
395     const bool ch_desktop = sys->ch_desktop;
396     sys->ch_desktop = false;
397     vlc_mutex_unlock(&sys->lock);
398
399     if (ch_desktop) {
400         sys->reopen_device = true;
401         vout_display_SendEventPicturesInvalid(vd);
402     }
403
404 #if 0
405     /*
406      * Position Change
407      */
408     if (sys->changes & DX_POSITION_CHANGE) {
409 #if 0 /* need that when bicubic filter is available */
410         RECT rect;
411         UINT width, height;
412
413         GetClientRect(p_sys->hvideownd, &rect);
414         width  = rect.right-rect.left;
415         height = rect.bottom-rect.top;
416
417         if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
418         {
419             msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
420             // need to reset D3D device to resize back buffer
421             if (VLC_SUCCESS != Direct3DResetDevice(vd, width, height))
422                 return VLC_EGENERIC;
423         }
424 #endif
425         sys->changes &= ~DX_POSITION_CHANGE;
426     }
427 #endif
428 }
429
430 /**
431  * It initializes an instance of Direct3D9
432  */
433 static int Direct3DCreate(vout_display_t *vd)
434 {
435     vout_display_sys_t *sys = vd->sys;
436
437     sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
438     if (!sys->hd3d9_dll) {
439         msg_Warn(vd, "cannot load d3d9.dll, aborting");
440         return VLC_EGENERIC;
441     }
442
443     LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
444     OurDirect3DCreate9 =
445         (void *)GetProcAddress(sys->hd3d9_dll, TEXT("Direct3DCreate9"));
446     if (!OurDirect3DCreate9) {
447         msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
448         return VLC_EGENERIC;
449     }
450
451     /* Create the D3D object. */
452     LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
453     if (!d3dobj) {
454        msg_Err(vd, "Could not create Direct3D9 instance.");
455        return VLC_EGENERIC;
456     }
457     sys->d3dobj = d3dobj;
458
459     /*
460     ** Get device capabilities
461     */
462     D3DCAPS9 d3dCaps;
463     ZeroMemory(&d3dCaps, sizeof(d3dCaps));
464     HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
465     if (FAILED(hr)) {
466        msg_Err(vd, "Could not read adapter capabilities. (hr=0x%lX)", hr);
467        return VLC_EGENERIC;
468     }
469     /* TODO: need to test device capabilities and select the right render function */
470
471     return VLC_SUCCESS;
472 }
473
474 /**
475  * It releases an instance of Direct3D9
476  */
477 static void Direct3DDestroy(vout_display_t *vd)
478 {
479     vout_display_sys_t *sys = vd->sys;
480
481     if (sys->d3dobj)
482        IDirect3D9_Release(sys->d3dobj);
483     if (sys->hd3d9_dll)
484         FreeLibrary(sys->hd3d9_dll);
485
486     sys->d3dobj = NULL;
487     sys->hd3d9_dll = NULL;
488 }
489
490
491 /**
492  * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
493  * from the default adapter.
494  */
495 static int Direct3DFillPresentationParameters(vout_display_t *vd)
496 {
497     vout_display_sys_t *sys = vd->sys;
498
499     /*
500     ** Get the current desktop display mode, so we can set up a back
501     ** buffer of the same format
502     */
503     D3DDISPLAYMODE d3ddm;
504     HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
505                                                   D3DADAPTER_DEFAULT, &d3ddm);
506     if (FAILED(hr)) {
507        msg_Err(vd, "Could not read adapter display mode. (hr=0x%lX)", hr);
508        return VLC_EGENERIC;
509     }
510
511     /* Set up the structure used to create the D3DDevice. */
512     D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
513     ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
514     d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
515     d3dpp->Windowed               = TRUE;
516     d3dpp->hDeviceWindow          = vd->sys->hvideownd;
517     d3dpp->BackBufferWidth        = __MAX(GetSystemMetrics(SM_CXVIRTUALSCREEN),
518                                           d3ddm.Width);
519     d3dpp->BackBufferHeight       = __MAX(GetSystemMetrics(SM_CYVIRTUALSCREEN),
520                                           d3ddm.Height);
521     d3dpp->SwapEffect             = D3DSWAPEFFECT_COPY;
522     d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
523     d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
524     d3dpp->BackBufferFormat       = d3ddm.Format;
525     d3dpp->BackBufferCount        = 1;
526     d3dpp->EnableAutoDepthStencil = FALSE;
527
528     /* */
529     RECT *display = &vd->sys->rect_display;
530     display->left   = 0;
531     display->top    = 0;
532     display->right  = d3dpp->BackBufferWidth;
533     display->bottom = d3dpp->BackBufferHeight;
534
535     return VLC_SUCCESS;
536 }
537
538 /* */
539 static int  Direct3DCreateResources (vout_display_t *, video_format_t *);
540 static void Direct3DDestroyResources(vout_display_t *);
541
542 /**
543  * It creates a Direct3D device and the associated resources.
544  */
545 static int Direct3DOpen(vout_display_t *vd, video_format_t *fmt)
546 {
547     vout_display_sys_t *sys = vd->sys;
548     LPDIRECT3D9 d3dobj = sys->d3dobj;
549
550     if (Direct3DFillPresentationParameters(vd))
551         return VLC_EGENERIC;
552
553     // Create the D3DDevice
554     LPDIRECT3DDEVICE9 d3ddev;
555     HRESULT hr = IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT,
556                                          D3DDEVTYPE_HAL, sys->hvideownd,
557                                          D3DCREATE_SOFTWARE_VERTEXPROCESSING|
558                                          D3DCREATE_MULTITHREADED,
559                                          &sys->d3dpp, &d3ddev);
560     if (FAILED(hr)) {
561        msg_Err(vd, "Could not create the D3D device! (hr=0x%lX)", hr);
562        return VLC_EGENERIC;
563     }
564     sys->d3ddev = d3ddev;
565
566     UpdateRects(vd, NULL, NULL, true);
567
568     if (Direct3DCreateResources(vd, fmt)) {
569         msg_Err(vd, "Failed to allocate resources");
570         return VLC_EGENERIC;
571     }
572
573     /* Change the window title bar text */
574     EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D output)");
575
576     msg_Dbg(vd, "Direct3D device adapter successfully initialized");
577     return VLC_SUCCESS;
578 }
579
580 /**
581  * It releases the Direct3D9 device and its resources.
582  */
583 static void Direct3DClose(vout_display_t *vd)
584 {
585     vout_display_sys_t *sys = vd->sys;
586
587     Direct3DDestroyResources(vd);
588
589     if (sys->d3ddev)
590        IDirect3DDevice9_Release(sys->d3ddev);
591
592     sys->d3ddev = NULL;
593 }
594
595 /**
596  * It reset the Direct3D9 device and its resources.
597  */
598 static int Direct3DReset(vout_display_t *vd)
599 {
600     vout_display_sys_t *sys = vd->sys;
601     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
602
603     if (Direct3DFillPresentationParameters(vd))
604         return VLC_EGENERIC;
605
606     /* release all D3D objects */
607     Direct3DDestroyResources(vd);
608
609     /* */
610     HRESULT hr = IDirect3DDevice9_Reset(d3ddev, &sys->d3dpp);
611     if (FAILED(hr)) {
612         msg_Err(vd, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
613         return VLC_EGENERIC;
614     }
615
616     UpdateRects(vd, NULL, NULL, true);
617
618     /* re-create them */
619     if (Direct3DCreateResources(vd, &vd->fmt)) {
620         msg_Dbg(vd, "%s failed !", __FUNCTION__);
621         return VLC_EGENERIC;
622     }
623     return VLC_SUCCESS;
624 }
625
626 /* */
627 static int  Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt);
628 static void Direct3DDestroyPool(vout_display_t *vd);
629
630 static int  Direct3DCreateScene(vout_display_t *vd);
631 static void Direct3DDestroyScene(vout_display_t *vd);
632
633 /**
634  * It creates the picture and scene resources.
635  */
636 static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
637 {
638     if (Direct3DCreatePool(vd, fmt)) {
639         msg_Err(vd, "Direct3D picture pool initialization failed");
640         return VLC_EGENERIC;
641     }
642     if (Direct3DCreateScene(vd)) {
643         msg_Err(vd, "Direct3D scene initialization failed !");
644         return VLC_EGENERIC;
645     }
646     return VLC_SUCCESS;
647 }
648 /**
649  * It destroys the picture and scene resources.
650  */
651 static void Direct3DDestroyResources(vout_display_t *vd)
652 {
653     Direct3DDestroyScene(vd);
654     Direct3DDestroyPool(vd);
655 }
656
657 /**
658  * It tests if the conversion from src to dst is supported.
659  */
660 static int Direct3DCheckConversion(vout_display_t *vd,
661                                    D3DFORMAT src, D3DFORMAT dst)
662 {
663     vout_display_sys_t *sys = vd->sys;
664     LPDIRECT3D9 d3dobj = sys->d3dobj;
665     HRESULT hr;
666
667     /* test whether device can create a surface of that format */
668     hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
669                                       D3DDEVTYPE_HAL, dst, 0,
670                                       D3DRTYPE_SURFACE, src);
671     if (SUCCEEDED(hr)) {
672         /* test whether device can perform color-conversion
673         ** from that format to target format
674         */
675         hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
676                                                     D3DADAPTER_DEFAULT,
677                                                     D3DDEVTYPE_HAL,
678                                                     src, dst);
679     }
680     if (!SUCCEEDED(hr)) {
681         if (D3DERR_NOTAVAILABLE != hr)
682             msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
683         return VLC_EGENERIC;
684     }
685     return VLC_SUCCESS;
686 }
687
688 typedef struct
689 {
690     const char   *name;
691     D3DFORMAT    format;    /* D3D format */
692     vlc_fourcc_t fourcc;    /* VLC fourcc */
693     uint32_t     rmask;
694     uint32_t     gmask;
695     uint32_t     bmask;
696 } d3d_format_t;
697
698 static const d3d_format_t d3d_formats[] = {
699     /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
700     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12,  0,0,0 },
701     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
702     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
703     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
704     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
705     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
706     { "A8R8G8B8",   D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
707     { "8G8B8",      D3DFMT_R8G8B8,  VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
708     { "R5G6B5",     D3DFMT_R5G6B5,  VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5,  0x1f<<0 },
709     { "X1R5G5B5",   D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5,  0x1f<<0 },
710
711     { NULL, 0, 0, 0,0,0}
712 };
713
714 /**
715  * It returns the format (closest to chroma) that can be converted to target */
716 static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
717 {
718     vout_display_sys_t *sys = vd->sys;
719
720     for (unsigned pass = 0; pass < 2; pass++) {
721         const vlc_fourcc_t *list;
722
723         if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
724             list = vlc_fourcc_GetYUVFallback(chroma);
725         else if (pass == 1)
726             list = vlc_fourcc_GetRGBFallback(chroma);
727         else
728             continue;
729
730         for (unsigned i = 0; list[i] != 0; i++) {
731             for (unsigned j = 0; d3d_formats[j].name; j++) {
732                 const d3d_format_t *format = &d3d_formats[j];
733
734                 if (format->fourcc != list[i])
735                     continue;
736
737                 msg_Warn(vd, "trying surface pixel format: %s",
738                          format->name);
739                 if (!Direct3DCheckConversion(vd, format->format, target)) {
740                     msg_Dbg(vd, "selected surface pixel format is %s",
741                             format->name);
742                     return format;
743                 }
744             }
745         }
746     }
747     return NULL;
748 }
749
750 /**
751  * It locks the surface associated to the picture and get the surface
752  * descriptor which amongst other things has the pointer to the picture
753  * data and its pitch.
754  */
755 static int Direct3DLockSurface(picture_t *picture)
756 {
757     /* Lock the surface to get a valid pointer to the picture buffer */
758     D3DLOCKED_RECT d3drect;
759     HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
760     if (FAILED(hr)) {
761         //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
762         return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
763     }
764
765     CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
766     return VLC_SUCCESS;
767 }
768 /**
769  * It unlocks the surface associated to the picture.
770  */
771 static void Direct3DUnlockSurface(picture_t *picture)
772 {
773     /* Unlock the Surface */
774     HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
775     if (FAILED(hr)) {
776         //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
777     }
778 }
779
780 /**
781  * It creates the pool of picture (only 1).
782  *
783  * Each picture has an associated offscreen surface in video memory
784  * depending on hardware capabilities the picture chroma will be as close
785  * as possible to the orginal render chroma to reduce CPU conversion overhead
786  * and delegate this work to video card GPU
787  */
788 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt)
789 {
790     vout_display_sys_t *sys = vd->sys;
791     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
792
793     /* */
794     *fmt = vd->source;
795
796     /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
797      * the requested chroma which is usable by the hardware in an offscreen surface, as they
798      * typically support more formats than textures */
799     const d3d_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, sys->d3dpp.BackBufferFormat);
800     if (!d3dfmt) {
801         msg_Err(vd, "surface pixel format is not supported.");
802         return VLC_EGENERIC;
803     }
804     fmt->i_chroma = d3dfmt->fourcc;
805     fmt->i_rmask  = d3dfmt->rmask;
806     fmt->i_gmask  = d3dfmt->gmask;
807     fmt->i_bmask  = d3dfmt->bmask;
808
809     /* We create one picture.
810      * It is useless to create more as we can't be used for direct rendering */
811
812     /* Create a surface */
813     LPDIRECT3DSURFACE9 surface;
814     HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
815                                                               fmt->i_width,
816                                                               fmt->i_height,
817                                                               d3dfmt->format,
818                                                               D3DPOOL_DEFAULT,
819                                                               &surface,
820                                                               NULL);
821     if (FAILED(hr)) {
822         msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
823         return VLC_EGENERIC;
824     }
825     /* fill surface with black color */
826     IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
827
828     /* Create the associated picture */
829     picture_resource_t *rsc = &sys->resource;
830     rsc->p_sys = malloc(sizeof(*rsc->p_sys));
831     if (!rsc->p_sys) {
832         IDirect3DSurface9_Release(surface);
833         return VLC_ENOMEM;
834     }
835     rsc->p_sys->surface = surface;
836     rsc->p_sys->fallback = NULL;
837     for (int i = 0; i < PICTURE_PLANE_MAX; i++) {
838         rsc->p[i].p_pixels = NULL;
839         rsc->p[i].i_pitch = 0;
840         rsc->p[i].i_lines = fmt->i_height / (i > 0 ? 2 : 1);
841     }
842     picture_t *picture = picture_NewFromResource(fmt, rsc);
843     if (!picture) {
844         IDirect3DSurface9_Release(surface);
845         free(rsc->p_sys);
846         return VLC_ENOMEM;
847     }
848
849     /* Wrap it into a picture pool */
850     picture_pool_configuration_t pool_cfg;
851     memset(&pool_cfg, 0, sizeof(pool_cfg));
852     pool_cfg.picture_count = 1;
853     pool_cfg.picture       = &picture;
854     pool_cfg.lock          = Direct3DLockSurface;
855     pool_cfg.unlock        = Direct3DUnlockSurface;
856
857     sys->pool = picture_pool_NewExtended(&pool_cfg);
858     if (!sys->pool) {
859         picture_Release(picture);
860         IDirect3DSurface9_Release(surface);
861         return VLC_ENOMEM;
862     }
863     return VLC_SUCCESS;
864 }
865 /**
866  * It destroys the pool of picture and its resources.
867  */
868 static void Direct3DDestroyPool(vout_display_t *vd)
869 {
870     vout_display_sys_t *sys = vd->sys;
871
872     if (sys->pool) {
873         picture_resource_t *rsc = &sys->resource;
874         IDirect3DSurface9_Release(rsc->p_sys->surface);
875         if (rsc->p_sys->fallback)
876             picture_Release(rsc->p_sys->fallback);
877         picture_pool_Delete(sys->pool);
878     }
879     sys->pool = NULL;
880 }
881
882 /* */
883 typedef struct
884 {
885     FLOAT       x,y,z;      // vertex untransformed position
886     FLOAT       rhw;        // eye distance
887     D3DCOLOR    diffuse;    // diffuse color
888     FLOAT       tu, tv;     // texture relative coordinates
889 } CUSTOMVERTEX;
890
891 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
892
893 /**
894  * It allocates and initializes the resources needed to render the scene.
895  */
896 static int Direct3DCreateScene(vout_display_t *vd)
897 {
898     vout_display_sys_t *sys = vd->sys;
899     LPDIRECT3DDEVICE9       d3ddev = sys->d3ddev;
900     HRESULT hr;
901
902     /*
903      * Create a texture for use when rendering a scene
904      * for performance reason, texture format is identical to backbuffer
905      * which would usually be a RGB format
906      */
907     LPDIRECT3DTEXTURE9 d3dtex;
908     hr = IDirect3DDevice9_CreateTexture(d3ddev,
909                                         sys->d3dpp.BackBufferWidth,
910                                         sys->d3dpp.BackBufferHeight,
911                                         1,
912                                         D3DUSAGE_RENDERTARGET,
913                                         sys->d3dpp.BackBufferFormat,
914                                         D3DPOOL_DEFAULT,
915                                         &d3dtex,
916                                         NULL);
917     if (FAILED(hr)) {
918         msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
919         return VLC_EGENERIC;
920     }
921
922     /*
923     ** Create a vertex buffer for use when rendering scene
924     */
925     LPDIRECT3DVERTEXBUFFER9 d3dvtc;
926     hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
927                                              sizeof(CUSTOMVERTEX)*4,
928                                              D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
929                                              D3DFVF_CUSTOMVERTEX,
930                                              D3DPOOL_DEFAULT,
931                                              &d3dvtc,
932                                              NULL);
933     if (FAILED(hr)) {
934         msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
935         IDirect3DTexture9_Release(d3dtex);
936         return VLC_EGENERIC;
937     }
938
939     /* */
940     sys->d3dtex = d3dtex;
941     sys->d3dvtc = d3dvtc;
942
943     // Texture coordinates outside the range [0.0, 1.0] are set
944     // to the texture color at 0.0 or 1.0, respectively.
945     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
946     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
947
948     // Set linear filtering quality
949     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
950     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
951
952     // set maximum ambient light
953     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
954
955     // Turn off culling
956     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
957
958     // Turn off the zbuffer
959     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
960
961     // Turn off lights
962     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
963
964     // Enable dithering
965     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
966
967     // disable stencil
968     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
969
970     // manage blending
971     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
972     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
973     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
974     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
975     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x10);
976     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
977
978     // Set texture states
979     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
980     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
981     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
982
983     // turn off alpha operation
984     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
985
986     msg_Dbg(vd, "Direct3D scene created successfully");
987
988     return VLC_SUCCESS;
989 }
990
991 /**
992  * It releases the scene resources.
993  */
994 static void Direct3DDestroyScene(vout_display_t *vd)
995 {
996     vout_display_sys_t *sys = vd->sys;
997
998     LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
999     if (d3dvtc)
1000         IDirect3DVertexBuffer9_Release(d3dvtc);
1001
1002     LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1003     if (d3dtex)
1004         IDirect3DTexture9_Release(d3dtex);
1005
1006     sys->d3dvtc = NULL;
1007     sys->d3dtex = NULL;
1008     msg_Dbg(vd, "Direct3D scene released successfully");
1009 }
1010
1011 /**
1012  * It copies picture surface into a texture and renders into a scene.
1013  *
1014  * This function is intented for higher end 3D cards, with pixel shader support
1015  * and at least 64 MiB of video RAM.
1016  */
1017 static void Direct3DRenderScene(vout_display_t *vd, LPDIRECT3DSURFACE9 surface)
1018 {
1019     vout_display_sys_t *sys = vd->sys;
1020     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1021     HRESULT hr;
1022
1023     // check if device is still available
1024     hr = IDirect3DDevice9_TestCooperativeLevel(d3ddev);
1025     if (FAILED(hr)) {
1026         if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
1027             vout_display_SendEventPicturesInvalid(vd);
1028             sys->reset_device = true;
1029         }
1030         return;
1031     }
1032     /* */
1033     LPDIRECT3DTEXTURE9      d3dtex  = sys->d3dtex;
1034     LPDIRECT3DVERTEXBUFFER9 d3dvtc  = sys->d3dvtc;
1035
1036     /* Clear the backbuffer and the zbuffer */
1037     hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1038                               D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1039     if (FAILED(hr)) {
1040         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1041         return;
1042     }
1043     /*  retrieve picture surface */
1044     LPDIRECT3DSURFACE9 d3dsrc = surface;
1045     if (!d3dsrc) {
1046         msg_Dbg(vd, "no surface to render ?");
1047         return;
1048     }
1049
1050     /* retrieve texture top-level surface */
1051     LPDIRECT3DSURFACE9 d3ddest;
1052     hr = IDirect3DTexture9_GetSurfaceLevel(d3dtex, 0, &d3ddest);
1053     if (FAILED(hr)) {
1054         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1055         return;
1056     }
1057
1058     /* Copy picture surface into texture surface
1059      * color space conversion and scaling happen here */
1060     RECT src = vd->sys->rect_src_clipped;
1061     RECT dst = vd->sys->rect_dest_clipped;
1062
1063     hr = IDirect3DDevice9_StretchRect(d3ddev, d3dsrc, &src, d3ddest, &dst, D3DTEXF_LINEAR);
1064     IDirect3DSurface9_Release(d3ddest);
1065     if (FAILED(hr)) {
1066         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1067         return;
1068     }
1069
1070     /* Update the vertex buffer */
1071     CUSTOMVERTEX *vertices;
1072     hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertices, D3DLOCK_DISCARD);
1073     if (FAILED(hr)) {
1074         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1075         return;
1076     }
1077
1078     /* Setup vertices */
1079     const float f_width  = vd->sys->d3dpp.BackBufferWidth;
1080     const float f_height = vd->sys->d3dpp.BackBufferHeight;
1081
1082     /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1083     /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1084     vertices[0].x       = -0.5f;       // left
1085     vertices[0].y       = -0.5f;       // top
1086     vertices[0].z       = 0.0f;
1087     vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1088     vertices[0].rhw     = 1.0f;
1089     vertices[0].tu      = 0.0f;
1090     vertices[0].tv      = 0.0f;
1091
1092     vertices[1].x       = f_width - 0.5f;    // right
1093     vertices[1].y       = -0.5f;       // top
1094     vertices[1].z       = 0.0f;
1095     vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1096     vertices[1].rhw     = 1.0f;
1097     vertices[1].tu      = 1.0f;
1098     vertices[1].tv      = 0.0f;
1099
1100     vertices[2].x       = f_width - 0.5f;    // right
1101     vertices[2].y       = f_height - 0.5f;   // bottom
1102     vertices[2].z       = 0.0f;
1103     vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1104     vertices[2].rhw     = 1.0f;
1105     vertices[2].tu      = 1.0f;
1106     vertices[2].tv      = 1.0f;
1107
1108     vertices[3].x       = -0.5f;       // left
1109     vertices[3].y       = f_height - 0.5f;   // bottom
1110     vertices[3].z       = 0.0f;
1111     vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1112     vertices[3].rhw     = 1.0f;
1113     vertices[3].tu      = 0.0f;
1114     vertices[3].tv      = 1.0f;
1115
1116     hr= IDirect3DVertexBuffer9_Unlock(d3dvtc);
1117     if (FAILED(hr)) {
1118         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1119         return;
1120     }
1121
1122     // Begin the scene
1123     hr = IDirect3DDevice9_BeginScene(d3ddev);
1124     if (FAILED(hr)) {
1125         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1126         return;
1127     }
1128
1129     // Setup our texture. Using textures introduces the texture stage states,
1130     // which govern how textures get blended together (in the case of multiple
1131     // textures) and lighting information. In this case, we are modulating
1132     // (blending) our texture with the diffuse color of the vertices.
1133     hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1134     if (FAILED(hr)) {
1135         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1136         IDirect3DDevice9_EndScene(d3ddev);
1137         return;
1138     }
1139
1140     // Render the vertex buffer contents
1141     hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1142     if (FAILED(hr)) {
1143         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1144         IDirect3DDevice9_EndScene(d3ddev);
1145         return;
1146     }
1147
1148     // we use FVF instead of vertex shader
1149     hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1150     if (FAILED(hr)) {
1151         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1152         IDirect3DDevice9_EndScene(d3ddev);
1153         return;
1154     }
1155
1156     // draw rectangle
1157     hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1158     if (FAILED(hr)) {
1159         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1160         IDirect3DDevice9_EndScene(d3ddev);
1161         return;
1162     }
1163
1164     // End the scene
1165     hr = IDirect3DDevice9_EndScene(d3ddev);
1166     if (FAILED(hr)) {
1167         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1168         return;
1169     }
1170 }
1171
1172 /*****************************************************************************
1173  * DesktopCallback: desktop mode variable callback
1174  *****************************************************************************/
1175 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1176                             vlc_value_t oldval, vlc_value_t newval,
1177                             void *p_data)
1178 {
1179     vout_display_t *vd = (vout_display_t *)object;
1180     vout_display_sys_t *sys = vd->sys;
1181     VLC_UNUSED(psz_cmd);
1182     VLC_UNUSED(oldval);
1183     VLC_UNUSED(p_data);
1184
1185     vlc_mutex_lock(&sys->lock);
1186     const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1187     sys->ch_desktop |= ch_desktop;
1188     sys->desktop_requested = newval.b_bool;
1189     vlc_mutex_unlock(&sys->lock);
1190
1191     /* FIXME we should have a way to export variable to be saved */
1192     if (ch_desktop) {
1193         playlist_t *p_playlist = pl_Get(vd);
1194         /* Modify playlist as well because the vout might have to be
1195          * restarted */
1196         var_Create(p_playlist, "direct3d-desktop", VLC_VAR_BOOL);
1197         var_SetBool(p_playlist, "direct3d-desktop", newval.b_bool);
1198     }
1199     return VLC_SUCCESS;
1200 }