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