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