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