win32 vout: factorize code
[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 /*****************************************************************************
84  * Module descriptor
85  *****************************************************************************/
86
87 static bool IsVistaOrAbove(void)
88 {
89     OSVERSIONINFO winVer;
90     winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
91
92     if( GetVersionEx(&winVer) )
93     {
94         if( winVer.dwMajorVersion > 5 )
95         {
96             /* Windows Vista or above, make this module the default */
97             return true;
98         }
99     }
100     /* Windows XP or lower, make sure this module isn't the default */
101     return false;
102 }
103
104 static int OpenVideoXP( vlc_object_t *obj )
105 {
106     return IsVistaOrAbove() ? VLC_EGENERIC : OpenVideo( obj );
107 }
108
109 static int OpenVideoVista( vlc_object_t *obj )
110 {
111     return IsVistaOrAbove() ? OpenVideo( obj ) : VLC_EGENERIC;
112 }
113
114 vlc_module_begin ()
115     set_shortname( "Direct3D" )
116     set_category( CAT_VIDEO )
117     set_subcategory( SUBCAT_VIDEO_VOUT )
118     set_description( N_("DirectX 3D video output") )
119     set_capability( "video output", 50 )
120     add_shortcut( "direct3d" )
121     set_callbacks( OpenVideoXP, CloseVideo )
122
123     /* FIXME: Hack to avoid unregistering our window class */
124     linked_with_a_crap_library_which_uses_atexit ()
125
126     add_submodule()
127         set_capability( "video output", 150 )
128         add_shortcut( "direct3d" )
129         set_callbacks( OpenVideoVista, CloseVideo )
130 vlc_module_end ()
131
132 #if 0 /* FIXME */
133     /* check if we registered a window class because we need to
134      * unregister it */
135     WNDCLASS wndclass;
136     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
137         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
138 #endif
139
140 /*****************************************************************************
141  * CUSTOMVERTEX:
142  *****************************************************************************
143  *****************************************************************************/
144 typedef struct
145 {
146     FLOAT       x,y,z;      // vertex untransformed position
147     FLOAT       rhw;        // eye distance
148     D3DCOLOR    diffuse;    // diffuse color
149     FLOAT       tu, tv;     // texture relative coordinates
150 } CUSTOMVERTEX;
151
152 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
153
154 /*****************************************************************************
155  * OpenVideo: allocate Vout video thread output method
156  *****************************************************************************
157  * This function allocates and initialize the Direct3D vout method.
158  *****************************************************************************/
159 static int OpenVideo( vlc_object_t *p_this )
160 {
161     vout_thread_t * p_vout = (vout_thread_t *)p_this;
162
163     /* Allocate structure */
164     p_vout->p_sys = calloc( 1, sizeof( vout_sys_t ) );
165     if( p_vout->p_sys == NULL )
166         return VLC_ENOMEM;
167
168     if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) )
169     {
170         msg_Err( p_vout, "Direct3D could not be initialized !");
171         Direct3DVoutRelease( p_vout );
172         free( p_vout->p_sys );
173         return VLC_EGENERIC;
174     }
175
176     /* Initialisations */
177     p_vout->pf_init = Init;
178     p_vout->pf_end = End;
179     p_vout->pf_manage = Manage;
180     p_vout->pf_render = Direct3DVoutRenderScene;
181     p_vout->pf_display = FirstDisplay;
182
183     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
184     p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
185     p_vout->p_sys->i_changes = 0;
186     vlc_mutex_init( &p_vout->p_sys->lock );
187     SetRectEmpty( &p_vout->p_sys->rect_display );
188     SetRectEmpty( &p_vout->p_sys->rect_parent );
189
190     var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
191     var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
192
193     p_vout->p_sys->b_cursor_hidden = 0;
194     p_vout->p_sys->i_lastmoved = mdate();
195     p_vout->p_sys->i_mouse_hide_timeout =
196         var_GetInteger(p_vout, "mouse-hide-timeout") * 1000;
197
198     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
199     var_Create( p_vout, "disable-screensaver", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
200
201     /* Set main window's size */
202     p_vout->p_sys->i_window_width = p_vout->i_window_width;
203     p_vout->p_sys->i_window_height = p_vout->i_window_height;
204
205     if ( CreateEventThread( p_vout ) )
206     {
207         /* Variable to indicate if the window should be on top of others */
208         /* Trigger a callback right now */
209         var_TriggerCallback( p_vout, "video-on-top" );
210
211         DisableScreensaver ( p_vout );
212
213         return VLC_SUCCESS;
214     }
215     else
216     {
217         CloseVideo( VLC_OBJECT(p_vout) );
218         return VLC_EGENERIC;
219     }
220 }
221
222 /*****************************************************************************
223  * CloseVideo: destroy Sys video thread output method
224  *****************************************************************************
225  * Terminate an output method created by Create
226  *****************************************************************************/
227 static void CloseVideo( vlc_object_t *p_this )
228 {
229     vout_thread_t * p_vout = (vout_thread_t *)p_this;
230
231     Direct3DVoutRelease( p_vout );
232
233     StopEventThread( p_vout );
234
235     RestoreScreensaver( p_vout );
236
237     free( p_vout->p_sys );
238     p_vout->p_sys = NULL;
239 }
240
241 /*****************************************************************************
242  * Init: initialize Direct3D video thread output method
243  *****************************************************************************/
244 static int Init( vout_thread_t *p_vout )
245 {
246     int i_ret;
247
248     p_vout->p_sys->b_hw_yuv = var_GetBool( p_vout, "directx-hw-yuv" );
249
250     /* Initialise Direct3D */
251     if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) )
252     {
253         msg_Err( p_vout, "cannot initialize Direct3D" );
254         return VLC_EGENERIC;
255     }
256
257     /* Initialize the output structure.
258      * Since Direct3D can do rescaling for us, stick to the default
259      * coordinates and aspect. */
260     p_vout->output.i_width  = p_vout->render.i_width;
261     p_vout->output.i_height = p_vout->render.i_height;
262     p_vout->output.i_aspect = p_vout->render.i_aspect;
263     p_vout->fmt_out = p_vout->fmt_in;
264     UpdateRects( p_vout, true );
265
266     /*  create picture pool */
267     p_vout->output.i_chroma = 0;
268     i_ret = Direct3DVoutCreatePictures(p_vout, 1);
269     if( VLC_SUCCESS != i_ret )
270     {
271         msg_Err(p_vout, "Direct3D picture pool initialization failed !");
272         return i_ret;
273     }
274
275     /* create scene */
276     i_ret = Direct3DVoutCreateScene(p_vout);
277     if( VLC_SUCCESS != i_ret )
278     {
279         msg_Err(p_vout, "Direct3D scene initialization failed !");
280         Direct3DVoutReleasePictures(p_vout);
281         return i_ret;
282     }
283
284     /* Change the window title bar text */
285     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
286
287     p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
288     return VLC_SUCCESS;
289 }
290
291 /*****************************************************************************
292  * End: terminate Sys video thread output method
293  *****************************************************************************
294  * Terminate an output method created by Create.
295  * It is called at the end of the thread.
296  *****************************************************************************/
297 static void End( vout_thread_t *p_vout )
298 {
299     Direct3DVoutReleaseScene(p_vout);
300     Direct3DVoutReleasePictures(p_vout);
301     Direct3DVoutClose( p_vout );
302 }
303
304 /*****************************************************************************
305  * Manage: handle Sys events
306  *****************************************************************************
307  * This function should be called regularly by the video output thread.
308  * It returns a non null value if an error occurred.
309  *****************************************************************************/
310 static int Manage( vout_thread_t *p_vout )
311 {
312     /* If we do not control our window, we check for geometry changes
313      * ourselves because the parent might not send us its events. */
314     vlc_mutex_lock( &p_vout->p_sys->lock );
315     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
316     {
317         RECT rect_parent;
318         POINT point;
319
320         vlc_mutex_unlock( &p_vout->p_sys->lock );
321
322         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
323         point.x = point.y = 0;
324         ClientToScreen( p_vout->p_sys->hparent, &point );
325         OffsetRect( &rect_parent, point.x, point.y );
326
327         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
328         {
329             p_vout->p_sys->rect_parent = rect_parent;
330
331             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
332                           rect_parent.right - rect_parent.left,
333                           rect_parent.bottom - rect_parent.top,
334                           SWP_NOZORDER );
335         }
336     }
337     else
338     {
339         vlc_mutex_unlock( &p_vout->p_sys->lock );
340     }
341
342     /*
343      * Position Change
344      */
345     if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
346     {
347 #if 0 /* need that when bicubic filter is available */
348         RECT rect;
349         UINT width, height;
350
351         GetClientRect(p_vout->p_sys->hvideownd, &rect);
352         width  = rect.right-rect.left;
353         height = rect.bottom-rect.top;
354
355         if( (width != p_vout->p_sys->d3dpp.BackBufferWidth)
356          || (height != p_vout->p_sys->d3dpp.BackBufferHeight) )
357         {
358             msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height);
359             // need to reset D3D device to resize back buffer
360             if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) )
361                 return VLC_EGENERIC;
362         }
363 #endif
364         p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
365     }
366
367     /* autoscale toggle */
368     if( p_vout->i_changes & VOUT_SCALE_CHANGE )
369     {
370         p_vout->i_changes &= ~VOUT_SCALE_CHANGE;
371
372         p_vout->b_autoscale = var_GetBool( p_vout, "autoscale" );
373         p_vout->i_zoom = (int) ZOOM_FP_FACTOR;
374
375         UpdateRects( p_vout, true );
376     }
377
378     /* scaling factor */
379     if( p_vout->i_changes & VOUT_ZOOM_CHANGE )
380     {
381         p_vout->i_changes &= ~VOUT_ZOOM_CHANGE;
382
383         p_vout->b_autoscale = false;
384         p_vout->i_zoom =
385             (int)( ZOOM_FP_FACTOR * var_GetFloat( p_vout, "scale" ) );
386         UpdateRects( p_vout, true );
387     }
388
389     /* Check for cropping / aspect changes */
390     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
391         p_vout->i_changes & VOUT_ASPECT_CHANGE )
392     {
393         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
394         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
395
396         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
397         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
398         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
399         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
400         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
401         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
402         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
403         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
404         UpdateRects( p_vout, true );
405     }
406
407     /* We used to call the Win32 PeekMessage function here to read the window
408      * messages. But since window can stay blocked into this function for a
409      * long time (for example when you move your window on the screen), I
410      * decided to isolate PeekMessage in another thread. */
411
412     /*
413      * Fullscreen change
414      */
415     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
416         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
417     {
418         Win32ToggleFullscreen( p_vout );
419
420         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
421         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
422     }
423
424     /*
425      * Pointer change
426      */
427     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
428         (mdate() - p_vout->p_sys->i_lastmoved) >
429             p_vout->p_sys->i_mouse_hide_timeout )
430     {
431         POINT point;
432         HWND hwnd;
433
434         /* Hide the cursor only if it is inside our window */
435         GetCursorPos( &point );
436         hwnd = WindowFromPoint(point);
437         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
438         {
439             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
440         }
441         else
442         {
443             p_vout->p_sys->i_lastmoved = mdate();
444         }
445     }
446
447     /*
448      * "Always on top" status change
449      */
450     if( p_vout->p_sys->b_on_top_change )
451     {
452         vlc_value_t val;
453         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
454
455         var_Get( p_vout, "video-on-top", &val );
456
457         /* Set the window on top if necessary */
458         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
459                            & WS_EX_TOPMOST ) )
460         {
461             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
462                            MF_BYCOMMAND | MFS_CHECKED );
463             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
464                           SWP_NOSIZE | SWP_NOMOVE );
465         }
466         else
467         /* The window shouldn't be on top */
468         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
469                            & WS_EX_TOPMOST ) )
470         {
471             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
472                            MF_BYCOMMAND | MFS_UNCHECKED );
473             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
474                           SWP_NOSIZE | SWP_NOMOVE );
475         }
476
477         p_vout->p_sys->b_on_top_change = false;
478     }
479
480     /* Check if the event thread is still running */
481     if( !vlc_object_alive (p_vout->p_sys->p_event) )
482     {
483         return VLC_EGENERIC; /* exit */
484     }
485
486     return VLC_SUCCESS;
487 }
488
489 /*****************************************************************************
490  * Display: displays previously rendered output
491  *****************************************************************************
492  * This function sends the currently rendered image to the display, wait until
493  * it is displayed and switch the two rendering buffers, preparing next frame.
494  *****************************************************************************/
495 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
496 {
497     LPDIRECT3DDEVICE9       p_d3ddev = p_vout->p_sys->p_d3ddev;
498     // Present the back buffer contents to the display
499     // stretching and filtering happens here
500     HRESULT hr = IDirect3DDevice9_Present(p_d3ddev,
501                     &(p_vout->p_sys->rect_src_clipped),
502                     NULL, NULL, NULL);
503     if( FAILED(hr) )
504         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
505 }
506
507 /*
508 ** this function is only used once when the first picture is received
509 ** this function will show the video window once a picture is ready
510 */
511
512 static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic )
513 {
514     /* get initial picture presented through D3D */
515     Display(p_vout, p_pic);
516
517     /*
518     ** Video window is initially hidden, show it now since we got a
519     ** picture to show.
520     */
521     SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
522         SWP_ASYNCWINDOWPOS|
523         SWP_FRAMECHANGED|
524         SWP_SHOWWINDOW|
525         SWP_NOMOVE|
526         SWP_NOSIZE|
527         SWP_NOZORDER );
528
529     /* use and restores proper display function for further pictures */
530     p_vout->pf_display = Display;
531 }
532
533 /*****************************************************************************
534  * DirectD3DVoutCreate: Initialize and instance of Direct3D9
535  *****************************************************************************
536  * This function initialize Direct3D and analyze available resources from
537  * default adapter.
538  *****************************************************************************/
539 static int Direct3DVoutCreate( vout_thread_t *p_vout )
540 {
541     HRESULT hr;
542     LPDIRECT3D9 p_d3dobj;
543     D3DCAPS9 d3dCaps;
544
545     LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
546
547     p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
548     if( NULL == p_vout->p_sys->hd3d9_dll )
549     {
550         msg_Warn( p_vout, "cannot load d3d9.dll, aborting" );
551         return VLC_EGENERIC;
552     }
553
554     OurDirect3DCreate9 =
555       (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll,
556                               TEXT("Direct3DCreate9") );
557     if( OurDirect3DCreate9 == NULL )
558     {
559         msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" );
560         return VLC_EGENERIC;
561     }
562
563     /* Create the D3D object. */
564     p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION );
565     if( NULL == p_d3dobj )
566     {
567        msg_Err( p_vout, "Could not create Direct3D9 instance.");
568        return VLC_EGENERIC;
569     }
570     p_vout->p_sys->p_d3dobj = p_d3dobj;
571
572     /*
573     ** Get device capabilities
574     */
575     ZeroMemory(&d3dCaps, sizeof(d3dCaps));
576     hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
577     if( FAILED(hr) )
578     {
579        msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr);
580        return VLC_EGENERIC;
581     }
582     /* TODO: need to test device capabilities and select the right render function */
583
584     return VLC_SUCCESS;
585 }
586
587 /*****************************************************************************
588  * DirectD3DVoutRelease: release an instance of Direct3D9
589  *****************************************************************************/
590
591 static void Direct3DVoutRelease( vout_thread_t *p_vout )
592 {
593     if( p_vout->p_sys->p_d3dobj )
594     {
595        IDirect3D9_Release(p_vout->p_sys->p_d3dobj);
596        p_vout->p_sys->p_d3dobj = NULL;
597     }
598     if( NULL != p_vout->p_sys->hd3d9_dll )
599     {
600         FreeLibrary(p_vout->p_sys->hd3d9_dll);
601         p_vout->p_sys->hd3d9_dll = NULL;
602     }
603 }
604
605 static int Direct3DFillPresentationParameters(vout_thread_t *p_vout, D3DPRESENT_PARAMETERS *d3dpp)
606 {
607     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
608     D3DDISPLAYMODE d3ddm;
609     HRESULT hr;
610
611     /*
612     ** Get the current desktop display mode, so we can set up a back
613     ** buffer of the same format
614     */
615     hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
616     if( FAILED(hr))
617     {
618        msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr);
619        return VLC_EGENERIC;
620     }
621
622     /* keep a copy of current desktop format */
623     p_vout->p_sys->bbFormat = d3ddm.Format;
624
625     /* Set up the structure used to create the D3DDevice. */
626     ZeroMemory( d3dpp, sizeof(D3DPRESENT_PARAMETERS) );
627     d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
628     d3dpp->Windowed               = TRUE;
629     d3dpp->hDeviceWindow          = p_vout->p_sys->hvideownd;
630     d3dpp->BackBufferWidth        = p_vout->output.i_width;
631     d3dpp->BackBufferHeight       = p_vout->output.i_height;
632     d3dpp->SwapEffect             = D3DSWAPEFFECT_COPY;
633     d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
634     d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
635     d3dpp->BackBufferFormat       = d3ddm.Format;
636     d3dpp->BackBufferCount        = 1;
637     d3dpp->EnableAutoDepthStencil = FALSE;
638
639     return VLC_SUCCESS;
640 }
641
642 /*****************************************************************************
643  * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations
644  *****************************************************************************
645  * This function creates Direct3D device
646  * this must be called from the vout thread for performance reason, as
647  * all Direct3D Device APIs are used in a non multithread safe environment
648  *****************************************************************************/
649 static int Direct3DVoutOpen( vout_thread_t *p_vout )
650 {
651     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
652     LPDIRECT3DDEVICE9 p_d3ddev;
653     D3DPRESENT_PARAMETERS d3dpp;
654     HRESULT hr;
655
656     if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
657         return VLC_EGENERIC;
658
659     // Create the D3DDevice
660     hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT,
661                                  D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd,
662                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING|
663                                  D3DCREATE_MULTITHREADED,
664                                  &d3dpp, &p_d3ddev );
665     if( FAILED(hr) )
666     {
667        msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr);
668        return VLC_EGENERIC;
669     }
670     p_vout->p_sys->p_d3ddev = p_d3ddev;
671
672     msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" );
673     return VLC_SUCCESS;
674 }
675
676 /*****************************************************************************
677  * DirectD3DClose: release the Direct3D9 device
678  *****************************************************************************/
679 static void Direct3DVoutClose( vout_thread_t *p_vout )
680 {
681     if( p_vout->p_sys->p_d3ddev )
682     {
683        IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev);
684        p_vout->p_sys->p_d3ddev = NULL;
685     }
686
687     p_vout->p_sys->hmonitor = NULL;
688 }
689
690 /*****************************************************************************
691  * DirectD3DClose: reset the Direct3D9 device
692  *****************************************************************************
693  * All resources must be deallocated before the reset occur, they will be
694  * realllocated once the reset has been performed successfully
695  *****************************************************************************/
696 static int Direct3DVoutResetDevice( vout_thread_t *p_vout )
697 {
698     LPDIRECT3DDEVICE9       p_d3ddev = p_vout->p_sys->p_d3ddev;
699     D3DPRESENT_PARAMETERS   d3dpp;
700     HRESULT hr;
701
702     if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
703         return VLC_EGENERIC;
704
705     // release all D3D objects
706     Direct3DVoutReleaseScene( p_vout );
707     Direct3DVoutReleasePictures( p_vout );
708
709     hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp);
710     if( SUCCEEDED(hr) )
711     {
712         // re-create them
713         if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1))
714          || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) )
715         {
716             msg_Dbg(p_vout, "%s failed !", __FUNCTION__);
717             return VLC_EGENERIC;
718         }
719     }
720     else {
721         msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
722         return VLC_EGENERIC;
723     }
724     return VLC_SUCCESS;
725 }
726
727 static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target,
728     const D3DFORMAT *formats, size_t count)
729 {
730     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
731     size_t c;
732
733     for( c=0; c<count; ++c )
734     {
735         HRESULT hr;
736         D3DFORMAT format = formats[c];
737         /* test whether device can create a surface of that format */
738         hr = IDirect3D9_CheckDeviceFormat(p_d3dobj, D3DADAPTER_DEFAULT,
739                 D3DDEVTYPE_HAL, target, 0, D3DRTYPE_SURFACE, format);
740         if( SUCCEEDED(hr) )
741         {
742             /* test whether device can perform color-conversion
743             ** from that format to target format
744             */
745             hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
746                     D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
747                     format, target);
748         }
749         if( SUCCEEDED(hr) )
750         {
751             // found a compatible format
752             switch( format )
753             {
754                 case D3DFMT_UYVY:
755                     msg_Dbg( p_vout, "selected surface pixel format is UYVY");
756                     break;
757                 case D3DFMT_YUY2:
758                     msg_Dbg( p_vout, "selected surface pixel format is YUY2");
759                     break;
760                 case D3DFMT_X8R8G8B8:
761                     msg_Dbg( p_vout, "selected surface pixel format is X8R8G8B8");
762                     break;
763                 case D3DFMT_A8R8G8B8:
764                     msg_Dbg( p_vout, "selected surface pixel format is A8R8G8B8");
765                     break;
766                 case D3DFMT_R8G8B8:
767                     msg_Dbg( p_vout, "selected surface pixel format is R8G8B8");
768                     break;
769                 case D3DFMT_R5G6B5:
770                     msg_Dbg( p_vout, "selected surface pixel format is R5G6B5");
771                     break;
772                 case D3DFMT_X1R5G5B5:
773                     msg_Dbg( p_vout, "selected surface pixel format is X1R5G5B5");
774                     break;
775                 default:
776                     msg_Dbg( p_vout, "selected surface pixel format is 0x%0X", format);
777                     break;
778             }
779             return format;
780         }
781         else if( D3DERR_NOTAVAILABLE != hr )
782         {
783             msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
784             break;
785         }
786     }
787     return D3DFMT_UNKNOWN;
788 }
789
790 static D3DFORMAT Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
791 {
792     //if( p_vout->p_sys->b_hw_yuv && ! _got_vista_or_above )
793     if( p_vout->p_sys->b_hw_yuv )
794     {
795     /* it sounds like vista does not support YUV surfaces at all */
796         switch( i_chroma )
797         {
798             case VLC_CODEC_UYVY:
799             {
800                 static const D3DFORMAT formats[] =
801                     { D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
802                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
803             }
804             case VLC_CODEC_I420:
805             case VLC_CODEC_I422:
806             case VLC_CODEC_YV12:
807             {
808                 /* typically 3D textures don't support planar format
809                 ** fallback to packed version and use CPU for the conversion
810                 */
811                 static const D3DFORMAT formats[] =
812                     { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
813                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
814             }
815             case VLC_CODEC_YUYV:
816             {
817                 static const D3DFORMAT formats[] =
818                     { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
819                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
820             }
821         }
822     }
823
824     switch( i_chroma )
825     {
826         case VLC_CODEC_RGB15:
827         {
828             static const D3DFORMAT formats[] =
829                 { D3DFMT_X1R5G5B5 };
830             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
831         }
832         case VLC_CODEC_RGB16:
833         {
834             static const D3DFORMAT formats[] =
835                 { D3DFMT_R5G6B5 };
836             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
837         }
838         case VLC_CODEC_RGB24:
839         {
840             static const D3DFORMAT formats[] =
841                 { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 };
842             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
843         }
844         case VLC_CODEC_RGB32:
845         {
846             static const D3DFORMAT formats[] =
847                 { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 };
848             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
849         }
850         default:
851         {
852             /* use display default format */
853             LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
854             D3DDISPLAYMODE d3ddm;
855
856             HRESULT hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
857             if( SUCCEEDED(hr))
858             {
859                 /*
860                 ** some professional cards could use some advanced pixel format as default,
861                 ** make sure we stick with chromas that we can handle internally
862                 */
863                 switch( d3ddm.Format )
864                 {
865                     case D3DFMT_R8G8B8:
866                     case D3DFMT_X8R8G8B8:
867                     case D3DFMT_A8R8G8B8:
868                     case D3DFMT_R5G6B5:
869                     case D3DFMT_X1R5G5B5:
870                         msg_Dbg( p_vout, "defaulting to adapter pixel format");
871                         return Direct3DVoutSelectFormat(p_vout, target, &d3ddm.Format, 1);
872                     default:
873                     {
874                         /* if we fall here, that probably means that we need to render some YUV format */
875                         static const D3DFORMAT formats[] =
876                             { D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
877                         msg_Dbg( p_vout, "defaulting to built-in pixel format");
878                         return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
879                     }
880                 }
881             }
882         }
883     }
884     return D3DFMT_UNKNOWN;
885 }
886
887 static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format)
888 {
889     switch( format )
890     {
891         case D3DFMT_YUY2:
892             p_vout->output.i_chroma = VLC_CODEC_YUYV;
893             break;
894         case D3DFMT_UYVY:
895             p_vout->output.i_chroma = VLC_CODEC_UYVY;
896             break;
897         case D3DFMT_R8G8B8:
898             p_vout->output.i_chroma = VLC_CODEC_RGB24;
899             p_vout->output.i_rmask = 0xff0000;
900             p_vout->output.i_gmask = 0x00ff00;
901             p_vout->output.i_bmask = 0x0000ff;
902             break;
903         case D3DFMT_X8R8G8B8:
904         case D3DFMT_A8R8G8B8:
905             p_vout->output.i_chroma = VLC_CODEC_RGB32;
906             p_vout->output.i_rmask = 0x00ff0000;
907             p_vout->output.i_gmask = 0x0000ff00;
908             p_vout->output.i_bmask = 0x000000ff;
909             break;
910         case D3DFMT_R5G6B5:
911             p_vout->output.i_chroma = VLC_CODEC_RGB16;
912             p_vout->output.i_rmask = (0x1fL)<<11;
913             p_vout->output.i_gmask = (0x3fL)<<5;
914             p_vout->output.i_bmask = (0x1fL)<<0;
915             break;
916         case D3DFMT_X1R5G5B5:
917             p_vout->output.i_chroma = VLC_CODEC_RGB15;
918             p_vout->output.i_rmask = (0x1fL)<<10;
919             p_vout->output.i_gmask = (0x1fL)<<5;
920             p_vout->output.i_bmask = (0x1fL)<<0;
921             break;
922         default:
923             return VLC_EGENERIC;
924     }
925     return VLC_SUCCESS;
926 }
927
928 /*****************************************************************************
929  * Direct3DVoutCreatePictures: allocate a vector of identical pictures
930  *****************************************************************************
931  * Each picture has an associated offscreen surface in video memory
932  * depending on hardware capabilities the picture chroma will be as close
933  * as possible to the orginal render chroma to reduce CPU conversion overhead
934  * and delegate this work to video card GPU
935  *****************************************************************************/
936 static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics )
937 {
938     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
939     D3DFORMAT               format;
940     HRESULT hr;
941     size_t c;
942     // if vout is already running, use current chroma, otherwise choose from upstream
943     int i_chroma = p_vout->output.i_chroma ? p_vout->output.i_chroma
944                                            : p_vout->render.i_chroma;
945
946     I_OUTPUTPICTURES = 0;
947
948     /*
949     ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
950     ** the requested chroma which is usable by the hardware in an offscreen surface, as they
951     ** typically support more formats than textures
952     */
953     format = Direct3DVoutFindFormat(p_vout, i_chroma, p_vout->p_sys->bbFormat);
954     if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) )
955     {
956         msg_Err(p_vout, "surface pixel format is not supported.");
957         return VLC_EGENERIC;
958     }
959
960     for( c=0; c<i_num_pics; )
961     {
962
963         LPDIRECT3DSURFACE9 p_d3dsurf;
964         picture_t *p_pic = p_vout->p_picture+c;
965
966         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev,
967                 p_vout->render.i_width,
968                 p_vout->render.i_height,
969                 format,
970                 D3DPOOL_DEFAULT,
971                 &p_d3dsurf,
972                 NULL);
973         if( FAILED(hr) )
974         {
975             msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr);
976             Direct3DVoutReleasePictures(p_vout);
977             return VLC_EGENERIC;
978         }
979
980         /* fill surface with black color */
981         IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
982
983         /* assign surface to internal structure */
984         p_pic->p_sys = (void *)p_d3dsurf;
985
986         /* Now that we've got our direct-buffer, we can finish filling in the
987          * picture_t structures */
988         switch( p_vout->output.i_chroma )
989         {
990             case VLC_CODEC_RGB8:
991                 p_pic->p->i_lines = p_vout->output.i_height;
992                 p_pic->p->i_visible_lines = p_vout->output.i_height;
993                 p_pic->p->i_pixel_pitch = 1;
994                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
995                     p_pic->p->i_pixel_pitch;
996                 p_pic->i_planes = 1;
997             break;
998             case VLC_CODEC_RGB15:
999             case VLC_CODEC_RGB16:
1000                 p_pic->p->i_lines = p_vout->output.i_height;
1001                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1002                 p_pic->p->i_pixel_pitch = 2;
1003                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1004                     p_pic->p->i_pixel_pitch;
1005                 p_pic->i_planes = 1;
1006             break;
1007             case VLC_CODEC_RGB24:
1008                 p_pic->p->i_lines = p_vout->output.i_height;
1009                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1010                 p_pic->p->i_pixel_pitch = 3;
1011                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1012                     p_pic->p->i_pixel_pitch;
1013                 p_pic->i_planes = 1;
1014             break;
1015             case VLC_CODEC_RGB32:
1016                 p_pic->p->i_lines = p_vout->output.i_height;
1017                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1018                 p_pic->p->i_pixel_pitch = 4;
1019                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1020                     p_pic->p->i_pixel_pitch;
1021                 p_pic->i_planes = 1;
1022                 break;
1023             case VLC_CODEC_UYVY:
1024             case VLC_CODEC_YUYV:
1025                 p_pic->p->i_lines = p_vout->output.i_height;
1026                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1027                 p_pic->p->i_pixel_pitch = 2;
1028                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1029                     p_pic->p->i_pixel_pitch;
1030                 p_pic->i_planes = 1;
1031                 break;
1032             default:
1033                 Direct3DVoutReleasePictures(p_vout);
1034                 return VLC_EGENERIC;
1035         }
1036         p_pic->i_status = DESTROYED_PICTURE;
1037         p_pic->i_type   = DIRECT_PICTURE;
1038         p_pic->b_slow   = true;
1039         p_pic->pf_lock  = Direct3DVoutLockSurface;
1040         p_pic->pf_unlock = Direct3DVoutUnlockSurface;
1041         PP_OUTPUTPICTURE[c] = p_pic;
1042
1043         I_OUTPUTPICTURES = ++c;
1044     }
1045
1046     msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c );
1047
1048     return VLC_SUCCESS;
1049 }
1050
1051 /*****************************************************************************
1052  * Direct3DVoutReleasePictures: destroy a picture vector
1053  *****************************************************************************
1054  * release all video resources used for pictures
1055  *****************************************************************************/
1056 static void Direct3DVoutReleasePictures( vout_thread_t *p_vout)
1057 {
1058     size_t i_num_pics = I_OUTPUTPICTURES;
1059     size_t c;
1060     for( c=0; c<i_num_pics; ++c )
1061     {
1062         picture_t *p_pic = p_vout->p_picture+c;
1063         if( p_pic->p_sys )
1064         {
1065             LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1066
1067             p_pic->p_sys = NULL;
1068
1069             if( p_d3dsurf )
1070             {
1071                 IDirect3DSurface9_Release(p_d3dsurf);
1072             }
1073         }
1074     }
1075     msg_Dbg( p_vout, "%u Direct3D pictures released.", c);
1076
1077     I_OUTPUTPICTURES = 0;
1078 }
1079
1080 /*****************************************************************************
1081  * Direct3DVoutLockSurface: Lock surface and get picture data pointer
1082  *****************************************************************************
1083  * This function locks a surface and get the surface descriptor which amongst
1084  * other things has the pointer to the picture data.
1085  *****************************************************************************/
1086 static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1087 {
1088     HRESULT hr;
1089     D3DLOCKED_RECT d3drect;
1090     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1091
1092     if( NULL == p_d3dsurf )
1093         return VLC_EGENERIC;
1094
1095     /* Lock the surface to get a valid pointer to the picture buffer */
1096     hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0);
1097     if( FAILED(hr) )
1098     {
1099         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1100         return VLC_EGENERIC;
1101     }
1102
1103     /* fill in buffer info in first plane */
1104     p_pic->p->p_pixels = d3drect.pBits;
1105     p_pic->p->i_pitch  = d3drect.Pitch;
1106
1107     return VLC_SUCCESS;
1108 }
1109
1110 /*****************************************************************************
1111  * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface().
1112  *****************************************************************************/
1113 static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1114 {
1115     HRESULT hr;
1116     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1117
1118     if( NULL == p_d3dsurf )
1119         return VLC_EGENERIC;
1120
1121     /* Unlock the Surface */
1122     hr = IDirect3DSurface9_UnlockRect(p_d3dsurf);
1123     if( FAILED(hr) )
1124     {
1125         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1126         return VLC_EGENERIC;
1127     }
1128     return VLC_SUCCESS;
1129 }
1130
1131 /*****************************************************************************
1132  * Direct3DVoutCreateScene: allocate and initialize a 3D scene
1133  *****************************************************************************
1134  * for advanced blending/filtering a texture needs be used in a 3D scene.
1135  *****************************************************************************/
1136
1137 static int Direct3DVoutCreateScene( vout_thread_t *p_vout )
1138 {
1139     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1140     LPDIRECT3DTEXTURE9      p_d3dtex;
1141     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1142
1143     HRESULT hr;
1144
1145     /*
1146     ** Create a texture for use when rendering a scene
1147     ** for performance reason, texture format is identical to backbuffer
1148     ** which would usually be a RGB format
1149     */
1150     hr = IDirect3DDevice9_CreateTexture(p_d3ddev,
1151             p_vout->render.i_width,
1152             p_vout->render.i_height,
1153             1,
1154             D3DUSAGE_RENDERTARGET,
1155             p_vout->p_sys->bbFormat,
1156             D3DPOOL_DEFAULT,
1157             &p_d3dtex,
1158             NULL);
1159     if( FAILED(hr))
1160     {
1161         msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr);
1162         return VLC_EGENERIC;
1163     }
1164
1165     /*
1166     ** Create a vertex buffer for use when rendering scene
1167     */
1168     hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev,
1169             sizeof(CUSTOMVERTEX)*4,
1170             D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1171             D3DFVF_CUSTOMVERTEX,
1172             D3DPOOL_DEFAULT,
1173             &p_d3dvtc,
1174             NULL);
1175     if( FAILED(hr) )
1176     {
1177         msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1178             IDirect3DTexture9_Release(p_d3dtex);
1179         return VLC_EGENERIC;
1180     }
1181
1182     p_vout->p_sys->p_d3dtex = p_d3dtex;
1183     p_vout->p_sys->p_d3dvtc = p_d3dvtc;
1184
1185     // Texture coordinates outside the range [0.0, 1.0] are set
1186     // to the texture color at 0.0 or 1.0, respectively.
1187     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1188     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1189
1190     // Set linear filtering quality
1191     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1192     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1193
1194     // set maximum ambient light
1195     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1196
1197     // Turn off culling
1198     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1199
1200     // Turn off the zbuffer
1201     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1202
1203     // Turn off lights
1204     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE);
1205
1206     // Enable dithering
1207     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE);
1208
1209     // disable stencil
1210     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE);
1211
1212     // manage blending
1213     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1214     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1215     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1216     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1217     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10);
1218     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1219
1220     // Set texture states
1221     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
1222     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1223     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
1224
1225     // turn off alpha operation
1226     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1227
1228     msg_Dbg( p_vout, "Direct3D scene created successfully");
1229
1230     return VLC_SUCCESS;
1231 }
1232
1233 /*****************************************************************************
1234  * Direct3DVoutReleaseScene
1235  *****************************************************************************/
1236 static void Direct3DVoutReleaseScene( vout_thread_t *p_vout )
1237 {
1238     LPDIRECT3DTEXTURE9      p_d3dtex = p_vout->p_sys->p_d3dtex;
1239     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1240
1241     if( p_d3dvtc )
1242     {
1243         IDirect3DVertexBuffer9_Release(p_d3dvtc);
1244         p_vout->p_sys->p_d3dvtc = NULL;
1245     }
1246
1247     if( p_d3dtex )
1248     {
1249         IDirect3DTexture9_Release(p_d3dtex);
1250         p_vout->p_sys->p_d3dtex = NULL;
1251     }
1252     msg_Dbg( p_vout, "Direct3D scene released successfully");
1253 }
1254
1255 /*****************************************************************************
1256  * Render: copy picture surface into a texture and render into a scene
1257  *****************************************************************************
1258  * This function is intented for higher end 3D cards, with pixel shader support
1259  * and at least 64 MB of video RAM.
1260  *****************************************************************************/
1261 static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic )
1262 {
1263     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1264     LPDIRECT3DTEXTURE9      p_d3dtex;
1265     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1266     LPDIRECT3DSURFACE9      p_d3dsrc, p_d3ddest;
1267     CUSTOMVERTEX            *p_vertices;
1268     HRESULT hr;
1269     float f_width, f_height;
1270
1271     // check if device is still available
1272     hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1273     if( FAILED(hr) )
1274     {
1275         if( (D3DERR_DEVICENOTRESET != hr)
1276          || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) )
1277         {
1278             // device is not usable at present (lost device, out of video mem ?)
1279             return;
1280         }
1281     }
1282     p_d3dtex  = p_vout->p_sys->p_d3dtex;
1283     p_d3dvtc  = p_vout->p_sys->p_d3dvtc;
1284
1285     /* Clear the backbuffer and the zbuffer */
1286     hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET,
1287                               D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
1288     if( FAILED(hr) )
1289     {
1290         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1291         return;
1292     }
1293
1294     /*  retrieve picture surface */
1295     p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1296     if( NULL == p_d3dsrc )
1297     {
1298         msg_Dbg( p_vout, "no surface to render ?");
1299         return;
1300     }
1301
1302     /* retrieve texture top-level surface */
1303     hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest);
1304     if( FAILED(hr) )
1305     {
1306         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1307         return;
1308     }
1309
1310     /* Copy picture surface into texture surface, color space conversion happens here */
1311     hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE);
1312     IDirect3DSurface9_Release(p_d3ddest);
1313     if( FAILED(hr) )
1314     {
1315         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1316         return;
1317     }
1318
1319     /* Update the vertex buffer */
1320     hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (VOID **)(&p_vertices), D3DLOCK_DISCARD);
1321     if( FAILED(hr) )
1322     {
1323         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1324         return;
1325     }
1326
1327     /* Setup vertices */
1328     f_width  = (float)(p_vout->output.i_width);
1329     f_height = (float)(p_vout->output.i_height);
1330
1331     /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1332     /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1333     p_vertices[0].x       = -0.5f;       // left
1334     p_vertices[0].y       = -0.5f;       // top
1335     p_vertices[0].z       = 0.0f;
1336     p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1337     p_vertices[0].rhw     = 1.0f;
1338     p_vertices[0].tu      = 0.0f;
1339     p_vertices[0].tv      = 0.0f;
1340
1341     p_vertices[1].x       = f_width - 0.5f;    // right
1342     p_vertices[1].y       = -0.5f;       // top
1343     p_vertices[1].z       = 0.0f;
1344     p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1345     p_vertices[1].rhw     = 1.0f;
1346     p_vertices[1].tu      = 1.0f;
1347     p_vertices[1].tv      = 0.0f;
1348
1349     p_vertices[2].x       = f_width - 0.5f;    // right
1350     p_vertices[2].y       = f_height - 0.5f;   // bottom
1351     p_vertices[2].z       = 0.0f;
1352     p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1353     p_vertices[2].rhw     = 1.0f;
1354     p_vertices[2].tu      = 1.0f;
1355     p_vertices[2].tv      = 1.0f;
1356
1357     p_vertices[3].x       = -0.5f;       // left
1358     p_vertices[3].y       = f_height - 0.5f;   // bottom
1359     p_vertices[3].z       = 0.0f;
1360     p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1361     p_vertices[3].rhw     = 1.0f;
1362     p_vertices[3].tu      = 0.0f;
1363     p_vertices[3].tv      = 1.0f;
1364
1365     hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc);
1366     if( FAILED(hr) )
1367     {
1368         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1369         return;
1370     }
1371
1372     // Begin the scene
1373     hr = IDirect3DDevice9_BeginScene(p_d3ddev);
1374     if( FAILED(hr) )
1375     {
1376         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1377         return;
1378     }
1379
1380     // Setup our texture. Using textures introduces the texture stage states,
1381     // which govern how textures get blended together (in the case of multiple
1382     // textures) and lighting information. In this case, we are modulating
1383     // (blending) our texture with the diffuse color of the vertices.
1384     hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex);
1385     if( FAILED(hr) )
1386     {
1387         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1388         IDirect3DDevice9_EndScene(p_d3ddev);
1389         return;
1390     }
1391
1392     // Render the vertex buffer contents
1393     hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX));
1394     if( FAILED(hr) )
1395     {
1396         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1397         IDirect3DDevice9_EndScene(p_d3ddev);
1398         return;
1399     }
1400
1401     // we use FVF instead of vertex shader
1402     hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX);
1403     if( FAILED(hr) )
1404     {
1405         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1406         IDirect3DDevice9_EndScene(p_d3ddev);
1407         return;
1408     }
1409
1410     // draw rectangle
1411     hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1412     if( FAILED(hr) )
1413     {
1414         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1415         IDirect3DDevice9_EndScene(p_d3ddev);
1416         return;
1417     }
1418
1419     // End the scene
1420     hr = IDirect3DDevice9_EndScene(p_d3ddev);
1421     if( FAILED(hr) )
1422     {
1423         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1424         return;
1425     }
1426 }
1427