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