More logical closing sequence (d3d)
[vlc.git] / modules / video_output / msw / direct3d.c
1 /*****************************************************************************
2  * direct3d.c: Windows Direct3D video output module
3  *****************************************************************************
4  * Copyright (C) 2006-2009 the VideoLAN team
5  *$Id$
6  *
7  * Authors: Damien Fouilleul <damienf@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble:
26  *
27  * This plugin will use YUV surface if supported, using YUV will result in
28  * the best video quality (hardware filering when rescaling the picture)
29  * and the fastest display as it requires less processing.
30  *
31  * If YUV overlay is not supported this plugin will use RGB offscreen video
32  * surfaces that will be blitted onto the primary surface (display) to
33  * effectively display the pictures.
34  *
35  *****************************************************************************/
36 #include <errno.h>                                                 /* ENOMEM */
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_interface.h>
45 #include <vlc_playlist.h>
46 #include <vlc_vout.h>
47
48 #include <windows.h>
49 #include <d3d9.h>
50
51 #include "vout.h"
52
53 /*****************************************************************************
54  * Local prototypes.
55  *****************************************************************************/
56 static int  OpenVideo  ( vlc_object_t * );
57 static void CloseVideo ( vlc_object_t * );
58
59 static int  Init      ( vout_thread_t * );
60 static void End       ( vout_thread_t * );
61 static int  Manage    ( vout_thread_t * );
62 static void Display   ( vout_thread_t *, picture_t * );
63 static void FirstDisplay( vout_thread_t *, picture_t * );
64
65 static int Direct3DVoutCreate     ( vout_thread_t * );
66 static void Direct3DVoutRelease   ( vout_thread_t * );
67
68 static int  Direct3DVoutOpen      ( vout_thread_t * );
69 static void Direct3DVoutClose     ( vout_thread_t * );
70
71 static int Direct3DVoutResetDevice( vout_thread_t * );
72
73 static int Direct3DVoutCreatePictures   ( vout_thread_t *, size_t );
74 static void Direct3DVoutReleasePictures ( vout_thread_t * );
75
76 static int Direct3DVoutLockSurface  ( vout_thread_t *, picture_t * );
77 static int Direct3DVoutUnlockSurface( vout_thread_t *, picture_t * );
78
79 static int Direct3DVoutCreateScene      ( vout_thread_t * );
80 static void Direct3DVoutReleaseScene    ( vout_thread_t * );
81 static void Direct3DVoutRenderScene     ( vout_thread_t *, picture_t * );
82
83 static int DesktopCallback( vlc_object_t *p_this, char const *psz_cmd,
84                             vlc_value_t oldval, vlc_value_t newval,
85                             void *p_data );
86
87 /*****************************************************************************
88  * Module descriptor
89  *****************************************************************************/
90
91 static bool IsVistaOrAbove(void)
92 {
93     OSVERSIONINFO winVer;
94     winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
95
96     if( GetVersionEx(&winVer) )
97     {
98         if( winVer.dwMajorVersion > 5 )
99         {
100             /* Windows Vista or above, make this module the default */
101             return true;
102         }
103     }
104     /* Windows XP or lower, make sure this module isn't the default */
105     return false;
106 }
107
108 static int OpenVideoXP( vlc_object_t *obj )
109 {
110     return IsVistaOrAbove() ? VLC_EGENERIC : OpenVideo( obj );
111 }
112
113 static int OpenVideoVista( vlc_object_t *obj )
114 {
115     return IsVistaOrAbove() ? OpenVideo( obj ) : VLC_EGENERIC;
116 }
117
118 #define DESKTOP_TEXT N_("Enable desktop mode ")
119 #define DESKTOP_LONGTEXT N_( \
120     "The desktop mode allows you to display the video on the desktop." )
121
122 vlc_module_begin ()
123     set_shortname( "Direct3D" )
124     set_category( CAT_VIDEO )
125     set_subcategory( SUBCAT_VIDEO_VOUT )
126
127     add_bool( "direct3d-desktop", false, NULL, DESKTOP_TEXT, DESKTOP_LONGTEXT,
128               true )
129
130     set_description( N_("DirectX 3D video output") )
131     set_capability( "video output", 50 )
132     add_shortcut( "direct3d" )
133     set_callbacks( OpenVideoXP, CloseVideo )
134
135     /* FIXME: Hack to avoid unregistering our window class */
136     linked_with_a_crap_library_which_uses_atexit ()
137
138     add_submodule()
139         set_capability( "video output", 150 )
140         add_shortcut( "direct3d" )
141         set_callbacks( OpenVideoVista, CloseVideo )
142 vlc_module_end ()
143
144 #if 0 /* FIXME */
145     /* check if we registered a window class because we need to
146      * unregister it */
147     WNDCLASS wndclass;
148     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
149         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
150 #endif
151
152 /*****************************************************************************
153  * CUSTOMVERTEX:
154  *****************************************************************************
155  *****************************************************************************/
156 typedef struct
157 {
158     FLOAT       x,y,z;      // vertex untransformed position
159     FLOAT       rhw;        // eye distance
160     D3DCOLOR    diffuse;    // diffuse color
161     FLOAT       tu, tv;     // texture relative coordinates
162 } CUSTOMVERTEX;
163
164 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
165
166 /*****************************************************************************
167  * OpenVideo: allocate Vout video thread output method
168  *****************************************************************************
169  * This function allocates and initialize the Direct3D vout method.
170  *****************************************************************************/
171 static int OpenVideo( vlc_object_t *p_this )
172 {
173     vlc_value_t val;
174     vout_thread_t * p_vout = (vout_thread_t *)p_this;
175
176     /* Allocate structure */
177     p_vout->p_sys = calloc( 1, sizeof( vout_sys_t ) );
178     if( p_vout->p_sys == NULL )
179         return VLC_ENOMEM;
180
181     if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) )
182     {
183         msg_Err( p_vout, "Direct3D could not be initialized !");
184         Direct3DVoutRelease( p_vout );
185         free( p_vout->p_sys );
186         return VLC_EGENERIC;
187     }
188
189     p_vout->p_sys->b_desktop = false;
190
191     /* Initialisations */
192     p_vout->pf_init = Init;
193     p_vout->pf_end = End;
194     p_vout->pf_manage = Manage;
195     p_vout->pf_render = Direct3DVoutRenderScene;
196     p_vout->pf_display = FirstDisplay;
197     p_vout->pf_control = Control;
198
199     if( CommonInit( p_vout ) )
200         goto error;
201
202     var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
203     var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
204
205     /* Trigger a callback right now */
206     var_Create( p_vout, "direct3d-desktop", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
207     val.psz_string = _("Desktop");
208     var_Change( p_vout, "direct3d-desktop", VLC_VAR_SETTEXT, &val, NULL );
209     var_AddCallback( p_vout, "direct3d-desktop", DesktopCallback, NULL );
210     var_TriggerCallback( p_vout, "direct3d-desktop" );
211
212     return VLC_SUCCESS;
213
214 error:
215     CloseVideo( VLC_OBJECT(p_vout) );
216     return VLC_EGENERIC;
217 }
218
219 /*****************************************************************************
220  * CloseVideo: destroy Sys video thread output method
221  *****************************************************************************
222  * Terminate an output method created by Create
223  *****************************************************************************/
224 static void CloseVideo( vlc_object_t *p_this )
225 {
226     vout_thread_t * p_vout = (vout_thread_t *)p_this;
227
228     CommonClean( p_vout );
229
230     Direct3DVoutRelease( p_vout );
231
232     free( p_vout->p_sys );
233 }
234
235 /*****************************************************************************
236  * Init: initialize Direct3D video thread output method
237  *****************************************************************************/
238 static int Init( vout_thread_t *p_vout )
239 {
240     int i_ret;
241
242     p_vout->p_sys->b_hw_yuv = var_GetBool( p_vout, "directx-hw-yuv" );
243
244     /* Initialise Direct3D */
245     if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) )
246     {
247         msg_Err( p_vout, "cannot initialize Direct3D" );
248         return VLC_EGENERIC;
249     }
250
251     /* Initialize the output structure.
252      * Since Direct3D can do rescaling for us, stick to the default
253      * coordinates and aspect. */
254     p_vout->output.i_width  = p_vout->render.i_width;
255     p_vout->output.i_height = p_vout->render.i_height;
256     p_vout->output.i_aspect = p_vout->render.i_aspect;
257     p_vout->fmt_out = p_vout->fmt_in;
258     UpdateRects( p_vout, true );
259
260     /*  create picture pool */
261     p_vout->output.i_chroma = 0;
262     i_ret = Direct3DVoutCreatePictures(p_vout, 1);
263     if( VLC_SUCCESS != i_ret )
264     {
265         msg_Err(p_vout, "Direct3D picture pool initialization failed !");
266         return i_ret;
267     }
268
269     /* create scene */
270     i_ret = Direct3DVoutCreateScene(p_vout);
271     if( VLC_SUCCESS != i_ret )
272     {
273         msg_Err(p_vout, "Direct3D scene initialization failed !");
274         Direct3DVoutReleasePictures(p_vout);
275         return i_ret;
276     }
277
278     /* Change the window title bar text */
279     EventThreadUpdateTitle( p_vout->p_sys->p_event, VOUT_TITLE " (Direct3D output)" );
280
281     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
282     return VLC_SUCCESS;
283 }
284
285 /*****************************************************************************
286  * End: terminate Sys video thread output method
287  *****************************************************************************
288  * Terminate an output method created by Create.
289  * It is called at the end of the thread.
290  *****************************************************************************/
291 static void End( vout_thread_t *p_vout )
292 {
293     Direct3DVoutReleaseScene(p_vout);
294     Direct3DVoutReleasePictures(p_vout);
295     Direct3DVoutClose( p_vout );
296 }
297
298 /*****************************************************************************
299  * Manage: handle Sys events
300  *****************************************************************************
301  * This function should be called regularly by the video output thread.
302  * It returns a non null value if an error occurred.
303  *****************************************************************************/
304 static int Manage( vout_thread_t *p_vout )
305 {
306     vout_sys_t *p_sys = p_vout->p_sys;
307
308     CommonManage( p_vout );
309
310     /*
311      * Position Change
312      */
313     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
314     {
315 #if 0 /* need that when bicubic filter is available */
316         RECT rect;
317         UINT width, height;
318
319         GetClientRect(p_vout->p_sys->hvideownd, &rect);
320         width  = rect.right-rect.left;
321         height = rect.bottom-rect.top;
322
323         if( (width != p_vout->p_sys->d3dpp.BackBufferWidth)
324          || (height != p_vout->p_sys->d3dpp.BackBufferHeight) )
325         {
326             msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height);
327             // need to reset D3D device to resize back buffer
328             if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) )
329                 return VLC_EGENERIC;
330         }
331 #endif
332         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
333     }
334
335     /*
336      * Desktop mode change
337      */
338     if( p_vout->p_sys->i_changes & DX_DESKTOP_CHANGE )
339     {
340         /* Close the direct3d instance attached to the current output window. */
341         End( p_vout );
342
343         ExitFullscreen( p_vout );
344
345         EventThreadStop( p_vout->p_sys->p_event );
346
347         /* Open the direct3d output and attaches it to the new window */
348         p_vout->p_sys->b_desktop = !p_vout->p_sys->b_desktop;
349         p_vout->pf_display = FirstDisplay;
350
351         event_cfg_t cfg;
352         memset(&cfg, 0, sizeof(cfg));
353         cfg.use_desktop = p_vout->p_sys->b_desktop;
354
355         event_hwnd_t hwnd;
356         EventThreadStart( p_vout->p_sys->p_event, &hwnd, &cfg );
357
358         p_sys->parent_window = hwnd.parent_window;
359         p_sys->hparent       = hwnd.hparent;
360         p_sys->hwnd          = hwnd.hwnd;
361         p_sys->hvideownd     = hwnd.hvideownd;
362         p_sys->hfswnd        = hwnd.hfswnd;
363
364         Init( p_vout );
365
366         /* Reset the flag */
367         p_vout->p_sys->i_changes &= ~DX_DESKTOP_CHANGE;
368     }
369
370     return VLC_SUCCESS;
371 }
372
373 /*****************************************************************************
374  * Display: displays previously rendered output
375  *****************************************************************************
376  * This function sends the currently rendered image to the display, wait until
377  * it is displayed and switch the two rendering buffers, preparing next frame.
378  *****************************************************************************/
379 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
380 {
381     VLC_UNUSED( p_pic );
382
383     LPDIRECT3DDEVICE9       p_d3ddev = p_vout->p_sys->p_d3ddev;
384
385     // Present the back buffer contents to the display
386     // stretching and filtering happens here
387     HRESULT hr = IDirect3DDevice9_Present(p_d3ddev,
388                     &(p_vout->p_sys->rect_src_clipped),
389                     NULL, NULL, NULL);
390     if( FAILED(hr) )
391         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
392 }
393
394 /*
395 ** this function is only used once when the first picture is received
396 ** this function will show the video window once a picture is ready
397 */
398
399 static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic )
400 {
401     /* get initial picture presented through D3D */
402     Display(p_vout, p_pic);
403
404     /*
405     ** Video window is initially hidden, show it now since we got a
406     ** picture to show.
407     */
408     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
409         SWP_ASYNCWINDOWPOS|
410         SWP_FRAMECHANGED|
411         SWP_SHOWWINDOW|
412         SWP_NOMOVE|
413         SWP_NOSIZE|
414         SWP_NOZORDER );
415
416     /* use and restores proper display function for further pictures */
417     p_vout->pf_display = Display;
418 }
419
420 /*****************************************************************************
421  * DirectD3DVoutCreate: Initialize and instance of Direct3D9
422  *****************************************************************************
423  * This function initialize Direct3D and analyze available resources from
424  * default adapter.
425  *****************************************************************************/
426 static int Direct3DVoutCreate( vout_thread_t *p_vout )
427 {
428     HRESULT hr;
429     LPDIRECT3D9 p_d3dobj;
430     D3DCAPS9 d3dCaps;
431
432     LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
433
434     p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
435     if( NULL == p_vout->p_sys->hd3d9_dll )
436     {
437         msg_Warn( p_vout, "cannot load d3d9.dll, aborting" );
438         return VLC_EGENERIC;
439     }
440
441     OurDirect3DCreate9 =
442       (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll,
443                               TEXT("Direct3DCreate9") );
444     if( OurDirect3DCreate9 == NULL )
445     {
446         msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" );
447         return VLC_EGENERIC;
448     }
449
450     /* Create the D3D object. */
451     p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION );
452     if( NULL == p_d3dobj )
453     {
454        msg_Err( p_vout, "Could not create Direct3D9 instance.");
455        return VLC_EGENERIC;
456     }
457     p_vout->p_sys->p_d3dobj = p_d3dobj;
458
459     /*
460     ** Get device capabilities
461     */
462     ZeroMemory(&d3dCaps, sizeof(d3dCaps));
463     hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
464     if( FAILED(hr) )
465     {
466        msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr);
467        return VLC_EGENERIC;
468     }
469     /* TODO: need to test device capabilities and select the right render function */
470
471     return VLC_SUCCESS;
472 }
473
474 /*****************************************************************************
475  * DirectD3DVoutRelease: release an instance of Direct3D9
476  *****************************************************************************/
477
478 static void Direct3DVoutRelease( vout_thread_t *p_vout )
479 {
480     if( p_vout->p_sys->p_d3dobj )
481     {
482        IDirect3D9_Release(p_vout->p_sys->p_d3dobj);
483        p_vout->p_sys->p_d3dobj = NULL;
484     }
485     if( NULL != p_vout->p_sys->hd3d9_dll )
486     {
487         FreeLibrary(p_vout->p_sys->hd3d9_dll);
488         p_vout->p_sys->hd3d9_dll = NULL;
489     }
490 }
491
492 static int Direct3DFillPresentationParameters(vout_thread_t *p_vout, D3DPRESENT_PARAMETERS *d3dpp)
493 {
494     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
495     D3DDISPLAYMODE d3ddm;
496     HRESULT hr;
497
498     /*
499     ** Get the current desktop display mode, so we can set up a back
500     ** buffer of the same format
501     */
502     hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
503     if( FAILED(hr))
504     {
505        msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr);
506        return VLC_EGENERIC;
507     }
508
509     /* keep a copy of current desktop format */
510     p_vout->p_sys->bbFormat = d3ddm.Format;
511
512     /* Set up the structure used to create the D3DDevice. */
513     ZeroMemory( d3dpp, sizeof(D3DPRESENT_PARAMETERS) );
514     d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
515     d3dpp->Windowed               = TRUE;
516     d3dpp->hDeviceWindow          = p_vout->p_sys->hvideownd;
517     d3dpp->BackBufferWidth        = p_vout->output.i_width;
518     d3dpp->BackBufferHeight       = p_vout->output.i_height;
519     d3dpp->SwapEffect             = D3DSWAPEFFECT_COPY;
520     d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
521     d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
522     d3dpp->BackBufferFormat       = d3ddm.Format;
523     d3dpp->BackBufferCount        = 1;
524     d3dpp->EnableAutoDepthStencil = FALSE;
525
526     return VLC_SUCCESS;
527 }
528
529 /*****************************************************************************
530  * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations
531  *****************************************************************************
532  * This function creates Direct3D device
533  * this must be called from the vout thread for performance reason, as
534  * all Direct3D Device APIs are used in a non multithread safe environment
535  *****************************************************************************/
536 static int Direct3DVoutOpen( vout_thread_t *p_vout )
537 {
538     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
539     LPDIRECT3DDEVICE9 p_d3ddev;
540     D3DPRESENT_PARAMETERS d3dpp;
541     HRESULT hr;
542
543     if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
544         return VLC_EGENERIC;
545
546     // Create the D3DDevice
547     hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT,
548                                  D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd,
549                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING|
550                                  D3DCREATE_MULTITHREADED,
551                                  &d3dpp, &p_d3ddev );
552     if( FAILED(hr) )
553     {
554        msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr);
555        return VLC_EGENERIC;
556     }
557     p_vout->p_sys->p_d3ddev = p_d3ddev;
558
559     msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" );
560     return VLC_SUCCESS;
561 }
562
563 /*****************************************************************************
564  * DirectD3DClose: release the Direct3D9 device
565  *****************************************************************************/
566 static void Direct3DVoutClose( vout_thread_t *p_vout )
567 {
568     if( p_vout->p_sys->p_d3ddev )
569     {
570        IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev);
571        p_vout->p_sys->p_d3ddev = NULL;
572     }
573
574     p_vout->p_sys->hmonitor = NULL;
575 }
576
577 /*****************************************************************************
578  * DirectD3DClose: reset the Direct3D9 device
579  *****************************************************************************
580  * All resources must be deallocated before the reset occur, they will be
581  * realllocated once the reset has been performed successfully
582  *****************************************************************************/
583 static int Direct3DVoutResetDevice( vout_thread_t *p_vout )
584 {
585     LPDIRECT3DDEVICE9       p_d3ddev = p_vout->p_sys->p_d3ddev;
586     D3DPRESENT_PARAMETERS   d3dpp;
587     HRESULT hr;
588
589     if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
590         return VLC_EGENERIC;
591
592     // release all D3D objects
593     Direct3DVoutReleaseScene( p_vout );
594     Direct3DVoutReleasePictures( p_vout );
595
596     hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp);
597     if( SUCCEEDED(hr) )
598     {
599         // re-create them
600         if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1))
601          || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) )
602         {
603             msg_Dbg(p_vout, "%s failed !", __FUNCTION__);
604             return VLC_EGENERIC;
605         }
606     }
607     else {
608         msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
609         return VLC_EGENERIC;
610     }
611     return VLC_SUCCESS;
612 }
613
614 static int Direct3DVoutCheckFormat( vout_thread_t *p_vout,
615                                     D3DFORMAT target, D3DFORMAT format )
616 {
617     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
618     HRESULT hr;
619
620     /* test whether device can create a surface of that format */
621     hr = IDirect3D9_CheckDeviceFormat(p_d3dobj, D3DADAPTER_DEFAULT,
622             D3DDEVTYPE_HAL, target, 0, D3DRTYPE_SURFACE, format);
623     if( SUCCEEDED(hr) )
624     {
625         /* test whether device can perform color-conversion
626         ** from that format to target format
627         */
628         hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
629                                                     D3DADAPTER_DEFAULT,
630                                                     D3DDEVTYPE_HAL,
631                                                     format, target);
632     }
633     if( !SUCCEEDED(hr) )
634     {
635         if( D3DERR_NOTAVAILABLE != hr )
636             msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
637         return VLC_EGENERIC;
638     }
639     return VLC_SUCCESS;
640 }
641
642 typedef struct
643 {
644     const char   *name;
645     D3DFORMAT    format;
646     vlc_fourcc_t fourcc;
647     uint32_t     rmask;
648     uint32_t     gmask;
649     uint32_t     bmask;
650 } d3d_format_t;
651
652 static const d3d_format_t p_d3d_formats[] = {
653     /* YV12 is always used for planar 420, the planes are then swapped in Lock() */
654     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12,  0,0,0 },
655     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_I420,  0,0,0 },
656     { "YV12",       MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_J420,  0,0,0 },
657     { "UYVY",       D3DFMT_UYVY,    VLC_CODEC_UYVY,  0,0,0 },
658     { "YUY2",       D3DFMT_YUY2,    VLC_CODEC_YUYV,  0,0,0 },
659     { "X8R8G8B8",   D3DFMT_X8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
660     { "A8R8G8B8",   D3DFMT_A8R8G8B8,VLC_CODEC_RGB32, 0xff0000, 0x00ff00, 0x0000ff },
661     { "8G8B8",      D3DFMT_R8G8B8,  VLC_CODEC_RGB24, 0xff0000, 0x00ff00, 0x0000ff },
662     { "R5G6B5",     D3DFMT_R5G6B5,  VLC_CODEC_RGB16, 0x1f<<11, 0x3f<<5,  0x1f<<0 },
663     { "X1R5G5B5",   D3DFMT_X1R5G5B5,VLC_CODEC_RGB15, 0x1f<<10, 0x1f<<5,  0x1f<<0 },
664
665     { NULL, 0, 0, 0,0,0}
666 };
667
668 static const d3d_format_t *Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
669 {
670     for( unsigned pass = 0; pass < 2; pass++ )
671     {
672         const vlc_fourcc_t *p_chromas;
673
674         if( pass == 0 && p_vout->p_sys->b_hw_yuv && vlc_fourcc_IsYUV( i_chroma ) )
675             p_chromas = vlc_fourcc_GetYUVFallback( i_chroma );
676         else if( pass == 1 )
677             p_chromas = vlc_fourcc_GetRGBFallback( i_chroma );
678         else
679             continue;
680
681         for( unsigned i = 0; p_chromas[i] != 0; i++ )
682         {
683             for( unsigned j = 0; p_d3d_formats[j].name; j++ )
684             {
685                 const d3d_format_t *p_format = &p_d3d_formats[j];
686
687                 if( p_format->fourcc != p_chromas[i] )
688                     continue;
689
690                 msg_Warn( p_vout, "trying surface pixel format: %s",
691                           p_format->name );
692                 if( !Direct3DVoutCheckFormat( p_vout, target, p_format->format ) )
693                 {
694                     msg_Dbg( p_vout, "selected surface pixel format is %s",
695                              p_format->name );
696                     return p_format;
697                 }
698             }
699         }
700     }
701     return NULL;
702 }
703
704 /*****************************************************************************
705  * Direct3DVoutCreatePictures: allocate a vector of identical pictures
706  *****************************************************************************
707  * Each picture has an associated offscreen surface in video memory
708  * depending on hardware capabilities the picture chroma will be as close
709  * as possible to the orginal render chroma to reduce CPU conversion overhead
710  * and delegate this work to video card GPU
711  *****************************************************************************/
712 static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics )
713 {
714     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
715     HRESULT hr;
716     size_t c;
717     // if vout is already running, use current chroma, otherwise choose from upstream
718     int i_chroma = p_vout->output.i_chroma ? p_vout->output.i_chroma
719                                            : p_vout->render.i_chroma;
720
721     I_OUTPUTPICTURES = 0;
722
723     /*
724     ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
725     ** the requested chroma which is usable by the hardware in an offscreen surface, as they
726     ** typically support more formats than textures
727     */
728     const d3d_format_t *p_format = Direct3DVoutFindFormat(p_vout, i_chroma, p_vout->p_sys->bbFormat);
729     if( !p_format )
730     {
731         msg_Err(p_vout, "surface pixel format is not supported.");
732         return VLC_EGENERIC;
733     }
734     p_vout->output.i_chroma = p_format->fourcc;
735     p_vout->output.i_rmask  = p_format->rmask;
736     p_vout->output.i_gmask  = p_format->gmask;
737     p_vout->output.i_bmask  = p_format->bmask;
738
739     /* */
740     for( c=0; c<i_num_pics; )
741     {
742
743         LPDIRECT3DSURFACE9 p_d3dsurf;
744         picture_t *p_pic = p_vout->p_picture+c;
745
746         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev,
747                 p_vout->render.i_width,
748                 p_vout->render.i_height,
749                 p_format->format,
750                 D3DPOOL_DEFAULT,
751                 &p_d3dsurf,
752                 NULL);
753         if( FAILED(hr) )
754         {
755             msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr);
756             Direct3DVoutReleasePictures(p_vout);
757             return VLC_EGENERIC;
758         }
759
760         /* fill surface with black color */
761         IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
762
763         /* */
764         video_format_Setup( &p_pic->format, p_vout->output.i_chroma, p_vout->output.i_width, p_vout->output.i_height, p_vout->output.i_aspect );
765
766         /* assign surface to internal structure */
767         p_pic->p_sys = (void *)p_d3dsurf;
768
769         /* Now that we've got our direct-buffer, we can finish filling in the
770          * picture_t structures */
771         switch( p_vout->output.i_chroma )
772         {
773             case VLC_CODEC_RGB8:
774                 p_pic->p->i_lines = p_vout->output.i_height;
775                 p_pic->p->i_visible_lines = p_vout->output.i_height;
776                 p_pic->p->i_pixel_pitch = 1;
777                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
778                     p_pic->p->i_pixel_pitch;
779                 p_pic->i_planes = 1;
780             break;
781             case VLC_CODEC_RGB15:
782             case VLC_CODEC_RGB16:
783                 p_pic->p->i_lines = p_vout->output.i_height;
784                 p_pic->p->i_visible_lines = p_vout->output.i_height;
785                 p_pic->p->i_pixel_pitch = 2;
786                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
787                     p_pic->p->i_pixel_pitch;
788                 p_pic->i_planes = 1;
789             break;
790             case VLC_CODEC_RGB24:
791                 p_pic->p->i_lines = p_vout->output.i_height;
792                 p_pic->p->i_visible_lines = p_vout->output.i_height;
793                 p_pic->p->i_pixel_pitch = 3;
794                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
795                     p_pic->p->i_pixel_pitch;
796                 p_pic->i_planes = 1;
797             break;
798             case VLC_CODEC_RGB32:
799                 p_pic->p->i_lines = p_vout->output.i_height;
800                 p_pic->p->i_visible_lines = p_vout->output.i_height;
801                 p_pic->p->i_pixel_pitch = 4;
802                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
803                     p_pic->p->i_pixel_pitch;
804                 p_pic->i_planes = 1;
805                 break;
806             case VLC_CODEC_UYVY:
807             case VLC_CODEC_YUYV:
808                 p_pic->p->i_lines = p_vout->output.i_height;
809                 p_pic->p->i_visible_lines = p_vout->output.i_height;
810                 p_pic->p->i_pixel_pitch = 2;
811                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
812                     p_pic->p->i_pixel_pitch;
813                 p_pic->i_planes = 1;
814                 break;
815             case VLC_CODEC_I420:
816             case VLC_CODEC_J420:
817             case VLC_CODEC_YV12:
818                 p_pic->i_planes = 3;
819                 for( int n = 0; n < p_pic->i_planes; n++ )
820                 {
821                     const unsigned d = 1 + (n > 0);
822                     plane_t *p = &p_pic->p[n];
823
824                     p->i_pixel_pitch = 1;
825                     p->i_lines         =
826                     p->i_visible_lines = p_vout->output.i_height / d;
827                     p->i_visible_pitch = p_vout->output.i_width / d;
828                 }
829                 break;
830             default:
831                 Direct3DVoutReleasePictures(p_vout);
832                 return VLC_EGENERIC;
833         }
834         p_pic->i_status = DESTROYED_PICTURE;
835         p_pic->i_type   = DIRECT_PICTURE;
836         p_pic->b_slow   = true;
837         p_pic->pf_lock  = Direct3DVoutLockSurface;
838         p_pic->pf_unlock = Direct3DVoutUnlockSurface;
839         PP_OUTPUTPICTURE[c] = p_pic;
840
841         I_OUTPUTPICTURES = ++c;
842     }
843
844     msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c );
845
846     return VLC_SUCCESS;
847 }
848
849 /*****************************************************************************
850  * Direct3DVoutReleasePictures: destroy a picture vector
851  *****************************************************************************
852  * release all video resources used for pictures
853  *****************************************************************************/
854 static void Direct3DVoutReleasePictures( vout_thread_t *p_vout)
855 {
856     size_t i_num_pics = I_OUTPUTPICTURES;
857     size_t c;
858     for( c=0; c<i_num_pics; ++c )
859     {
860         picture_t *p_pic = p_vout->p_picture+c;
861         if( p_pic->p_sys )
862         {
863             LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
864
865             p_pic->p_sys = NULL;
866
867             if( p_d3dsurf )
868             {
869                 IDirect3DSurface9_Release(p_d3dsurf);
870             }
871         }
872     }
873     msg_Dbg( p_vout, "%u Direct3D pictures released.", c);
874
875     I_OUTPUTPICTURES = 0;
876 }
877
878 /*****************************************************************************
879  * Direct3DVoutLockSurface: Lock surface and get picture data pointer
880  *****************************************************************************
881  * This function locks a surface and get the surface descriptor which amongst
882  * other things has the pointer to the picture data.
883  *****************************************************************************/
884 static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
885 {
886     HRESULT hr;
887     D3DLOCKED_RECT d3drect;
888     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
889
890     if( NULL == p_d3dsurf )
891         return VLC_EGENERIC;
892
893     /* Lock the surface to get a valid pointer to the picture buffer */
894     hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0);
895     if( FAILED(hr) )
896     {
897         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
898         return VLC_EGENERIC;
899     }
900
901     /* fill in buffer info in first plane */
902     p_pic->p->p_pixels = d3drect.pBits;
903     p_pic->p->i_pitch  = d3drect.Pitch;
904
905     /*  Fill chroma planes for planar YUV */
906     if( p_pic->format.i_chroma == VLC_CODEC_I420 ||
907         p_pic->format.i_chroma == VLC_CODEC_J420 ||
908         p_pic->format.i_chroma == VLC_CODEC_YV12 )
909     {
910         for( int n = 1; n < p_pic->i_planes; n++ )
911         {
912             const plane_t *o = &p_pic->p[n-1];
913             plane_t *p = &p_pic->p[n];
914
915             p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
916             p->i_pitch  = d3drect.Pitch / 2;
917         }
918         /* The d3d buffer is always allocated as YV12 */
919         if( vlc_fourcc_AreUVPlanesSwapped( p_pic->format.i_chroma, VLC_CODEC_YV12 ) )
920         {
921             uint8_t *p_tmp = p_pic->p[1].p_pixels;
922             p_pic->p[1].p_pixels = p_pic->p[2].p_pixels;
923             p_pic->p[2].p_pixels = p_tmp;
924         }
925     }
926
927     return VLC_SUCCESS;
928 }
929
930 /*****************************************************************************
931  * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface().
932  *****************************************************************************/
933 static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
934 {
935     HRESULT hr;
936     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
937
938     if( NULL == p_d3dsurf )
939         return VLC_EGENERIC;
940
941     /* Unlock the Surface */
942     hr = IDirect3DSurface9_UnlockRect(p_d3dsurf);
943     if( FAILED(hr) )
944     {
945         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
946         return VLC_EGENERIC;
947     }
948     return VLC_SUCCESS;
949 }
950
951 /*****************************************************************************
952  * Direct3DVoutCreateScene: allocate and initialize a 3D scene
953  *****************************************************************************
954  * for advanced blending/filtering a texture needs be used in a 3D scene.
955  *****************************************************************************/
956
957 static int Direct3DVoutCreateScene( vout_thread_t *p_vout )
958 {
959     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
960     LPDIRECT3DTEXTURE9      p_d3dtex;
961     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
962
963     HRESULT hr;
964
965     /*
966     ** Create a texture for use when rendering a scene
967     ** for performance reason, texture format is identical to backbuffer
968     ** which would usually be a RGB format
969     */
970     hr = IDirect3DDevice9_CreateTexture(p_d3ddev,
971             p_vout->render.i_width,
972             p_vout->render.i_height,
973             1,
974             D3DUSAGE_RENDERTARGET,
975             p_vout->p_sys->bbFormat,
976             D3DPOOL_DEFAULT,
977             &p_d3dtex,
978             NULL);
979     if( FAILED(hr))
980     {
981         msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr);
982         return VLC_EGENERIC;
983     }
984
985     /*
986     ** Create a vertex buffer for use when rendering scene
987     */
988     hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev,
989             sizeof(CUSTOMVERTEX)*4,
990             D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
991             D3DFVF_CUSTOMVERTEX,
992             D3DPOOL_DEFAULT,
993             &p_d3dvtc,
994             NULL);
995     if( FAILED(hr) )
996     {
997         msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr);
998             IDirect3DTexture9_Release(p_d3dtex);
999         return VLC_EGENERIC;
1000     }
1001
1002     p_vout->p_sys->p_d3dtex = p_d3dtex;
1003     p_vout->p_sys->p_d3dvtc = p_d3dvtc;
1004
1005     // Texture coordinates outside the range [0.0, 1.0] are set
1006     // to the texture color at 0.0 or 1.0, respectively.
1007     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1008     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1009
1010     // Set linear filtering quality
1011     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1012     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1013
1014     // set maximum ambient light
1015     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1016
1017     // Turn off culling
1018     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1019
1020     // Turn off the zbuffer
1021     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1022
1023     // Turn off lights
1024     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE);
1025
1026     // Enable dithering
1027     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE);
1028
1029     // disable stencil
1030     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE);
1031
1032     // manage blending
1033     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1034     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1035     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1036     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1037     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10);
1038     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1039
1040     // Set texture states
1041     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
1042     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1043     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
1044
1045     // turn off alpha operation
1046     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1047
1048     msg_Dbg( p_vout, "Direct3D scene created successfully");
1049
1050     return VLC_SUCCESS;
1051 }
1052
1053 /*****************************************************************************
1054  * Direct3DVoutReleaseScene
1055  *****************************************************************************/
1056 static void Direct3DVoutReleaseScene( vout_thread_t *p_vout )
1057 {
1058     LPDIRECT3DTEXTURE9      p_d3dtex = p_vout->p_sys->p_d3dtex;
1059     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1060
1061     if( p_d3dvtc )
1062     {
1063         IDirect3DVertexBuffer9_Release(p_d3dvtc);
1064         p_vout->p_sys->p_d3dvtc = NULL;
1065     }
1066
1067     if( p_d3dtex )
1068     {
1069         IDirect3DTexture9_Release(p_d3dtex);
1070         p_vout->p_sys->p_d3dtex = NULL;
1071     }
1072     msg_Dbg( p_vout, "Direct3D scene released successfully");
1073 }
1074
1075 /*****************************************************************************
1076  * Render: copy picture surface into a texture and render into a scene
1077  *****************************************************************************
1078  * This function is intented for higher end 3D cards, with pixel shader support
1079  * and at least 64 MB of video RAM.
1080  *****************************************************************************/
1081 static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic )
1082 {
1083     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1084     LPDIRECT3DTEXTURE9      p_d3dtex;
1085     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1086     LPDIRECT3DSURFACE9      p_d3dsrc, p_d3ddest;
1087     CUSTOMVERTEX            *p_vertices;
1088     HRESULT hr;
1089     float f_width, f_height;
1090
1091     // check if device is still available
1092     hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1093     if( FAILED(hr) )
1094     {
1095         if( (D3DERR_DEVICENOTRESET != hr)
1096          || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) )
1097         {
1098             // device is not usable at present (lost device, out of video mem ?)
1099             return;
1100         }
1101     }
1102     p_d3dtex  = p_vout->p_sys->p_d3dtex;
1103     p_d3dvtc  = p_vout->p_sys->p_d3dvtc;
1104
1105     /* Clear the backbuffer and the zbuffer */
1106     hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET,
1107                               D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
1108     if( FAILED(hr) )
1109     {
1110         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1111         return;
1112     }
1113     /*  retrieve picture surface */
1114     p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1115     if( NULL == p_d3dsrc )
1116     {
1117         msg_Dbg( p_vout, "no surface to render ?");
1118         return;
1119     }
1120
1121     /* retrieve texture top-level surface */
1122     hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest);
1123     if( FAILED(hr) )
1124     {
1125         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1126         return;
1127     }
1128
1129     /* Copy picture surface into texture surface, color space conversion happens here */
1130     hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE);
1131     IDirect3DSurface9_Release(p_d3ddest);
1132     if( FAILED(hr) )
1133     {
1134         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1135         return;
1136     }
1137
1138     /* Update the vertex buffer */
1139     hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (&p_vertices), D3DLOCK_DISCARD);
1140     if( FAILED(hr) )
1141     {
1142         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1143         return;
1144     }
1145
1146     /* Setup vertices */
1147     f_width  = (float)(p_vout->output.i_width);
1148     f_height = (float)(p_vout->output.i_height);
1149
1150     /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1151     /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1152     p_vertices[0].x       = -0.5f;       // left
1153     p_vertices[0].y       = -0.5f;       // top
1154     p_vertices[0].z       = 0.0f;
1155     p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1156     p_vertices[0].rhw     = 1.0f;
1157     p_vertices[0].tu      = 0.0f;
1158     p_vertices[0].tv      = 0.0f;
1159
1160     p_vertices[1].x       = f_width - 0.5f;    // right
1161     p_vertices[1].y       = -0.5f;       // top
1162     p_vertices[1].z       = 0.0f;
1163     p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1164     p_vertices[1].rhw     = 1.0f;
1165     p_vertices[1].tu      = 1.0f;
1166     p_vertices[1].tv      = 0.0f;
1167
1168     p_vertices[2].x       = f_width - 0.5f;    // right
1169     p_vertices[2].y       = f_height - 0.5f;   // bottom
1170     p_vertices[2].z       = 0.0f;
1171     p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1172     p_vertices[2].rhw     = 1.0f;
1173     p_vertices[2].tu      = 1.0f;
1174     p_vertices[2].tv      = 1.0f;
1175
1176     p_vertices[3].x       = -0.5f;       // left
1177     p_vertices[3].y       = f_height - 0.5f;   // bottom
1178     p_vertices[3].z       = 0.0f;
1179     p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1180     p_vertices[3].rhw     = 1.0f;
1181     p_vertices[3].tu      = 0.0f;
1182     p_vertices[3].tv      = 1.0f;
1183
1184     hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc);
1185     if( FAILED(hr) )
1186     {
1187         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1188         return;
1189     }
1190
1191     // Begin the scene
1192     hr = IDirect3DDevice9_BeginScene(p_d3ddev);
1193     if( FAILED(hr) )
1194     {
1195         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1196         return;
1197     }
1198
1199     // Setup our texture. Using textures introduces the texture stage states,
1200     // which govern how textures get blended together (in the case of multiple
1201     // textures) and lighting information. In this case, we are modulating
1202     // (blending) our texture with the diffuse color of the vertices.
1203     hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex);
1204     if( FAILED(hr) )
1205     {
1206         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1207         IDirect3DDevice9_EndScene(p_d3ddev);
1208         return;
1209     }
1210
1211     // Render the vertex buffer contents
1212     hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX));
1213     if( FAILED(hr) )
1214     {
1215         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1216         IDirect3DDevice9_EndScene(p_d3ddev);
1217         return;
1218     }
1219
1220     // we use FVF instead of vertex shader
1221     hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX);
1222     if( FAILED(hr) )
1223     {
1224         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1225         IDirect3DDevice9_EndScene(p_d3ddev);
1226         return;
1227     }
1228
1229     // draw rectangle
1230     hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1231     if( FAILED(hr) )
1232     {
1233         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1234         IDirect3DDevice9_EndScene(p_d3ddev);
1235         return;
1236     }
1237
1238     // End the scene
1239     hr = IDirect3DDevice9_EndScene(p_d3ddev);
1240     if( FAILED(hr) )
1241     {
1242         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1243         return;
1244     }
1245 }
1246
1247 /*****************************************************************************
1248  * DesktopCallback: desktop mode variable callback
1249  *****************************************************************************/
1250 static int DesktopCallback( vlc_object_t *p_this, char const *psz_cmd,
1251                             vlc_value_t oldval, vlc_value_t newval,
1252                             void *p_data )
1253 {
1254     VLC_UNUSED( psz_cmd );
1255     VLC_UNUSED( oldval );
1256     VLC_UNUSED( p_data );
1257
1258     vout_thread_t *p_vout = (vout_thread_t *)p_this;
1259
1260     if( (newval.b_bool && !p_vout->p_sys->b_desktop) ||
1261         (!newval.b_bool && p_vout->p_sys->b_desktop) )
1262     {
1263         playlist_t *p_playlist = pl_Hold( p_vout );
1264
1265         if( p_playlist )
1266         {
1267             /* Modify playlist as well because the vout might have to be
1268              * restarted */
1269             var_Create( p_playlist, "direct3d-desktop", VLC_VAR_BOOL );
1270             var_Set( p_playlist, "direct3d-desktop", newval );
1271             pl_Release( p_vout );
1272         }
1273
1274         p_vout->p_sys->i_changes |= DX_DESKTOP_CHANGE;
1275     }
1276
1277     return VLC_SUCCESS;
1278 }