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