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