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