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