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