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