]> git.sesse.net Git - vlc/blob - modules/video_output/msw/direct3d.c
Wayland/shell: fix NULL dereference in pathological case
[vlc] / modules / video_output / msw / direct3d.c
1 /*****************************************************************************
2  * direct3d.c: Windows Direct3D video output module
3  *****************************************************************************
4  * Copyright (C) 2006-2014 VLC authors and VideoLAN
5  *$Id$
6  *
7  * Authors: Damien Fouilleul <damienf@videolan.org>,
8  *          Sasha Koruga <skoruga@gmail.com>,
9  *          Felix Abecassis <felix.abecassis@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble:
28  *
29  * This plugin will use YUV surface if supported, using YUV will result in
30  * the best video quality (hardware filering when rescaling the picture)
31  * and the fastest display as it requires less processing.
32  *
33  * If YUV overlay is not supported this plugin will use RGB offscreen video
34  * surfaces that will be blitted onto the primary surface (display) to
35  * effectively display the pictures.
36  *
37  *****************************************************************************/
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_vout_display.h>
45 #include <vlc_charset.h> /* ToT function */
46
47 #include <windows.h>
48 #include <d3d9.h>
49
50 #include "common.h"
51 #include "builtin_shaders.h"
52
53 /*****************************************************************************
54  * Module descriptor
55  *****************************************************************************/
56 static int  Open(vlc_object_t *);
57 static void Close(vlc_object_t *);
58
59 #define DESKTOP_LONGTEXT N_(\
60     "The desktop mode allows you to display the video on the desktop.")
61
62 #define HW_BLENDING_TEXT N_("Use hardware blending support")
63 #define HW_BLENDING_LONGTEXT N_(\
64     "Try to use hardware acceleration for subtitle/OSD blending.")
65
66 #define PIXEL_SHADER_TEXT N_("Pixel Shader")
67 #define PIXEL_SHADER_LONGTEXT N_(\
68         "Choose a pixel shader to apply.")
69 #define PIXEL_SHADER_FILE_TEXT N_("Path to HLSL file")
70 #define PIXEL_SHADER_FILE_LONGTEXT N_("Path to an HLSL file containing a single pixel shader.")
71 /* The latest option in the selection list: used for loading a shader file. */
72 #define SELECTED_SHADER_FILE N_("HLSL File")
73
74 #define D3D_HELP N_("Recommended video output for Windows Vista and later versions")
75
76 static int FindShadersCallback(vlc_object_t *, const char *,
77                                char ***, char ***);
78
79 vlc_module_begin ()
80     set_shortname("Direct3D")
81     set_description(N_("Direct3D video output"))
82     set_help(D3D_HELP)
83     set_category(CAT_VIDEO)
84     set_subcategory(SUBCAT_VIDEO_VOUT)
85
86     add_bool("direct3d-hw-blending", true, HW_BLENDING_TEXT, HW_BLENDING_LONGTEXT, true)
87
88     add_string("direct3d-shader", "", PIXEL_SHADER_TEXT, PIXEL_SHADER_LONGTEXT, true)
89         change_string_cb(FindShadersCallback)
90     add_loadfile("direct3d-shader-file", NULL, PIXEL_SHADER_FILE_TEXT, PIXEL_SHADER_FILE_LONGTEXT, false)
91
92     set_capability("vout display", 240)
93     add_shortcut("direct3d")
94     set_callbacks(Open, Close)
95
96 vlc_module_end ()
97
98 /*****************************************************************************
99  * Local prototypes.
100  *****************************************************************************/
101 static const vlc_fourcc_t d3d_subpicture_chromas[] = {
102     VLC_CODEC_RGBA,
103     0
104 };
105
106 struct picture_sys_t
107 {
108     LPDIRECT3DSURFACE9 surface;
109     picture_t          *fallback;
110 };
111
112 static int  Open(vlc_object_t *);
113
114 static picture_pool_t *Pool  (vout_display_t *, unsigned);
115 static void           Prepare(vout_display_t *, picture_t *, subpicture_t *subpicture);
116 static void           Display(vout_display_t *, picture_t *, subpicture_t *subpicture);
117 static int            Control(vout_display_t *, int, va_list);
118 static void           Manage (vout_display_t *);
119
120 static int  Direct3DCreate (vout_display_t *);
121 static int  Direct3DReset  (vout_display_t *);
122 static void Direct3DDestroy(vout_display_t *);
123
124 static int  Direct3DOpen (vout_display_t *, video_format_t *);
125 static void Direct3DClose(vout_display_t *);
126
127 /* */
128 typedef struct
129 {
130     FLOAT       x,y,z;      // vertex untransformed position
131     FLOAT       rhw;        // eye distance
132     D3DCOLOR    diffuse;    // diffuse color
133     FLOAT       tu, tv;     // texture relative coordinates
134 } CUSTOMVERTEX;
135 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
136
137 typedef struct d3d_region_t {
138     D3DFORMAT          format;
139     unsigned           width;
140     unsigned           height;
141     CUSTOMVERTEX       vertex[4];
142     LPDIRECT3DTEXTURE9 texture;
143 } d3d_region_t;
144
145 static void Direct3DDeleteRegions(int, d3d_region_t *);
146
147 static int  Direct3DImportPicture(vout_display_t *vd, d3d_region_t *, LPDIRECT3DSURFACE9 surface);
148 static void Direct3DImportSubpicture(vout_display_t *vd, int *, d3d_region_t **, subpicture_t *);
149
150 static void Direct3DRenderScene(vout_display_t *vd, d3d_region_t *, int, d3d_region_t *);
151
152 /* */
153 static int DesktopCallback(vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void *);
154
155 /**
156  * It creates a Direct3D vout display.
157  */
158 static int Open(vlc_object_t *object)
159 {
160     vout_display_t *vd = (vout_display_t *)object;
161     vout_display_sys_t *sys;
162
163     OSVERSIONINFO winVer;
164     winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
165     if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6 && !object->b_force)
166         return VLC_EGENERIC;
167
168     /* Allocate structure */
169     vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
170     if (!sys)
171         return VLC_ENOMEM;
172
173     if (Direct3DCreate(vd)) {
174         msg_Err(vd, "Direct3D could not be initialized");
175         Direct3DDestroy(vd);
176         free(sys);
177         return VLC_EGENERIC;
178     }
179
180     sys->use_desktop = var_CreateGetBool(vd, "video-wallpaper");
181     sys->reset_device = false;
182     sys->reopen_device = false;
183     sys->lost_not_ready = false;
184     sys->allow_hw_yuv = var_CreateGetBool(vd, "directx-hw-yuv");
185     sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
186     sys->desktop_save.is_on_top     = false;
187     sys->desktop_save.win.left      = var_InheritInteger(vd, "video-x");
188     sys->desktop_save.win.right     = vd->cfg->display.width;
189     sys->desktop_save.win.top       = var_InheritInteger(vd, "video-y");
190     sys->desktop_save.win.bottom    = vd->cfg->display.height;
191
192     if (CommonInit(vd))
193         goto error;
194
195     /* */
196     video_format_t fmt;
197     if (Direct3DOpen(vd, &fmt)) {
198         msg_Err(vd, "Direct3D could not be opened");
199         goto error;
200     }
201
202     /* */
203     vout_display_info_t info = vd->info;
204     info.is_slow = true;
205     info.has_double_click = true;
206     info.has_hide_mouse = false;
207     info.has_pictures_invalid = true;
208     info.has_event_thread = true;
209     if (var_InheritBool(vd, "direct3d-hw-blending") &&
210         sys->d3dregion_format != D3DFMT_UNKNOWN &&
211         (sys->d3dcaps.SrcBlendCaps  & D3DPBLENDCAPS_SRCALPHA) &&
212         (sys->d3dcaps.DestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) &&
213         (sys->d3dcaps.TextureCaps   & D3DPTEXTURECAPS_ALPHA) &&
214         (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_SELECTARG1) &&
215         (sys->d3dcaps.TextureOpCaps & D3DTEXOPCAPS_MODULATE))
216         info.subpicture_chromas = d3d_subpicture_chromas;
217     else
218         info.subpicture_chromas = NULL;
219
220     /* Interaction */
221     vlc_mutex_init(&sys->lock);
222     sys->ch_desktop = false;
223     sys->desktop_requested = sys->use_desktop;
224
225     vlc_value_t val;
226     val.psz_string = _("Desktop");
227     var_Change(vd, "video-wallpaper", VLC_VAR_SETTEXT, &val, NULL);
228     var_AddCallback(vd, "video-wallpaper", DesktopCallback, NULL);
229
230     /* Setup vout_display now that everything is fine */
231     video_format_Clean(&vd->fmt);
232     video_format_Copy(&vd->fmt, &fmt);
233     vd->info = info;
234
235     vd->pool    = Pool;
236     vd->prepare = Prepare;
237     vd->display = Display;
238     vd->control = Control;
239     vd->manage  = Manage;
240
241     /* Fix state in case of desktop mode */
242     if (sys->use_desktop && vd->cfg->is_fullscreen)
243         vout_display_SendEventFullscreen(vd, false);
244
245     return VLC_SUCCESS;
246 error:
247     Direct3DClose(vd);
248     CommonClean(vd);
249     Direct3DDestroy(vd);
250     free(vd->sys);
251     return VLC_EGENERIC;
252 }
253
254 /**
255  * It destroyes a Direct3D vout display.
256  */
257 static void Close(vlc_object_t *object)
258 {
259     vout_display_t * vd = (vout_display_t *)object;
260
261     var_DelCallback(vd, "video-wallpaper", DesktopCallback, NULL);
262     vlc_mutex_destroy(&vd->sys->lock);
263
264     Direct3DClose(vd);
265
266     CommonClean(vd);
267
268     Direct3DDestroy(vd);
269
270     free(vd->sys);
271 }
272
273 /* */
274 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
275 {
276     VLC_UNUSED(count);
277     return vd->sys->pool;
278 }
279
280 static int  Direct3DLockSurface(picture_t *);
281 static void Direct3DUnlockSurface(picture_t *);
282
283 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
284 {
285     vout_display_sys_t *sys = vd->sys;
286     LPDIRECT3DSURFACE9 surface = picture->p_sys->surface;
287 #if 0
288     picture_Release(picture);
289     VLC_UNUSED(subpicture);
290 #else
291     /* FIXME it is a bit ugly, we need the surface to be unlocked for
292      * rendering.
293      *  The clean way would be to release the picture (and ensure that
294      * the vout doesn't keep a reference). But because of the vout
295      * wrapper, we can't */
296
297     Direct3DUnlockSurface(picture);
298     VLC_UNUSED(subpicture);
299 #endif
300
301     /* check if device is still available */
302     HRESULT hr = IDirect3DDevice9_TestCooperativeLevel(sys->d3ddev);
303     if (FAILED(hr)) {
304         if (hr == D3DERR_DEVICENOTRESET && !sys->reset_device) {
305             vout_display_SendEventPicturesInvalid(vd);
306             sys->reset_device = true;
307             sys->lost_not_ready = false;
308         }
309         if (hr == D3DERR_DEVICELOST && !sys->lost_not_ready) {
310             /* Device is lost but not yet ready for reset. */
311             sys->lost_not_ready = true;
312         }
313         return;
314     }
315
316     d3d_region_t picture_region;
317     if (!Direct3DImportPicture(vd, &picture_region, surface)) {
318         picture_region.width = picture->format.i_visible_width;
319         picture_region.height = picture->format.i_visible_height;
320         int subpicture_region_count     = 0;
321         d3d_region_t *subpicture_region = NULL;
322         if (subpicture)
323             Direct3DImportSubpicture(vd, &subpicture_region_count, &subpicture_region,
324                                      subpicture);
325
326         Direct3DRenderScene(vd, &picture_region,
327                             subpicture_region_count, subpicture_region);
328
329         Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
330         sys->d3dregion_count = subpicture_region_count;
331         sys->d3dregion       = subpicture_region;
332     }
333 }
334
335 static void Display(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
336 {
337     vout_display_sys_t *sys = vd->sys;
338     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
339
340     if (sys->lost_not_ready) {
341         picture_Release(picture);
342         if (subpicture)
343             subpicture_Delete(subpicture);
344         return;
345     }
346
347     // Present the back buffer contents to the display
348     // No stretching should happen here !
349     const RECT src = sys->rect_dest_clipped;
350     const RECT dst = sys->rect_dest_clipped;
351     HRESULT hr = IDirect3DDevice9_Present(d3ddev, &src, &dst, NULL, NULL);
352     if (FAILED(hr)) {
353         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
354     }
355
356 #if 0
357     VLC_UNUSED(picture);
358     VLC_UNUSED(subpicture);
359 #else
360     /* XXX See Prepare() */
361     Direct3DLockSurface(picture);
362     picture_Release(picture);
363 #endif
364     if (subpicture)
365         subpicture_Delete(subpicture);
366
367     CommonDisplay(vd);
368 }
369 static int ControlResetDevice(vout_display_t *vd)
370 {
371     return Direct3DReset(vd);
372 }
373 static int ControlReopenDevice(vout_display_t *vd)
374 {
375     vout_display_sys_t *sys = vd->sys;
376
377     if (!sys->use_desktop) {
378         /* Save non-desktop state */
379         sys->desktop_save.is_fullscreen = vd->cfg->is_fullscreen;
380         sys->desktop_save.is_on_top     = sys->is_on_top;
381
382         WINDOWPLACEMENT wp = { .length = sizeof(wp), };
383         GetWindowPlacement(sys->hparent ? sys->hparent : sys->hwnd, &wp);
384         sys->desktop_save.win = wp.rcNormalPosition;
385     }
386
387     /* */
388     Direct3DClose(vd);
389     EventThreadStop(sys->event);
390
391     /* */
392     vlc_mutex_lock(&sys->lock);
393     sys->use_desktop = sys->desktop_requested;
394     sys->ch_desktop = false;
395     vlc_mutex_unlock(&sys->lock);
396
397     /* */
398     event_cfg_t cfg;
399     memset(&cfg, 0, sizeof(cfg));
400     cfg.use_desktop = sys->use_desktop;
401     if (!sys->use_desktop) {
402         cfg.x      = sys->desktop_save.win.left;
403         cfg.y      = sys->desktop_save.win.top;
404         cfg.width  = sys->desktop_save.win.right  - sys->desktop_save.win.left;
405         cfg.height = sys->desktop_save.win.bottom - sys->desktop_save.win.top;
406     }
407
408     event_hwnd_t hwnd;
409     if (EventThreadStart(sys->event, &hwnd, &cfg)) {
410         msg_Err(vd, "Failed to restart event thread");
411         return VLC_EGENERIC;
412     }
413     sys->parent_window = hwnd.parent_window;
414     sys->hparent       = hwnd.hparent;
415     sys->hwnd          = hwnd.hwnd;
416     sys->hvideownd     = hwnd.hvideownd;
417     sys->hfswnd        = hwnd.hfswnd;
418     SetRectEmpty(&sys->rect_parent);
419
420     /* */
421     video_format_t fmt;
422     if (Direct3DOpen(vd, &fmt)) {
423         CommonClean(vd);
424         msg_Err(vd, "Failed to reopen device");
425         return VLC_EGENERIC;
426     }
427     vd->fmt = fmt;
428     sys->is_first_display = true;
429
430     if (sys->use_desktop) {
431         /* Disable fullscreen/on_top while using desktop */
432         if (sys->desktop_save.is_fullscreen)
433             vout_display_SendEventFullscreen(vd, false);
434         if (sys->desktop_save.is_on_top)
435             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_NORMAL);
436     } else {
437         /* Restore fullscreen/on_top */
438         if (sys->desktop_save.is_fullscreen)
439             vout_display_SendEventFullscreen(vd, true);
440         if (sys->desktop_save.is_on_top)
441             vout_display_SendWindowState(vd, VOUT_WINDOW_STATE_ABOVE);
442     }
443     return VLC_SUCCESS;
444 }
445 static int Control(vout_display_t *vd, int query, va_list args)
446 {
447     vout_display_sys_t *sys = vd->sys;
448
449     switch (query) {
450     case VOUT_DISPLAY_RESET_PICTURES:
451         /* FIXME what to do here in case of failure */
452         if (sys->reset_device) {
453             if (ControlResetDevice(vd)) {
454                 msg_Err(vd, "Failed to reset device");
455                 return VLC_EGENERIC;
456             }
457             sys->reset_device = false;
458         } else if(sys->reopen_device) {
459             if (ControlReopenDevice(vd)) {
460                 msg_Err(vd, "Failed to reopen device");
461                 return VLC_EGENERIC;
462             }
463             sys->reopen_device = false;
464         }
465         return VLC_SUCCESS;
466     default:
467         return CommonControl(vd, query, args);
468     }
469 }
470 static void Manage (vout_display_t *vd)
471 {
472     vout_display_sys_t *sys = vd->sys;
473
474     CommonManage(vd);
475
476     /* Desktop mode change */
477     vlc_mutex_lock(&sys->lock);
478     const bool ch_desktop = sys->ch_desktop;
479     sys->ch_desktop = false;
480     vlc_mutex_unlock(&sys->lock);
481
482     if (ch_desktop) {
483         sys->reopen_device = true;
484         vout_display_SendEventPicturesInvalid(vd);
485     }
486
487     /* Position Change */
488     if (sys->changes & DX_POSITION_CHANGE) {
489 #if 0 /* need that when bicubic filter is available */
490         RECT rect;
491         UINT width, height;
492
493         GetClientRect(p_sys->hvideownd, &rect);
494         width  = rect.right-rect.left;
495         height = rect.bottom-rect.top;
496
497         if (width != p_sys->d3dpp.BackBufferWidth || height != p_sys->d3dpp.BackBufferHeight)
498         {
499             msg_Dbg(vd, "resizing device back buffers to (%lux%lu)", width, height);
500             // need to reset D3D device to resize back buffer
501             if (VLC_SUCCESS != Direct3DResetDevice(vd, width, height))
502                 return VLC_EGENERIC;
503         }
504 #endif
505         sys->clear_scene = true;
506         sys->changes &= ~DX_POSITION_CHANGE;
507     }
508 }
509
510 static HINSTANCE Direct3DLoadShaderLibrary(void)
511 {
512     HINSTANCE instance = NULL;
513     for (int i = 43; i > 23; --i) {
514         TCHAR filename[16];
515         _sntprintf(filename, 16, TEXT("D3dx9_%d.dll"), i);
516         instance = LoadLibrary(filename);
517         if (instance)
518             break;
519     }
520     return instance;
521 }
522
523 /**
524  * It initializes an instance of Direct3D9
525  */
526 static int Direct3DCreate(vout_display_t *vd)
527 {
528     vout_display_sys_t *sys = vd->sys;
529
530     sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
531     if (!sys->hd3d9_dll) {
532         msg_Warn(vd, "cannot load d3d9.dll, aborting");
533         return VLC_EGENERIC;
534     }
535
536     LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
537     OurDirect3DCreate9 =
538         (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
539     if (!OurDirect3DCreate9) {
540         msg_Err(vd, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
541         return VLC_EGENERIC;
542     }
543
544     /* Create the D3D object. */
545     LPDIRECT3D9 d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
546     if (!d3dobj) {
547        msg_Err(vd, "Could not create Direct3D9 instance.");
548        return VLC_EGENERIC;
549     }
550     sys->d3dobj = d3dobj;
551
552     sys->hd3d9x_dll = Direct3DLoadShaderLibrary();
553     if (!sys->hd3d9x_dll)
554         msg_Warn(vd, "cannot load Direct3D Shader Library; HLSL pixel shading will be disabled.");
555
556     /*
557     ** Get device capabilities
558     */
559     ZeroMemory(&sys->d3dcaps, sizeof(sys->d3dcaps));
560     HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sys->d3dcaps);
561     if (FAILED(hr)) {
562        msg_Err(vd, "Could not read adapter capabilities. (hr=0x%lX)", hr);
563        return VLC_EGENERIC;
564     }
565
566     /* TODO: need to test device capabilities and select the right render function */
567     if (!(sys->d3dcaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) ||
568         !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MAGFLINEAR)) ||
569         !(sys->d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR))) {
570         msg_Err(vd, "Device does not support stretching from textures.");
571         return VLC_EGENERIC;
572     }
573
574     return VLC_SUCCESS;
575 }
576
577 /**
578  * It releases an instance of Direct3D9
579  */
580 static void Direct3DDestroy(vout_display_t *vd)
581 {
582     vout_display_sys_t *sys = vd->sys;
583
584     if (sys->d3dobj)
585        IDirect3D9_Release(sys->d3dobj);
586     if (sys->hd3d9_dll)
587         FreeLibrary(sys->hd3d9_dll);
588     if (sys->hd3d9x_dll)
589         FreeLibrary(sys->hd3d9x_dll);
590
591     sys->d3dobj = NULL;
592     sys->hd3d9_dll = NULL;
593     sys->hd3d9x_dll = NULL;
594 }
595
596
597 /**
598  * It setup vout_display_sys_t::d3dpp and vout_display_sys_t::rect_display
599  * from the default adapter.
600  */
601 static int Direct3DFillPresentationParameters(vout_display_t *vd)
602 {
603     vout_display_sys_t *sys = vd->sys;
604
605     /*
606     ** Get the current desktop display mode, so we can set up a back
607     ** buffer of the same format
608     */
609     D3DDISPLAYMODE d3ddm;
610     HRESULT hr = IDirect3D9_GetAdapterDisplayMode(sys->d3dobj,
611                                                   D3DADAPTER_DEFAULT, &d3ddm);
612     if (FAILED(hr)) {
613        msg_Err(vd, "Could not read adapter display mode. (hr=0x%lX)", hr);
614        return VLC_EGENERIC;
615     }
616
617     /* Set up the structure used to create the D3DDevice. */
618     D3DPRESENT_PARAMETERS *d3dpp = &vd->sys->d3dpp;
619     ZeroMemory(d3dpp, sizeof(D3DPRESENT_PARAMETERS));
620     d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
621     d3dpp->Windowed               = TRUE;
622     d3dpp->hDeviceWindow          = vd->sys->hvideownd;
623     d3dpp->BackBufferWidth        = __MAX((unsigned int)GetSystemMetrics(SM_CXVIRTUALSCREEN),
624                                           d3ddm.Width);
625     d3dpp->BackBufferHeight       = __MAX((unsigned int)GetSystemMetrics(SM_CYVIRTUALSCREEN),
626                                           d3ddm.Height);
627     d3dpp->SwapEffect             = D3DSWAPEFFECT_COPY;
628     d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
629     d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
630     d3dpp->BackBufferFormat       = d3ddm.Format;
631     d3dpp->BackBufferCount        = 1;
632     d3dpp->EnableAutoDepthStencil = FALSE;
633
634     /* */
635     RECT *display = &vd->sys->rect_display;
636     display->left   = 0;
637     display->top    = 0;
638     display->right  = d3dpp->BackBufferWidth;
639     display->bottom = d3dpp->BackBufferHeight;
640
641     return VLC_SUCCESS;
642 }
643
644 /* */
645 static int  Direct3DCreateResources (vout_display_t *, video_format_t *);
646 static void Direct3DDestroyResources(vout_display_t *);
647
648 /**
649  * It creates a Direct3D device and the associated resources.
650  */
651 static int Direct3DOpen(vout_display_t *vd, video_format_t *fmt)
652 {
653     vout_display_sys_t *sys = vd->sys;
654     LPDIRECT3D9 d3dobj = sys->d3dobj;
655
656     if (Direct3DFillPresentationParameters(vd))
657         return VLC_EGENERIC;
658
659     // Create the D3DDevice
660     LPDIRECT3DDEVICE9 d3ddev;
661
662     UINT AdapterToUse = D3DADAPTER_DEFAULT;
663     D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
664
665 #ifndef NDEBUG
666     // Look for 'NVIDIA PerfHUD' adapter
667     // If it is present, override default settings
668     for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj); ++Adapter) {
669         D3DADAPTER_IDENTIFIER9 Identifier;
670         HRESULT Res = IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier);
671         if (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) {
672             AdapterToUse = Adapter;
673             DeviceType = D3DDEVTYPE_REF;
674             break;
675         }
676     }
677 #endif
678
679     /* */
680     D3DADAPTER_IDENTIFIER9 d3dai;
681     if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0, &d3dai))) {
682         msg_Warn(vd, "IDirect3D9_GetAdapterIdentifier failed");
683     } else {
684         msg_Dbg(vd, "Direct3d Device: %s %lu %lu %lu", d3dai.Description,
685                 d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
686     }
687
688     HRESULT hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
689                                          DeviceType, sys->hvideownd,
690                                          D3DCREATE_SOFTWARE_VERTEXPROCESSING|
691                                          D3DCREATE_MULTITHREADED,
692                                          &sys->d3dpp, &d3ddev);
693     if (FAILED(hr)) {
694        msg_Err(vd, "Could not create the D3D device! (hr=0x%lX)", hr);
695        return VLC_EGENERIC;
696     }
697     sys->d3ddev = d3ddev;
698
699     UpdateRects(vd, NULL, NULL, true);
700
701     if (Direct3DCreateResources(vd, fmt)) {
702         msg_Err(vd, "Failed to allocate resources");
703         return VLC_EGENERIC;
704     }
705
706     /* Change the window title bar text */
707     EventThreadUpdateTitle(sys->event, VOUT_TITLE " (Direct3D output)");
708
709     msg_Dbg(vd, "Direct3D device adapter successfully initialized");
710     return VLC_SUCCESS;
711 }
712
713 /**
714  * It releases the Direct3D9 device and its resources.
715  */
716 static void Direct3DClose(vout_display_t *vd)
717 {
718     vout_display_sys_t *sys = vd->sys;
719
720     Direct3DDestroyResources(vd);
721
722     if (sys->d3ddev)
723        IDirect3DDevice9_Release(sys->d3ddev);
724
725     sys->d3ddev = NULL;
726 }
727
728 /**
729  * It reset the Direct3D9 device and its resources.
730  */
731 static int Direct3DReset(vout_display_t *vd)
732 {
733     vout_display_sys_t *sys = vd->sys;
734     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
735
736     if (Direct3DFillPresentationParameters(vd))
737         return VLC_EGENERIC;
738
739     /* release all D3D objects */
740     Direct3DDestroyResources(vd);
741
742     /* */
743     HRESULT hr = IDirect3DDevice9_Reset(d3ddev, &sys->d3dpp);
744     if (FAILED(hr)) {
745         msg_Err(vd, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
746         return VLC_EGENERIC;
747     }
748
749     UpdateRects(vd, NULL, NULL, true);
750
751     /* re-create them */
752     if (Direct3DCreateResources(vd, &vd->fmt)) {
753         msg_Dbg(vd, "%s failed !", __FUNCTION__);
754         return VLC_EGENERIC;
755     }
756     return VLC_SUCCESS;
757 }
758
759 /* */
760 static int  Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt);
761 static void Direct3DDestroyPool(vout_display_t *vd);
762
763 static int  Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt);
764 static void Direct3DDestroyScene(vout_display_t *vd);
765
766 static int  Direct3DCreateShaders(vout_display_t *vd);
767 static void Direct3DDestroyShaders(vout_display_t *vd);
768
769 /**
770  * It creates the picture and scene resources.
771  */
772 static int Direct3DCreateResources(vout_display_t *vd, video_format_t *fmt)
773 {
774     vout_display_sys_t *sys = vd->sys;
775
776     if (Direct3DCreatePool(vd, fmt)) {
777         msg_Err(vd, "Direct3D picture pool initialization failed");
778         return VLC_EGENERIC;
779     }
780     if (Direct3DCreateScene(vd, fmt)) {
781         msg_Err(vd, "Direct3D scene initialization failed !");
782         return VLC_EGENERIC;
783     }
784     if (Direct3DCreateShaders(vd)) {
785         /* Failing to initialize shaders is not fatal. */
786         msg_Warn(vd, "Direct3D shaders initialization failed !");
787     }
788
789     sys->d3dregion_format = D3DFMT_UNKNOWN;
790     for (int i = 0; i < 2; i++) {
791         D3DFORMAT fmt = i == 0 ? D3DFMT_A8B8G8R8 : D3DFMT_A8R8G8B8;
792         if (SUCCEEDED(IDirect3D9_CheckDeviceFormat(sys->d3dobj,
793                                                    D3DADAPTER_DEFAULT,
794                                                    D3DDEVTYPE_HAL,
795                                                    sys->d3dpp.BackBufferFormat,
796                                                    D3DUSAGE_DYNAMIC,
797                                                    D3DRTYPE_TEXTURE,
798                                                    fmt))) {
799             sys->d3dregion_format = fmt;
800             break;
801         }
802     }
803     return VLC_SUCCESS;
804 }
805 /**
806  * It destroys the picture and scene resources.
807  */
808 static void Direct3DDestroyResources(vout_display_t *vd)
809 {
810     Direct3DDestroyScene(vd);
811     Direct3DDestroyPool(vd);
812     Direct3DDestroyShaders(vd);
813 }
814
815 /**
816  * It tests if the conversion from src to dst is supported.
817  */
818 static int Direct3DCheckConversion(vout_display_t *vd,
819                                    D3DFORMAT src, D3DFORMAT dst)
820 {
821     vout_display_sys_t *sys = vd->sys;
822     LPDIRECT3D9 d3dobj = sys->d3dobj;
823     HRESULT hr;
824
825     /* test whether device can create a surface of that format */
826     hr = IDirect3D9_CheckDeviceFormat(d3dobj, D3DADAPTER_DEFAULT,
827                                       D3DDEVTYPE_HAL, dst, 0,
828                                       D3DRTYPE_SURFACE, src);
829     if (SUCCEEDED(hr)) {
830         /* test whether device can perform color-conversion
831         ** from that format to target format
832         */
833         hr = IDirect3D9_CheckDeviceFormatConversion(d3dobj,
834                                                     D3DADAPTER_DEFAULT,
835                                                     D3DDEVTYPE_HAL,
836                                                     src, dst);
837     }
838     if (!SUCCEEDED(hr)) {
839         if (D3DERR_NOTAVAILABLE != hr)
840             msg_Err(vd, "Could not query adapter supported formats. (hr=0x%lX)", hr);
841         return VLC_EGENERIC;
842     }
843     return VLC_SUCCESS;
844 }
845
846 typedef struct
847 {
848     const char   *name;
849     D3DFORMAT    format;    /* D3D format */
850     vlc_fourcc_t fourcc;    /* VLC fourcc */
851     uint32_t     rmask;
852     uint32_t     gmask;
853     uint32_t     bmask;
854 } d3d_format_t;
855
856 static const d3d_format_t d3d_formats[] = {
857     /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
858     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12,  0,0,0 },
859     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
860     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
861     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
862     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
863     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
864     { "A8R8G8B8",   D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
865     { "8G8B8",      D3DFMT_R8G8B8,  VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
866     { "R5G6B5",     D3DFMT_R5G6B5,  VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5,  0x1f<<0 },
867     { "X1R5G5B5",   D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5,  0x1f<<0 },
868
869     { NULL, 0, 0, 0,0,0}
870 };
871
872 /**
873  * It returns the format (closest to chroma) that can be converted to target */
874 static const d3d_format_t *Direct3DFindFormat(vout_display_t *vd, vlc_fourcc_t chroma, D3DFORMAT target)
875 {
876     vout_display_sys_t *sys = vd->sys;
877
878     for (unsigned pass = 0; pass < 2; pass++) {
879         const vlc_fourcc_t *list;
880
881         if (pass == 0 && sys->allow_hw_yuv && vlc_fourcc_IsYUV(chroma))
882             list = vlc_fourcc_GetYUVFallback(chroma);
883         else if (pass == 1)
884             list = vlc_fourcc_GetRGBFallback(chroma);
885         else
886             continue;
887
888         for (unsigned i = 0; list[i] != 0; i++) {
889             for (unsigned j = 0; d3d_formats[j].name; j++) {
890                 const d3d_format_t *format = &d3d_formats[j];
891
892                 if (format->fourcc != list[i])
893                     continue;
894
895                 msg_Warn(vd, "trying surface pixel format: %s",
896                          format->name);
897                 if (!Direct3DCheckConversion(vd, format->format, target)) {
898                     msg_Dbg(vd, "selected surface pixel format is %s",
899                             format->name);
900                     return format;
901                 }
902             }
903         }
904     }
905     return NULL;
906 }
907
908 /**
909  * It locks the surface associated to the picture and get the surface
910  * descriptor which amongst other things has the pointer to the picture
911  * data and its pitch.
912  */
913 static int Direct3DLockSurface(picture_t *picture)
914 {
915     /* Lock the surface to get a valid pointer to the picture buffer */
916     D3DLOCKED_RECT d3drect;
917     HRESULT hr = IDirect3DSurface9_LockRect(picture->p_sys->surface, &d3drect, NULL, 0);
918     if (FAILED(hr)) {
919         //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
920         return CommonUpdatePicture(picture, &picture->p_sys->fallback, NULL, 0);
921     }
922
923     CommonUpdatePicture(picture, NULL, d3drect.pBits, d3drect.Pitch);
924     return VLC_SUCCESS;
925 }
926 /**
927  * It unlocks the surface associated to the picture.
928  */
929 static void Direct3DUnlockSurface(picture_t *picture)
930 {
931     /* Unlock the Surface */
932     HRESULT hr = IDirect3DSurface9_UnlockRect(picture->p_sys->surface);
933     if (FAILED(hr)) {
934         //msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
935     }
936 }
937
938 /**
939  * It creates the pool of picture (only 1).
940  *
941  * Each picture has an associated offscreen surface in video memory
942  * depending on hardware capabilities the picture chroma will be as close
943  * as possible to the orginal render chroma to reduce CPU conversion overhead
944  * and delegate this work to video card GPU
945  */
946 static int Direct3DCreatePool(vout_display_t *vd, video_format_t *fmt)
947 {
948     vout_display_sys_t *sys = vd->sys;
949     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
950
951     /* */
952     *fmt = vd->source;
953
954     /* Find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
955      * the requested chroma which is usable by the hardware in an offscreen surface, as they
956      * typically support more formats than textures */
957     const d3d_format_t *d3dfmt = Direct3DFindFormat(vd, fmt->i_chroma, sys->d3dpp.BackBufferFormat);
958     if (!d3dfmt) {
959         msg_Err(vd, "surface pixel format is not supported.");
960         return VLC_EGENERIC;
961     }
962     fmt->i_chroma = d3dfmt->fourcc;
963     fmt->i_rmask  = d3dfmt->rmask;
964     fmt->i_gmask  = d3dfmt->gmask;
965     fmt->i_bmask  = d3dfmt->bmask;
966
967     /* We create one picture.
968      * It is useless to create more as we can't be used for direct rendering */
969
970     /* Create a surface */
971     LPDIRECT3DSURFACE9 surface;
972     HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
973                                                               fmt->i_visible_width,
974                                                               fmt->i_visible_height,
975                                                               d3dfmt->format,
976                                                               D3DPOOL_DEFAULT,
977                                                               &surface,
978                                                               NULL);
979     if (FAILED(hr)) {
980         msg_Err(vd, "Failed to create picture surface. (hr=0x%lx)", hr);
981         return VLC_EGENERIC;
982     }
983     /* fill surface with black color */
984     IDirect3DDevice9_ColorFill(d3ddev, surface, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0));
985
986     /* Create the associated picture */
987     picture_sys_t *picsys = malloc(sizeof(*picsys));
988     if (unlikely(picsys == NULL)) {
989         IDirect3DSurface9_Release(surface);
990         return VLC_ENOMEM;
991     }
992     picsys->surface = surface;
993     picsys->fallback = NULL;
994
995     picture_resource_t resource = { .p_sys = picsys };
996     for (int i = 0; i < PICTURE_PLANE_MAX; i++)
997         resource.p[i].i_lines = fmt->i_visible_height / (i > 0 ? 2 : 1);
998
999     picture_t *picture = picture_NewFromResource(fmt, &resource);
1000     if (!picture) {
1001         IDirect3DSurface9_Release(surface);
1002         free(picsys);
1003         return VLC_ENOMEM;
1004     }
1005     sys->picsys = picsys;
1006
1007     /* Wrap it into a picture pool */
1008     picture_pool_configuration_t pool_cfg;
1009     memset(&pool_cfg, 0, sizeof(pool_cfg));
1010     pool_cfg.picture_count = 1;
1011     pool_cfg.picture       = &picture;
1012     pool_cfg.lock          = Direct3DLockSurface;
1013     pool_cfg.unlock        = Direct3DUnlockSurface;
1014
1015     sys->pool = picture_pool_NewExtended(&pool_cfg);
1016     if (!sys->pool) {
1017         picture_Release(picture);
1018         IDirect3DSurface9_Release(surface);
1019         return VLC_ENOMEM;
1020     }
1021     return VLC_SUCCESS;
1022 }
1023 /**
1024  * It destroys the pool of picture and its resources.
1025  */
1026 static void Direct3DDestroyPool(vout_display_t *vd)
1027 {
1028     vout_display_sys_t *sys = vd->sys;
1029
1030     if (sys->pool) {
1031         picture_sys_t *picsys = sys->picsys;
1032         IDirect3DSurface9_Release(picsys->surface);
1033         if (picsys->fallback)
1034             picture_Release(picsys->fallback);
1035         picture_pool_Delete(sys->pool);
1036     }
1037     sys->pool = NULL;
1038 }
1039
1040 /**
1041  * It allocates and initializes the resources needed to render the scene.
1042  */
1043 static int Direct3DCreateScene(vout_display_t *vd, const video_format_t *fmt)
1044 {
1045     vout_display_sys_t *sys = vd->sys;
1046     LPDIRECT3DDEVICE9       d3ddev = sys->d3ddev;
1047     HRESULT hr;
1048
1049     /*
1050      * Create a texture for use when rendering a scene
1051      * for performance reason, texture format is identical to backbuffer
1052      * which would usually be a RGB format
1053      */
1054     LPDIRECT3DTEXTURE9 d3dtex;
1055     hr = IDirect3DDevice9_CreateTexture(d3ddev,
1056                                         fmt->i_visible_width,
1057                                         fmt->i_visible_height,
1058                                         1,
1059                                         D3DUSAGE_RENDERTARGET,
1060                                         sys->d3dpp.BackBufferFormat,
1061                                         D3DPOOL_DEFAULT,
1062                                         &d3dtex,
1063                                         NULL);
1064     if (FAILED(hr)) {
1065         msg_Err(vd, "Failed to create texture. (hr=0x%lx)", hr);
1066         return VLC_EGENERIC;
1067     }
1068
1069     /*
1070     ** Create a vertex buffer for use when rendering scene
1071     */
1072     LPDIRECT3DVERTEXBUFFER9 d3dvtc;
1073     hr = IDirect3DDevice9_CreateVertexBuffer(d3ddev,
1074                                              sizeof(CUSTOMVERTEX)*4,
1075                                              D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1076                                              D3DFVF_CUSTOMVERTEX,
1077                                              D3DPOOL_DEFAULT,
1078                                              &d3dvtc,
1079                                              NULL);
1080     if (FAILED(hr)) {
1081         msg_Err(vd, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1082         IDirect3DTexture9_Release(d3dtex);
1083         return VLC_EGENERIC;
1084     }
1085
1086     /* */
1087     sys->d3dtex = d3dtex;
1088     sys->d3dvtc = d3dvtc;
1089
1090     sys->d3dregion_count = 0;
1091     sys->d3dregion       = NULL;
1092
1093     sys->clear_scene = true;
1094
1095     // Texture coordinates outside the range [0.0, 1.0] are set
1096     // to the texture color at 0.0 or 1.0, respectively.
1097     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1098     IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1099
1100     // Set linear filtering quality
1101     if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR) {
1102         msg_Dbg(vd, "Using D3DTEXF_LINEAR for minification");
1103         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1104     } else {
1105         msg_Dbg(vd, "Using D3DTEXF_POINT for minification");
1106         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
1107     }
1108     if (sys->d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR) {
1109         msg_Dbg(vd, "Using D3DTEXF_LINEAR for magnification");
1110         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1111     } else {
1112         msg_Dbg(vd, "Using D3DTEXF_POINT for magnification");
1113         IDirect3DDevice9_SetSamplerState(d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
1114     }
1115
1116     // set maximum ambient light
1117     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1118
1119     // Turn off culling
1120     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1121
1122     // Turn off the zbuffer
1123     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1124
1125     // Turn off lights
1126     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_LIGHTING, FALSE);
1127
1128     // Enable dithering
1129     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DITHERENABLE, TRUE);
1130
1131     // disable stencil
1132     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_STENCILENABLE, FALSE);
1133
1134     // manage blending
1135     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1136     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1137     IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1138
1139     if (sys->d3dcaps.AlphaCmpCaps & D3DPCMPCAPS_GREATER) {
1140         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1141         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAREF, 0x00);
1142         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1143     }
1144
1145     // Set texture states
1146     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLOROP,D3DTOP_SELECTARG1);
1147     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1148
1149     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
1150     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG1,D3DTA_TEXTURE);
1151     IDirect3DDevice9_SetTextureStageState(d3ddev, 0, D3DTSS_ALPHAARG2,D3DTA_DIFFUSE);
1152
1153     msg_Dbg(vd, "Direct3D scene created successfully");
1154
1155     return VLC_SUCCESS;
1156 }
1157
1158 /**
1159  * It releases the scene resources.
1160  */
1161 static void Direct3DDestroyScene(vout_display_t *vd)
1162 {
1163     vout_display_sys_t *sys = vd->sys;
1164
1165     Direct3DDeleteRegions(sys->d3dregion_count, sys->d3dregion);
1166
1167     LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1168     if (d3dvtc)
1169         IDirect3DVertexBuffer9_Release(d3dvtc);
1170
1171     LPDIRECT3DTEXTURE9 d3dtex = sys->d3dtex;
1172     if (d3dtex)
1173         IDirect3DTexture9_Release(d3dtex);
1174
1175     sys->d3dvtc = NULL;
1176     sys->d3dtex = NULL;
1177
1178     sys->d3dregion_count = 0;
1179     sys->d3dregion       = NULL;
1180
1181     msg_Dbg(vd, "Direct3D scene released successfully");
1182 }
1183
1184 static int Direct3DCompileShader(vout_display_t *vd, const char *shader_source, size_t source_length)
1185 {
1186     vout_display_sys_t *sys = vd->sys;
1187
1188     HRESULT (WINAPI * OurD3DXCompileShader)(
1189             LPCSTR pSrcData,
1190             UINT srcDataLen,
1191             const D3DXMACRO *pDefines,
1192             LPD3DXINCLUDE pInclude,
1193             LPCSTR pFunctionName,
1194             LPCSTR pProfile,
1195             DWORD Flags,
1196             LPD3DXBUFFER *ppShader,
1197             LPD3DXBUFFER *ppErrorMsgs,
1198             LPD3DXCONSTANTTABLE *ppConstantTable);
1199
1200     OurD3DXCompileShader = (void*)GetProcAddress(sys->hd3d9x_dll, "D3DXCompileShader");
1201     if (!OurD3DXCompileShader) {
1202         msg_Warn(vd, "Cannot locate reference to D3DXCompileShader; pixel shading will be disabled");
1203         return VLC_EGENERIC;
1204     }
1205
1206     LPD3DXBUFFER error_msgs = NULL;
1207     LPD3DXBUFFER compiled_shader = NULL;
1208
1209     DWORD shader_flags = 0;
1210     HRESULT hr = OurD3DXCompileShader(shader_source, source_length, NULL, NULL,
1211                 "main", "ps_3_0", shader_flags, &compiled_shader, &error_msgs, NULL);
1212
1213     if (FAILED(hr)) {
1214         msg_Warn(vd, "D3DXCompileShader Error (hr=0x%lX)", hr);
1215         if (error_msgs)
1216             msg_Warn(vd, "HLSL Compilation Error: %s", (char*)ID3DXBuffer_GetBufferPointer(error_msgs));
1217         return VLC_EGENERIC;
1218     }
1219
1220     hr = IDirect3DDevice9_CreatePixelShader(sys->d3ddev,
1221             ID3DXBuffer_GetBufferPointer(compiled_shader),
1222             &sys->d3dx_shader);
1223
1224     if (FAILED(hr)) {
1225         msg_Warn(vd, "IDirect3DDevice9_CreatePixelShader error (hr=0x%lX)", hr);
1226         return VLC_EGENERIC;
1227     }
1228     return VLC_SUCCESS;
1229 }
1230
1231 #define MAX_SHADER_FILE_SIZE 1024*1024
1232
1233 static int Direct3DCreateShaders(vout_display_t *vd)
1234 {
1235     vout_display_sys_t *sys = vd->sys;
1236
1237     if (!sys->hd3d9x_dll)
1238         return VLC_EGENERIC;
1239
1240     /* Find which shader was selected in the list. */
1241     char *selected_shader = var_InheritString(vd, "direct3d-shader");
1242     if (!selected_shader)
1243         return VLC_SUCCESS; /* Nothing to do */
1244
1245     const char *shader_source_builtin = NULL;
1246     char *shader_source_file = NULL;
1247     FILE *fs = NULL;
1248
1249     for (size_t i = 0; i < BUILTIN_SHADERS_COUNT; ++i) {
1250         if (!strcmp(selected_shader, builtin_shaders[i].name)) {
1251             shader_source_builtin = builtin_shaders[i].code;
1252             break;
1253         }
1254     }
1255
1256     if (shader_source_builtin) {
1257         /* A builtin shader was selected. */
1258         int err = Direct3DCompileShader(vd, shader_source_builtin, strlen(shader_source_builtin));
1259         if (err)
1260             goto error;
1261     } else {
1262         if (strcmp(selected_shader, SELECTED_SHADER_FILE))
1263             goto error; /* Unrecognized entry in the list. */
1264         /* The source code of the shader needs to be read from a file. */
1265         char *filepath = var_InheritString(vd, "direct3d-shader-file");
1266         if (!filepath || !*filepath)
1267         {
1268             free(filepath);
1269             goto error;
1270         }
1271         /* Open file, find its size with fseek/ftell and read its content in a buffer. */
1272         fs = fopen(filepath, "rb");
1273         if (!fs)
1274             goto error;
1275         int ret = fseek(fs, 0, SEEK_END);
1276         if (ret == -1)
1277             goto error;
1278         long length = ftell(fs);
1279         if (length == -1 || length >= MAX_SHADER_FILE_SIZE)
1280             goto error;
1281         rewind(fs);
1282         shader_source_file = malloc(sizeof(*shader_source_file) * length);
1283         if (!shader_source_file)
1284             goto error;
1285         ret = fread(shader_source_file, length, 1, fs);
1286         if (ret != 1)
1287             goto error;
1288         ret = Direct3DCompileShader(vd, shader_source_file, length);
1289         if (ret)
1290             goto error;
1291     }
1292
1293     free(selected_shader);
1294     free(shader_source_file);
1295     fclose(fs);
1296
1297     return VLC_SUCCESS;
1298
1299 error:
1300     Direct3DDestroyShaders(vd);
1301     free(selected_shader);
1302     free(shader_source_file);
1303     if (fs)
1304         fclose(fs);
1305     return VLC_EGENERIC;
1306 }
1307
1308 static void Direct3DDestroyShaders(vout_display_t *vd)
1309 {
1310     vout_display_sys_t *sys = vd->sys;
1311
1312     if (sys->d3dx_shader)
1313         IDirect3DPixelShader9_Release(sys->d3dx_shader);
1314     sys->d3dx_shader = NULL;
1315 }
1316
1317 /**
1318  * Compute the vertex ordering needed to rotate the video. Without
1319  * rotation, the vertices of the rectangle are defined in a clockwise
1320  * order. This function computes a remapping of the coordinates to
1321  * implement the rotation, given fixed texture coordinates.
1322  * The unrotated order is the following:
1323  * 0--1
1324  * |  |
1325  * 3--2
1326  * For a 180 degrees rotation it should like this:
1327  * 2--3
1328  * |  |
1329  * 1--0
1330  * Vertex 0 should be assigned coordinates at index 2 from the
1331  * unrotated order and so on, thus yielding order: 2 3 0 1.
1332  */
1333 static void orientationVertexOrder(video_orientation_t orientation, int vertex_order[static 4])
1334 {
1335     switch (orientation) {
1336         case ORIENT_ROTATED_90:
1337             vertex_order[0] = 1;
1338             vertex_order[1] = 2;
1339             vertex_order[2] = 3;
1340             vertex_order[3] = 0;
1341             break;
1342         case ORIENT_ROTATED_270:
1343             vertex_order[0] = 3;
1344             vertex_order[1] = 0;
1345             vertex_order[2] = 1;
1346             vertex_order[3] = 2;
1347             break;
1348         case ORIENT_ROTATED_180:
1349             vertex_order[0] = 2;
1350             vertex_order[1] = 3;
1351             vertex_order[2] = 0;
1352             vertex_order[3] = 1;
1353             break;
1354         case ORIENT_TRANSPOSED:
1355             vertex_order[0] = 0;
1356             vertex_order[1] = 3;
1357             vertex_order[2] = 2;
1358             vertex_order[3] = 1;
1359             break;
1360         case ORIENT_HFLIPPED:
1361             vertex_order[0] = 3;
1362             vertex_order[1] = 2;
1363             vertex_order[2] = 1;
1364             vertex_order[3] = 0;
1365             break;
1366         case ORIENT_VFLIPPED:
1367             vertex_order[0] = 1;
1368             vertex_order[1] = 0;
1369             vertex_order[2] = 3;
1370             vertex_order[3] = 2;
1371             break;
1372         case ORIENT_ANTI_TRANSPOSED: /* transpose + vflip */
1373             vertex_order[0] = 1;
1374             vertex_order[1] = 2;
1375             vertex_order[2] = 3;
1376             vertex_order[3] = 0;
1377             break;
1378        default:
1379             vertex_order[0] = 0;
1380             vertex_order[1] = 1;
1381             vertex_order[2] = 2;
1382             vertex_order[3] = 3;
1383             break;
1384     }
1385 }
1386
1387 static void Direct3DSetupVertices(CUSTOMVERTEX *vertices,
1388                                   const RECT src_full,
1389                                   const RECT src_crop,
1390                                   const RECT dst,
1391                                   int alpha,
1392                                   video_orientation_t orientation)
1393 {
1394     const float src_full_width  = src_full.right  - src_full.left;
1395     const float src_full_height = src_full.bottom - src_full.top;
1396
1397     /* Vertices of the dst rectangle in the unrotated (clockwise) order. */
1398     const int vertices_coords[4][2] = {
1399         { dst.left,  dst.top    },
1400         { dst.right, dst.top    },
1401         { dst.right, dst.bottom },
1402         { dst.left,  dst.bottom },
1403     };
1404
1405     /* Compute index remapping necessary to implement the rotation. */
1406     int vertex_order[4];
1407     orientationVertexOrder(orientation, vertex_order);
1408
1409     for (int i = 0; i < 4; ++i) {
1410         vertices[i].x  = vertices_coords[vertex_order[i]][0];
1411         vertices[i].y  = vertices_coords[vertex_order[i]][1];
1412     }
1413
1414     vertices[0].tu = src_crop.left / src_full_width;
1415     vertices[0].tv = src_crop.top  / src_full_height;
1416
1417     vertices[1].tu = src_crop.right / src_full_width;
1418     vertices[1].tv = src_crop.top   / src_full_height;
1419
1420     vertices[2].tu = src_crop.right  / src_full_width;
1421     vertices[2].tv = src_crop.bottom / src_full_height;
1422
1423     vertices[3].tu = src_crop.left   / src_full_width;
1424     vertices[3].tv = src_crop.bottom / src_full_height;
1425
1426     for (int i = 0; i < 4; i++) {
1427         /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1428         /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1429         vertices[i].x -= 0.5;
1430         vertices[i].y -= 0.5;
1431
1432         vertices[i].z       = 0.0f;
1433         vertices[i].rhw     = 1.0f;
1434         vertices[i].diffuse = D3DCOLOR_ARGB(alpha, 255, 255, 255);
1435     }
1436 }
1437
1438 /**
1439  * It copies picture surface into a texture and setup the associated d3d_region_t.
1440  */
1441 static int Direct3DImportPicture(vout_display_t *vd,
1442                                  d3d_region_t *region,
1443                                  LPDIRECT3DSURFACE9 source)
1444 {
1445     vout_display_sys_t *sys = vd->sys;
1446     HRESULT hr;
1447
1448     if (!source) {
1449         msg_Dbg(vd, "no surface to render ?");
1450         return VLC_EGENERIC;
1451     }
1452
1453     /* retrieve texture top-level surface */
1454     LPDIRECT3DSURFACE9 destination;
1455     hr = IDirect3DTexture9_GetSurfaceLevel(sys->d3dtex, 0, &destination);
1456     if (FAILED(hr)) {
1457         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1458         return VLC_EGENERIC;
1459     }
1460
1461     /* Copy picture surface into texture surface
1462      * color space conversion happen here */
1463     hr = IDirect3DDevice9_StretchRect(sys->d3ddev, source, NULL, destination, NULL, D3DTEXF_LINEAR);
1464     IDirect3DSurface9_Release(destination);
1465     if (FAILED(hr)) {
1466         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1467         return VLC_EGENERIC;
1468     }
1469
1470     /* */
1471     region->texture = sys->d3dtex;
1472     Direct3DSetupVertices(region->vertex,
1473                           vd->sys->rect_src,
1474                           vd->sys->rect_src_clipped,
1475                           vd->sys->rect_dest_clipped, 255, vd->fmt.orientation);
1476     return VLC_SUCCESS;
1477 }
1478
1479 static void Direct3DDeleteRegions(int count, d3d_region_t *region)
1480 {
1481     for (int i = 0; i < count; i++) {
1482         if (region[i].texture)
1483             IDirect3DTexture9_Release(region[i].texture);
1484     }
1485     free(region);
1486 }
1487
1488 static void Direct3DImportSubpicture(vout_display_t *vd,
1489                                      int *count_ptr, d3d_region_t **region,
1490                                      subpicture_t *subpicture)
1491 {
1492     vout_display_sys_t *sys = vd->sys;
1493
1494     int count = 0;
1495     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next)
1496         count++;
1497
1498     *count_ptr = count;
1499     *region    = calloc(count, sizeof(**region));
1500     if (*region == NULL) {
1501         *count_ptr = 0;
1502         return;
1503     }
1504
1505     int i = 0;
1506     for (subpicture_region_t *r = subpicture->p_region; r; r = r->p_next, i++) {
1507         d3d_region_t *d3dr = &(*region)[i];
1508         HRESULT hr;
1509
1510         d3dr->texture = NULL;
1511         for (int j = 0; j < sys->d3dregion_count; j++) {
1512             d3d_region_t *cache = &sys->d3dregion[j];
1513             if (cache->texture &&
1514                 cache->format == sys->d3dregion_format &&
1515                 cache->width  == r->fmt.i_visible_width &&
1516                 cache->height == r->fmt.i_visible_height) {
1517 #ifndef NDEBUG
1518                 msg_Dbg(vd, "Reusing %dx%d texture for OSD",
1519                         cache->width, cache->height);
1520 #endif
1521                 *d3dr = *cache;
1522                 memset(cache, 0, sizeof(*cache));
1523                 break;
1524             }
1525         }
1526         if (!d3dr->texture) {
1527             d3dr->format = sys->d3dregion_format;
1528             d3dr->width  = r->fmt.i_visible_width;
1529             d3dr->height = r->fmt.i_visible_height;
1530             hr = IDirect3DDevice9_CreateTexture(sys->d3ddev,
1531                                                 d3dr->width, d3dr->height,
1532                                                 1,
1533                                                 D3DUSAGE_DYNAMIC,
1534                                                 d3dr->format,
1535                                                 D3DPOOL_DEFAULT,
1536                                                 &d3dr->texture,
1537                                                 NULL);
1538             if (FAILED(hr)) {
1539                 d3dr->texture = NULL;
1540                 msg_Err(vd, "Failed to create %dx%d texture for OSD (hr=0x%0lX)",
1541                         d3dr->width, d3dr->height, hr);
1542                 continue;
1543             }
1544 #ifndef NDEBUG
1545             msg_Dbg(vd, "Created %dx%d texture for OSD",
1546                     r->fmt.i_visible_width, r->fmt.i_visible_height);
1547 #endif
1548         }
1549
1550         D3DLOCKED_RECT lock;
1551         hr = IDirect3DTexture9_LockRect(d3dr->texture, 0, &lock, NULL, 0);
1552         if (SUCCEEDED(hr)) {
1553             uint8_t  *dst_data   = lock.pBits;
1554             int       dst_pitch  = lock.Pitch;
1555             const int src_offset = r->fmt.i_y_offset * r->p_picture->p->i_pitch +
1556                                    r->fmt.i_x_offset * r->p_picture->p->i_pixel_pitch;
1557             uint8_t  *src_data   = &r->p_picture->p->p_pixels[src_offset];
1558             int       src_pitch  = r->p_picture->p->i_pitch;
1559             for (unsigned y = 0; y < r->fmt.i_visible_height; y++) {
1560                 int copy_pitch = __MIN(dst_pitch, r->p_picture->p->i_visible_pitch);
1561                 if (d3dr->format == D3DFMT_A8B8G8R8) {
1562                     memcpy(&dst_data[y * dst_pitch], &src_data[y * src_pitch],
1563                            copy_pitch);
1564                 } else {
1565                     for (int x = 0; x < copy_pitch; x += 4) {
1566                         dst_data[y * dst_pitch + x + 0] = src_data[y * src_pitch + x + 2];
1567                         dst_data[y * dst_pitch + x + 1] = src_data[y * src_pitch + x + 1];
1568                         dst_data[y * dst_pitch + x + 2] = src_data[y * src_pitch + x + 0];
1569                         dst_data[y * dst_pitch + x + 3] = src_data[y * src_pitch + x + 3];
1570                     }
1571                 }
1572             }
1573             hr = IDirect3DTexture9_UnlockRect(d3dr->texture, 0);
1574             if (FAILED(hr))
1575                 msg_Err(vd, "Failed to unlock the texture");
1576         } else {
1577             msg_Err(vd, "Failed to lock the texture");
1578         }
1579
1580         /* Map the subpicture to sys->rect_dest */
1581         RECT src;
1582         src.left   = 0;
1583         src.right  = src.left + r->fmt.i_visible_width;
1584         src.top    = 0;
1585         src.bottom = src.top  + r->fmt.i_visible_height;
1586
1587         const RECT video = sys->rect_dest;
1588         const float scale_w = (float)(video.right  - video.left) / subpicture->i_original_picture_width;
1589         const float scale_h = (float)(video.bottom - video.top)  / subpicture->i_original_picture_height;
1590
1591         RECT dst;
1592         dst.left   = video.left + scale_w * r->i_x,
1593         dst.right  = dst.left + scale_w * r->fmt.i_visible_width,
1594         dst.top    = video.top  + scale_h * r->i_y,
1595         dst.bottom = dst.top  + scale_h * r->fmt.i_visible_height,
1596         Direct3DSetupVertices(d3dr->vertex,
1597                               src, src, dst,
1598                               subpicture->i_alpha * r->i_alpha / 255, ORIENT_NORMAL);
1599     }
1600 }
1601
1602 static int Direct3DRenderRegion(vout_display_t *vd,
1603                                 d3d_region_t *region,
1604                                 bool use_pixel_shader)
1605 {
1606     vout_display_sys_t *sys = vd->sys;
1607
1608     LPDIRECT3DDEVICE9 d3ddev = vd->sys->d3ddev;
1609
1610     LPDIRECT3DVERTEXBUFFER9 d3dvtc = sys->d3dvtc;
1611     LPDIRECT3DTEXTURE9      d3dtex = region->texture;
1612
1613     HRESULT hr;
1614
1615     /* Import vertices */
1616     void *vertex;
1617     hr = IDirect3DVertexBuffer9_Lock(d3dvtc, 0, 0, &vertex, D3DLOCK_DISCARD);
1618     if (FAILED(hr)) {
1619         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1620         return -1;
1621     }
1622     memcpy(vertex, region->vertex, sizeof(region->vertex));
1623     hr = IDirect3DVertexBuffer9_Unlock(d3dvtc);
1624     if (FAILED(hr)) {
1625         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1626         return -1;
1627     }
1628
1629     // Setup our texture. Using textures introduces the texture stage states,
1630     // which govern how textures get blended together (in the case of multiple
1631     // textures) and lighting information. In this case, we are modulating
1632     // (blending) our texture with the diffuse color of the vertices.
1633     hr = IDirect3DDevice9_SetTexture(d3ddev, 0, (LPDIRECT3DBASETEXTURE9)d3dtex);
1634     if (FAILED(hr)) {
1635         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1636         return -1;
1637     }
1638
1639     if (sys->d3dx_shader) {
1640         if (use_pixel_shader)
1641         {
1642             hr = IDirect3DDevice9_SetPixelShader(d3ddev, sys->d3dx_shader);
1643             float shader_data[4] = { region->width, region->height, 0, 0 };
1644             hr = IDirect3DDevice9_SetPixelShaderConstantF(d3ddev, 0, shader_data, 1);
1645             if (FAILED(hr)) {
1646                 msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1647                 return -1;
1648             }
1649         }
1650         else /* Disable any existing pixel shader. */
1651             hr = IDirect3DDevice9_SetPixelShader(d3ddev, NULL);
1652         if (FAILED(hr)) {
1653             msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1654             return -1;
1655         }
1656     }
1657
1658     // Render the vertex buffer contents
1659     hr = IDirect3DDevice9_SetStreamSource(d3ddev, 0, d3dvtc, 0, sizeof(CUSTOMVERTEX));
1660     if (FAILED(hr)) {
1661         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1662         return -1;
1663     }
1664
1665     // we use FVF instead of vertex shader
1666     hr = IDirect3DDevice9_SetFVF(d3ddev, D3DFVF_CUSTOMVERTEX);
1667     if (FAILED(hr)) {
1668         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1669         return -1;
1670     }
1671
1672     // draw rectangle
1673     hr = IDirect3DDevice9_DrawPrimitive(d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1674     if (FAILED(hr)) {
1675         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1676         return -1;
1677     }
1678     return 0;
1679 }
1680
1681 /**
1682  * It renders the scene.
1683  *
1684  * This function is intented for higher end 3D cards, with pixel shader support
1685  * and at least 64 MiB of video RAM.
1686  */
1687 static void Direct3DRenderScene(vout_display_t *vd,
1688                                 d3d_region_t *picture,
1689                                 int subpicture_count,
1690                                 d3d_region_t *subpicture)
1691 {
1692     vout_display_sys_t *sys = vd->sys;
1693     LPDIRECT3DDEVICE9 d3ddev = sys->d3ddev;
1694     HRESULT hr;
1695
1696     if (sys->clear_scene) {
1697         /* Clear the backbuffer and the zbuffer */
1698         hr = IDirect3DDevice9_Clear(d3ddev, 0, NULL, D3DCLEAR_TARGET,
1699                                   D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
1700         if (FAILED(hr)) {
1701             msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1702             return;
1703         }
1704         sys->clear_scene = false;
1705     }
1706
1707     // Begin the scene
1708     hr = IDirect3DDevice9_BeginScene(d3ddev);
1709     if (FAILED(hr)) {
1710         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1711         return;
1712     }
1713
1714     Direct3DRenderRegion(vd, picture, true);
1715
1716     if (subpicture_count > 0)
1717         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1718     for (int i = 0; i < subpicture_count; i++) {
1719         d3d_region_t *r = &subpicture[i];
1720         if (r->texture)
1721             Direct3DRenderRegion(vd, r, false);
1722     }
1723     if (subpicture_count > 0)
1724         IDirect3DDevice9_SetRenderState(d3ddev, D3DRS_ALPHABLENDENABLE, FALSE);
1725
1726     // End the scene
1727     hr = IDirect3DDevice9_EndScene(d3ddev);
1728     if (FAILED(hr)) {
1729         msg_Dbg(vd, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1730         return;
1731     }
1732 }
1733
1734 /*****************************************************************************
1735  * DesktopCallback: desktop mode variable callback
1736  *****************************************************************************/
1737 static int DesktopCallback(vlc_object_t *object, char const *psz_cmd,
1738                             vlc_value_t oldval, vlc_value_t newval,
1739                             void *p_data)
1740 {
1741     vout_display_t *vd = (vout_display_t *)object;
1742     vout_display_sys_t *sys = vd->sys;
1743     VLC_UNUSED(psz_cmd);
1744     VLC_UNUSED(oldval);
1745     VLC_UNUSED(p_data);
1746
1747     vlc_mutex_lock(&sys->lock);
1748     const bool ch_desktop = !sys->desktop_requested != !newval.b_bool;
1749     sys->ch_desktop |= ch_desktop;
1750     sys->desktop_requested = newval.b_bool;
1751     vlc_mutex_unlock(&sys->lock);
1752     return VLC_SUCCESS;
1753 }
1754
1755 typedef struct
1756 {
1757     char **values;
1758     char **descs;
1759     size_t count;
1760 } enum_context_t;
1761
1762 static void ListShaders(enum_context_t *ctx)
1763 {
1764     size_t num_shaders = BUILTIN_SHADERS_COUNT;
1765     ctx->values = xrealloc(ctx->values, (ctx->count + num_shaders + 1) * sizeof(char *));
1766     ctx->descs = xrealloc(ctx->descs, (ctx->count + num_shaders + 1) * sizeof(char *));
1767     for (size_t i = 0; i < num_shaders; ++i) {
1768         ctx->values[ctx->count] = strdup(builtin_shaders[i].name);
1769         ctx->descs[ctx->count] = strdup(builtin_shaders[i].name);
1770         ctx->count++;
1771     }
1772     ctx->values[ctx->count] = strdup(SELECTED_SHADER_FILE);
1773     ctx->descs[ctx->count] = strdup(SELECTED_SHADER_FILE);
1774     ctx->count++;
1775 }
1776
1777 /* Populate the list of available shader techniques in the options */
1778 static int FindShadersCallback(vlc_object_t *object, const char *name,
1779                                char ***values, char ***descs)
1780 {
1781     VLC_UNUSED(object);
1782     VLC_UNUSED(name);
1783
1784     enum_context_t ctx = { NULL, NULL, 0 };
1785
1786     ListShaders(&ctx);
1787
1788     *values = ctx.values;
1789     *descs = ctx.descs;
1790     return ctx.count;
1791
1792 }