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