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