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