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