]> git.sesse.net Git - vlc/blob - modules/video_output/directx/direct3d.c
- direct3d.c: attempt to fix incompatibility with Vista, needs testing since i still...
[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(void)
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;
711     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
712     LPDIRECT3DDEVICE9 p_d3ddev;
713
714     /* Set up the structure used to create the D3DDevice. */
715     ZeroMemory( &p_vout->p_sys->d3dpp, sizeof(D3DPRESENT_PARAMETERS) );
716     p_vout->p_sys->d3dpp.Windowed               = TRUE;
717     p_vout->p_sys->d3dpp.hDeviceWindow          = p_vout->p_sys->hvideownd;
718     p_vout->p_sys->d3dpp.BackBufferWidth        = p_vout->render.i_width;
719     p_vout->p_sys->d3dpp.BackBufferHeight       = p_vout->render.i_height;
720     p_vout->p_sys->d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
721     p_vout->p_sys->d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;
722     p_vout->p_sys->d3dpp.BackBufferFormat       = D3DFMT_UNKNOWN;
723     p_vout->p_sys->d3dpp.BackBufferCount        = 1;
724     p_vout->p_sys->d3dpp.EnableAutoDepthStencil = TRUE;
725     p_vout->p_sys->d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
726
727     // Create the D3DDevice
728     hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd,
729                 D3DCREATE_SOFTWARE_VERTEXPROCESSING, &(p_vout->p_sys->d3dpp), &p_d3ddev );
730     if( FAILED(hr) )                                      
731     {
732        msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr);
733        return VLC_EGENERIC;
734     }
735     p_vout->p_sys->p_d3ddev = p_d3ddev;
736
737     msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" );
738     return VLC_SUCCESS;
739 }
740
741 /*****************************************************************************
742  * DirectD3DClose: release the Direct3D9 device 
743  *****************************************************************************/
744 static void Direct3DVoutClose( vout_thread_t *p_vout )
745 {
746     if( p_vout->p_sys->p_d3ddev )
747     {
748        IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev);
749        p_vout->p_sys->p_d3ddev = NULL;
750     }
751        
752     p_vout->p_sys->hmonitor = NULL;
753 }
754
755 /*****************************************************************************
756  * DirectD3DClose: reset the Direct3D9 device 
757  *****************************************************************************
758  * All resources must be deallocated before the reset occur, they will be
759  * realllocated once the reset has been performed successfully
760  *****************************************************************************/
761 static int Direct3DVoutResetDevice( vout_thread_t *p_vout, UINT i_width, UINT i_height )
762 {
763     LPDIRECT3DDEVICE9       p_d3ddev = p_vout->p_sys->p_d3ddev;
764     D3DPRESENT_PARAMETERS   d3dpp;
765     HRESULT hr;
766
767     memcpy(&d3dpp, &(p_vout->p_sys->d3dpp), sizeof(d3dpp));
768     if( i_width )
769         d3dpp.BackBufferWidth  = i_width;
770     if( i_height )
771         d3dpp.BackBufferHeight = i_height;
772
773     // release all D3D objects
774     Direct3DVoutReleaseScene( p_vout );
775     Direct3DVoutReleasePictures( p_vout );
776
777     hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp);
778     if( SUCCEEDED(hr) )
779     {
780         // re-create them
781         if( (VLC_SUCCESS == Direct3DVoutCreatePictures(p_vout, 1))
782          && (VLC_SUCCESS == Direct3DVoutCreateScene(p_vout)) )
783         {
784             p_vout->p_sys->d3dpp.BackBufferWidth  = i_width;
785             p_vout->p_sys->d3dpp.BackBufferHeight = i_height;
786             return VLC_SUCCESS;
787         }
788         return VLC_EGENERIC;
789     }
790     else {
791         msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
792         return VLC_EGENERIC;
793     }
794     return VLC_SUCCESS;
795 }
796
797 static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target,
798     const D3DFORMAT *formats, size_t count)
799 {
800     LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
801     size_t c;
802
803     for( c=0; c<count; ++c )
804     {
805         HRESULT hr;
806         D3DFORMAT format = formats[c];
807         hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
808                 D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
809                 format, target);
810         if( SUCCEEDED(hr) )
811         {
812             // found a compatible format
813             switch( format )
814             {
815                 case D3DFMT_UYVY:
816                     msg_Dbg( p_vout, "selected surface pixel format is UYVY");
817                     break;
818                 case D3DFMT_YUY2:
819                     msg_Dbg( p_vout, "selected surface pixel format is YUY2");
820                     break;
821                 case D3DFMT_X8R8G8B8:
822                     msg_Dbg( p_vout, "selected surface pixel format is X8R8G8B8");
823                     break;
824                 case D3DFMT_A8R8G8B8:
825                     msg_Dbg( p_vout, "selected surface pixel format is A8R8G8B8");
826                     break;
827                 case D3DFMT_R8G8B8:
828                     msg_Dbg( p_vout, "selected surface pixel format is R8G8B8");
829                     break;
830                 case D3DFMT_R5G6B5:
831                     msg_Dbg( p_vout, "selected surface pixel format is R5G6B5");
832                     break;
833                 case D3DFMT_X1R5G5B5:
834                     msg_Dbg( p_vout, "selected surface pixel format is X1R5G5B5");
835                     break;
836                 default:
837                     msg_Dbg( p_vout, "selected surface pixel format is 0x%0X", format);
838                     break;
839             }
840             return format;
841         }
842         else if( D3DERR_NOTAVAILABLE != hr )
843         {
844             msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
845             break;
846         }
847     }
848     return D3DFMT_UNKNOWN;
849 }
850
851 D3DFORMAT Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
852 {
853     if( p_vout->p_sys->b_hw_yuv )
854     {
855         switch( i_chroma )
856         {
857             case VLC_FOURCC('U','Y','V','Y'):
858             case VLC_FOURCC('U','Y','N','V'):
859             case VLC_FOURCC('Y','4','2','2'):
860             {
861                 static const D3DFORMAT formats[] =
862                     { D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
863                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
864             }
865             case VLC_FOURCC('I','4','2','0'):
866             case VLC_FOURCC('I','4','2','2'):
867             case VLC_FOURCC('Y','V','1','2'):
868             {
869                 /* typically 3D textures don't support planar format
870                 ** fallback to packed version and use CPU for the conversion
871                 */
872                 static const D3DFORMAT formats[] = 
873                     { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
874                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
875             }
876             case VLC_FOURCC('Y','U','Y','2'):
877             case VLC_FOURCC('Y','U','N','V'):
878             {
879                 static const D3DFORMAT formats[] = 
880                     { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
881                 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
882             }
883         }
884     }
885
886     switch( i_chroma )
887     {
888         case VLC_FOURCC('R', 'V', '1', '5'):
889         {
890             static const D3DFORMAT formats[] = 
891                 { D3DFMT_X1R5G5B5 };
892             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
893         }
894         case VLC_FOURCC('R', 'V', '1', '6'):
895         {
896             static const D3DFORMAT formats[] = 
897                 { D3DFMT_R5G6B5 };
898             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
899         }
900         case VLC_FOURCC('R', 'V', '2', '4'):
901         {
902             static const D3DFORMAT formats[] = 
903                 { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 };
904             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
905         }
906         case VLC_FOURCC('R', 'V', '3', '2'):
907         {
908             static const D3DFORMAT formats[] = 
909                 { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 };
910             return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
911         }
912         default:
913         {
914             /* use display default format */
915             LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
916             D3DDISPLAYMODE d3ddm;
917
918             HRESULT hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
919             if( SUCCEEDED(hr))
920             {
921                 /*
922                 ** some professional cards could use some advanced pixel format as default, 
923                 ** make sure we stick with chromas that we can handle internally
924                 */
925                 switch( d3ddm.Format )
926                 {
927                     case D3DFMT_R8G8B8:
928                     case D3DFMT_X8R8G8B8:
929                     case D3DFMT_A8R8G8B8:
930                         msg_Dbg( p_vout, "defaulting to adpater pixel format");
931                         return Direct3DVoutSelectFormat(p_vout, target, &d3ddm.Format, 1);
932                     default:
933                     {
934                         /* if we fall here, that probably means that we need to render some YUV format */
935                         static const D3DFORMAT formats[] = 
936                             { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 };
937                         msg_Dbg( p_vout, "defaulting to built-in pixel format");
938                         return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
939                     }
940                 }
941             }
942         }
943     }
944     return D3DFMT_UNKNOWN;
945 }
946
947 static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format)
948 {
949     switch( format )
950     {
951         case D3DFMT_YUY2:
952             p_vout->output.i_chroma = VLC_FOURCC('Y', 'U', 'Y', '2');
953             break;
954         case D3DFMT_UYVY:
955             p_vout->output.i_chroma = VLC_FOURCC('U', 'Y', 'V', 'Y');
956             break;
957         case D3DFMT_R8G8B8:
958             p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '2', '4');
959             break;
960         case D3DFMT_X8R8G8B8:
961         case D3DFMT_A8R8G8B8:
962             p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '3', '2');
963             p_vout->output.i_rmask = 0x00ff0000;
964             p_vout->output.i_gmask = 0x0000ff00;
965             p_vout->output.i_bmask = 0x000000ff;
966 #       if defined( WORDS_BIGENDIAN )
967             p_vout->output.i_rrshift = 0;
968             p_vout->output.i_lrshift = 24;
969             p_vout->output.i_rgshift = 0;
970             p_vout->output.i_lgshift = 16;
971             p_vout->output.i_rbshift = 0;
972             p_vout->output.i_lbshift = 8;
973 #       else
974             p_vout->output.i_rrshift = 0;
975             p_vout->output.i_lrshift = 8;
976             p_vout->output.i_rgshift = 0;
977             p_vout->output.i_lgshift = 16;
978             p_vout->output.i_rbshift = 0;
979             p_vout->output.i_lbshift = 24;
980 #       endif
981             break;
982         case D3DFMT_R5G6B5:
983             p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '6');
984             p_vout->output.i_rmask = (0x1fL)<<11;
985             p_vout->output.i_gmask = (0x3fL)<<5;
986             p_vout->output.i_bmask = (0x1fL)<<0;
987 #       if defined( WORDS_BIGENDIAN )
988             p_vout->output.i_rrshift = 0;
989             p_vout->output.i_lrshift = 11;
990             p_vout->output.i_rgshift = 0;
991             p_vout->output.i_lgshift = 5;
992             p_vout->output.i_rbshift = 0;
993             p_vout->output.i_lbshift = 0;
994 #       else
995             /* FIXME: since components are not byte aligned,
996                       there is not chance that this will work */
997             p_vout->output.i_rrshift = 0;
998             p_vout->output.i_lrshift = 0;
999             p_vout->output.i_rgshift = 0;
1000             p_vout->output.i_lgshift = 5;
1001             p_vout->output.i_rbshift = 0;
1002             p_vout->output.i_lbshift = 11;
1003 #       endif
1004             break;
1005         case D3DFMT_X1R5G5B5:
1006             p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '5');
1007             p_vout->output.i_rmask = (0x1fL)<<10;
1008             p_vout->output.i_gmask = (0x1fL)<<5;
1009             p_vout->output.i_bmask = (0x1fL)<<0;
1010 #       if defined( WORDS_BIGENDIAN )
1011             p_vout->output.i_rrshift = 0;
1012             p_vout->output.i_lrshift = 10;
1013             p_vout->output.i_rgshift = 0;
1014             p_vout->output.i_lgshift = 5;
1015             p_vout->output.i_rbshift = 0;
1016             p_vout->output.i_lbshift = 0;
1017 #       else
1018             /* FIXME: since components are not byte aligned,
1019                       there is not chance that this will work */
1020             p_vout->output.i_rrshift = 0;
1021             p_vout->output.i_lrshift = 1;
1022             p_vout->output.i_rgshift = 0;
1023             p_vout->output.i_lgshift = 5;
1024             p_vout->output.i_rbshift = 0;
1025             p_vout->output.i_lbshift = 11;
1026 #       endif
1027             break;
1028         default:
1029             return VLC_EGENERIC;
1030     }
1031     return VLC_SUCCESS;
1032 }
1033
1034 /*****************************************************************************
1035  * Direct3DVoutCreatePictures: allocate a vector of identical pictures
1036  *****************************************************************************
1037  * Each picture has an associated offscreen surface in video memory
1038  * depending on hardware capabilities the picture chroma will be as close
1039  * as possible to the orginal render chroma to reduce CPU conversion overhead
1040  * and delegate this work to video card GPU
1041  *****************************************************************************/
1042 static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics )
1043 {
1044     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1045     D3DFORMAT               format    = p_vout->p_sys->d3dpp.BackBufferFormat;
1046     HRESULT hr;
1047     size_t c;
1048
1049     I_OUTPUTPICTURES = 0;
1050
1051     /*
1052     ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
1053     ** the requested chroma which is usable by the hardware in an offscreen surface, as they
1054     ** typically support more formats than textures
1055     */ 
1056     format = Direct3DVoutFindFormat(p_vout, p_vout->render.i_chroma, format);
1057     if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) )
1058     {
1059         msg_Err(p_vout, "surface pixel format is not supported.");
1060         return VLC_EGENERIC;
1061     }
1062
1063     for( c=0; c<i_num_pics; )
1064     {
1065
1066         LPDIRECT3DSURFACE9 p_d3dsurf;
1067         picture_t *p_pic = p_vout->p_picture+c; 
1068
1069         hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev, 
1070                 p_vout->render.i_width,
1071                 p_vout->render.i_height,
1072                 format,
1073                 D3DPOOL_DEFAULT,
1074                 &p_d3dsurf,
1075                 NULL);
1076         if( FAILED(hr) )
1077         {
1078             msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr);
1079             Direct3DVoutReleasePictures(p_vout);
1080             return VLC_EGENERIC;
1081         }
1082
1083         /* fill surface with black color */
1084         IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
1085         
1086         /* assign surface to internal structure */
1087         p_pic->p_sys = (void *)p_d3dsurf;
1088
1089         /* Now that we've got our direct-buffer, we can finish filling in the
1090          * picture_t structures */
1091         switch( p_vout->output.i_chroma )
1092         {
1093             case VLC_FOURCC('R','G','B','2'):
1094                 p_pic->p->i_lines = p_vout->output.i_height;
1095                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1096                 p_pic->p->i_pixel_pitch = 1;
1097                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1098                     p_pic->p->i_pixel_pitch;
1099                 p_pic->i_planes = 1;
1100             break;
1101             case VLC_FOURCC('R','V','1','5'):
1102             case VLC_FOURCC('R','V','1','6'):
1103                 p_pic->p->i_lines = p_vout->output.i_height;
1104                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1105                 p_pic->p->i_pixel_pitch = 2;
1106                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1107                     p_pic->p->i_pixel_pitch;
1108                 p_pic->i_planes = 1;
1109             break;
1110             case VLC_FOURCC('R','V','2','4'):
1111                 p_pic->p->i_lines = p_vout->output.i_height;
1112                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1113                 p_pic->p->i_pixel_pitch = 3;
1114                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1115                     p_pic->p->i_pixel_pitch;
1116                 p_pic->i_planes = 1;
1117             break;
1118             case VLC_FOURCC('R','V','3','2'):
1119                 p_pic->p->i_lines = p_vout->output.i_height;
1120                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1121                 p_pic->p->i_pixel_pitch = 4;
1122                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1123                     p_pic->p->i_pixel_pitch;
1124                 p_pic->i_planes = 1;
1125                 break;
1126             case VLC_FOURCC('U','Y','V','Y'):
1127             case VLC_FOURCC('Y','U','Y','2'):
1128                 p_pic->p->i_lines = p_vout->output.i_height;
1129                 p_pic->p->i_visible_lines = p_vout->output.i_height;
1130                 p_pic->p->i_pixel_pitch = 2;
1131                 p_pic->p->i_visible_pitch = p_vout->output.i_width *
1132                     p_pic->p->i_pixel_pitch;
1133                 p_pic->i_planes = 1;
1134                 break;
1135             default:
1136                 Direct3DVoutReleasePictures(p_vout);
1137                 return VLC_EGENERIC;
1138         }
1139         p_pic->i_status = DESTROYED_PICTURE;
1140         p_pic->i_type   = DIRECT_PICTURE;
1141         p_pic->b_slow   = VLC_TRUE;
1142         p_pic->pf_lock  = Direct3DVoutLockSurface;
1143         p_pic->pf_unlock = Direct3DVoutUnlockSurface;
1144         PP_OUTPUTPICTURE[c] = p_pic;
1145
1146         I_OUTPUTPICTURES = ++c;
1147     }
1148
1149     msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c );
1150
1151     return VLC_SUCCESS;
1152 }
1153
1154 /*****************************************************************************
1155  * Direct3DVoutReleasePictures: destroy a picture vector 
1156  *****************************************************************************
1157  * release all video resources used for pictures
1158  *****************************************************************************/
1159 static void Direct3DVoutReleasePictures( vout_thread_t *p_vout)
1160 {
1161     size_t i_num_pics = I_OUTPUTPICTURES;
1162     size_t c;
1163     for( c=0; c<i_num_pics; ++c )
1164     {
1165         picture_t *p_pic = p_vout->p_picture+c; 
1166         if( p_pic->p_sys )
1167         {
1168             LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1169
1170             p_pic->p_sys = NULL;
1171
1172             if( p_d3dsurf )
1173             {
1174                 IDirect3DSurface9_Release(p_d3dsurf);
1175             }
1176         }
1177     }
1178     msg_Dbg( p_vout, "%u Direct3D pictures released.", c);
1179
1180     I_OUTPUTPICTURES = 0;
1181 }
1182
1183 /*****************************************************************************
1184  * Direct3DVoutLockSurface: Lock surface and get picture data pointer
1185  *****************************************************************************
1186  * This function locks a surface and get the surface descriptor which amongst
1187  * other things has the pointer to the picture data.
1188  *****************************************************************************/
1189 static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1190 {
1191     HRESULT hr;
1192     D3DLOCKED_RECT d3drect;
1193     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1194
1195     if( NULL == p_d3dsurf )
1196         return VLC_EGENERIC;
1197
1198     /* Lock the surface to get a valid pointer to the picture buffer */
1199     hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, D3DLOCK_DISCARD);
1200     if( FAILED(hr) )
1201     {
1202         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1203         return VLC_EGENERIC;
1204     }
1205
1206     /* fill in buffer info in first plane */
1207     p_pic->p->p_pixels = d3drect.pBits;
1208     p_pic->p->i_pitch  = d3drect.Pitch;
1209
1210     return VLC_SUCCESS;
1211 }
1212
1213 /*****************************************************************************
1214  * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface().
1215  *****************************************************************************/
1216 static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
1217 {
1218     HRESULT hr;
1219     LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1220
1221     if( NULL == p_d3dsurf )
1222         return VLC_EGENERIC;
1223
1224     /* Unlock the Surface */
1225     hr = IDirect3DSurface9_UnlockRect(p_d3dsurf);
1226     if( FAILED(hr) )
1227     {
1228         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1229         return VLC_EGENERIC;
1230     }
1231     return VLC_SUCCESS;
1232 }
1233
1234 /*****************************************************************************
1235  * Direct3DVoutCreateScene: allocate and initialize a 3D scene
1236  *****************************************************************************
1237  * for advanced blending/filtering a texture needs be used in a 3D scene.
1238  *****************************************************************************/
1239
1240 static int Direct3DVoutCreateScene( vout_thread_t *p_vout )
1241 {
1242     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1243     LPDIRECT3DTEXTURE9      p_d3dtex;
1244     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1245
1246     HRESULT hr;
1247
1248     /*
1249     ** Create a texture for use when rendering a scene
1250     ** for performance reason, texture format is identical to backbuffer
1251     ** which would usually be a RGB format
1252     */
1253     hr = IDirect3DDevice9_CreateTexture(p_d3ddev, 
1254             p_vout->render.i_width,
1255             p_vout->render.i_height,
1256             1,
1257             D3DUSAGE_RENDERTARGET, 
1258             p_vout->p_sys->d3dpp.BackBufferFormat,
1259             D3DPOOL_DEFAULT,
1260             &p_d3dtex, 
1261             NULL);
1262     if( FAILED(hr))
1263     {
1264         msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr);
1265         return VLC_EGENERIC;
1266     }
1267
1268     /*
1269     ** Create a vertex buffer for use when rendering scene
1270     */
1271     hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev,
1272             sizeof(CUSTOMVERTEX)*4,
1273             D3DUSAGE_WRITEONLY,
1274             D3DFVF_CUSTOMVERTEX,
1275             D3DPOOL_DEFAULT,
1276             &p_d3dvtc,
1277             NULL);
1278     if( FAILED(hr) )
1279     {
1280         msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1281             IDirect3DTexture9_Release(p_d3dtex);
1282         return VLC_EGENERIC;
1283     }
1284
1285     p_vout->p_sys->p_d3dtex = p_d3dtex;
1286     p_vout->p_sys->p_d3dvtc = p_d3dvtc;
1287
1288     // Texture coordinates outside the range [0.0, 1.0] are set 
1289     // to the texture color at 0.0 or 1.0, respectively.
1290     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1291     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1292
1293     // Set linear filtering quality
1294     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1295     IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1296
1297     // set maximum ambient light
1298     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1299
1300     // Turn off culling
1301     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1302
1303     // Turn off the zbuffer
1304     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1305
1306     // Turn off lights
1307     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE);
1308
1309     // Enable dithering
1310     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE);
1311
1312     // disable stencil
1313     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE);
1314
1315     // manage blending
1316     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1317     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1318     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1319     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1320     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10);
1321     IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1322
1323     // Set texture states
1324     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
1325     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1326     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
1327
1328     // turn off alpha operation
1329     IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1330
1331     msg_Dbg( p_vout, "Direct3D scene created successfully");
1332
1333     return VLC_SUCCESS;
1334 }
1335
1336 /*****************************************************************************
1337  * Direct3DVoutReleaseScene
1338  *****************************************************************************/
1339 static void Direct3DVoutReleaseScene( vout_thread_t *p_vout )
1340 {
1341     LPDIRECT3DTEXTURE9      p_d3dtex = p_vout->p_sys->p_d3dtex;
1342     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1343
1344     if( p_d3dvtc )
1345     {
1346         IDirect3DVertexBuffer9_Release(p_d3dvtc);
1347         p_vout->p_sys->p_d3dvtc = NULL;
1348     }
1349
1350     if( p_d3dtex )
1351     {
1352         IDirect3DTexture9_Release(p_d3dtex);
1353         p_vout->p_sys->p_d3dtex = NULL;
1354     }
1355     msg_Dbg( p_vout, "Direct3D scene released successfully");
1356 }
1357
1358 /*****************************************************************************
1359  * Direct3DVoutRenderDefault: copy picture surface to display back buffer
1360  *****************************************************************************
1361  * This function is intented for lower end video cards, without pixel shader 
1362  * support or low video RAM
1363  *****************************************************************************/
1364 static void Direct3DVoutRenderSurface( vout_thread_t *p_vout, picture_t *p_pic )
1365 {
1366     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1367     LPDIRECT3DSURFACE9      p_d3dsrc, p_d3ddest;
1368     UINT                    iSwapChain, iSwapChains;
1369     HRESULT hr;
1370
1371     // check if device is still available    
1372     hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1373     if( FAILED(hr) )
1374     {
1375         if( (D3DERR_DEVICENOTRESET != hr)
1376          || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, 0, 0)) )
1377         {
1378             // device is not usable at present (lost device, out of video mem ?)
1379             return;
1380         }
1381     }
1382
1383     /*  retrieve the number of swap chains */
1384     iSwapChains = IDirect3DDevice9_GetNumberOfSwapChains(p_d3ddev);
1385     if( 0 == iSwapChains )
1386     {
1387         msg_Dbg( p_vout, "no swap chain to render ?");
1388         return;
1389     }
1390
1391     /*  retrieve picture surface */
1392     p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1393     if( NULL == p_d3dsrc )
1394     {
1395         msg_Dbg( p_vout, "no surface to render ?");
1396         return;
1397     }
1398
1399     for( iSwapChain=0; iSwapChain < iSwapChains; ++iSwapChain )
1400     {
1401         /* retrieve swap chain back buffer */
1402         hr = IDirect3DDevice9_GetBackBuffer(p_d3ddev, iSwapChain, 0, D3DBACKBUFFER_TYPE_MONO, &p_d3ddest);
1403         if( FAILED(hr) )
1404         {
1405             msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1406             continue;
1407         }
1408
1409         /* Copy picture surface into back buffer surface, color space conversion happens here */
1410         hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE);
1411         IDirect3DSurface9_Release(p_d3ddest);
1412         if( FAILED(hr) )
1413         {
1414             msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1415             return;
1416         }
1417     }
1418 }
1419
1420 /*****************************************************************************
1421  * Render: copy picture surface into a texture and render into a scene
1422  *****************************************************************************
1423  * This function is intented for higher end 3D cards, with pixel shader support
1424  * and at least 64 MB of video RAM.
1425  *****************************************************************************/
1426 static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic )
1427 {
1428     LPDIRECT3DDEVICE9       p_d3ddev  = p_vout->p_sys->p_d3ddev;
1429     LPDIRECT3DTEXTURE9      p_d3dtex  = p_vout->p_sys->p_d3dtex;
1430     LPDIRECT3DVERTEXBUFFER9 p_d3dvtc  = p_vout->p_sys->p_d3dvtc;
1431     LPDIRECT3DSURFACE9      p_d3dsrc, p_d3ddest;
1432     CUSTOMVERTEX            *p_vertices;
1433     HRESULT hr;
1434     float f_width, f_height;
1435
1436     // check if device is still available    
1437     hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1438     if( FAILED(hr) )
1439     {
1440         if( (D3DERR_DEVICENOTRESET != hr)
1441          || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, 0, 0)) )
1442         {
1443             // device is not usable at present (lost device, out of video mem ?)
1444             return;
1445         }
1446     }
1447
1448     /* Clear the backbuffer and the zbuffer */
1449     hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET,
1450                               D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
1451     if( FAILED(hr) )
1452     {
1453         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1454         return;
1455     }
1456
1457     /*  retrieve picture surface */
1458     p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1459     if( NULL == p_d3dsrc )
1460     {
1461         msg_Dbg( p_vout, "no surface to render ?");
1462         return;
1463     }
1464
1465     /* retrieve texture top-level surface */
1466     hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest);
1467     if( FAILED(hr) )
1468     {
1469         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1470         return;
1471     }
1472
1473     /* Copy picture surface into texture surface, color space conversion happens here */
1474     hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE);
1475     IDirect3DSurface9_Release(p_d3ddest);
1476     if( FAILED(hr) )
1477     {
1478         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1479         return;
1480     }
1481
1482     /* Update the vertex buffer */
1483     hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (VOID **)(&p_vertices), D3DLOCK_DISCARD);
1484     if( FAILED(hr) )
1485     {
1486         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1487         return;
1488     }
1489
1490     /* Setup vertices */
1491     f_width  = (float)(p_vout->p_sys->d3dpp.BackBufferWidth);
1492     f_height = (float)(p_vout->p_sys->d3dpp.BackBufferHeight);
1493
1494     p_vertices[0].x       = 0.0f;       // left
1495     p_vertices[0].y       = 0.0f;       // top
1496     p_vertices[0].z       = 0.0f;
1497     p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1498     p_vertices[0].rhw     = 1.0f;
1499     p_vertices[0].tu      = 0.0f;
1500     p_vertices[0].tv      = 0.0f;
1501  
1502     p_vertices[1].x       = f_width;    // right
1503     p_vertices[1].y       = 0.0f;       // top
1504     p_vertices[1].z       = 0.0f;
1505     p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1506     p_vertices[1].rhw     = 1.0f;
1507     p_vertices[1].tu      = 1.0f;
1508     p_vertices[1].tv      = 0.0f;
1509  
1510     p_vertices[2].x       = f_width;    // right
1511     p_vertices[2].y       = f_height;   // bottom
1512     p_vertices[2].z       = 0.0f;
1513     p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1514     p_vertices[2].rhw     = 1.0f;
1515     p_vertices[2].tu      = 1.0f;
1516     p_vertices[2].tv      = 1.0f;
1517  
1518     p_vertices[3].x       = 0.0f;       // left
1519     p_vertices[3].y       = f_height;   // bottom
1520     p_vertices[3].z       = 0.0f;
1521     p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1522     p_vertices[3].rhw     = 1.0f;
1523     p_vertices[3].tu      = 0.0f;
1524     p_vertices[3].tv      = 1.0f;
1525  
1526     hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc);
1527     if( FAILED(hr) )
1528     {
1529         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1530         return;
1531     }
1532
1533     // Begin the scene
1534     hr = IDirect3DDevice9_BeginScene(p_d3ddev);
1535     if( FAILED(hr) )
1536     {
1537         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1538         return;
1539     }
1540
1541     // Setup our texture. Using textures introduces the texture stage states,
1542     // which govern how textures get blended together (in the case of multiple
1543     // textures) and lighting information. In this case, we are modulating
1544     // (blending) our texture with the diffuse color of the vertices.
1545     hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex);
1546     if( FAILED(hr) )
1547     {
1548         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1549         IDirect3DDevice9_EndScene(p_d3ddev);
1550         return;
1551     }
1552
1553     // Render the vertex buffer contents
1554     hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX));
1555     if( FAILED(hr) )
1556     {
1557         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1558         IDirect3DDevice9_EndScene(p_d3ddev);
1559         return;
1560     }
1561  
1562     // we use FVF instead of vertex shader
1563     hr = IDirect3DDevice9_SetVertexShader(p_d3ddev, NULL);
1564     if( FAILED(hr) )
1565     {
1566         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1567         IDirect3DDevice9_EndScene(p_d3ddev);
1568         return;
1569     }
1570
1571     hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX);
1572     if( FAILED(hr) )
1573     {
1574         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1575         IDirect3DDevice9_EndScene(p_d3ddev);
1576         return;
1577     }
1578
1579     // draw rectangle
1580     hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1581     if( FAILED(hr) )
1582     {
1583         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1584         IDirect3DDevice9_EndScene(p_d3ddev);
1585         return;
1586     }
1587
1588     // End the scene
1589     hr = IDirect3DDevice9_EndScene(p_d3ddev);
1590     if( FAILED(hr) )
1591     {
1592         msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1593         return;
1594     }
1595 }
1596