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