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