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