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