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