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