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