1 /*****************************************************************************
2 * direct3d.c: Windows Direct3D video output module
3 *****************************************************************************
4 * Copyright (C) 2006-2009 the VideoLAN team
7 * Authors: Damien Fouilleul <damienf@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
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.
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.
35 *****************************************************************************/
36 #include <errno.h> /* ENOMEM */
42 #include <vlc_common.h>
43 #include <vlc_plugin.h>
44 #include <vlc_interface.h>
45 #include <vlc_playlist.h>
53 /*****************************************************************************
55 *****************************************************************************/
56 static int OpenVideo ( vlc_object_t * );
57 static void CloseVideo ( vlc_object_t * );
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 * );
65 static int Direct3DVoutCreate ( vout_thread_t * );
66 static void Direct3DVoutRelease ( vout_thread_t * );
68 static int Direct3DVoutOpen ( vout_thread_t * );
69 static void Direct3DVoutClose ( vout_thread_t * );
71 static int Direct3DVoutResetDevice( vout_thread_t * );
73 static int Direct3DVoutCreatePictures ( vout_thread_t *, size_t );
74 static void Direct3DVoutReleasePictures ( vout_thread_t * );
76 static int Direct3DVoutLockSurface ( vout_thread_t *, picture_t * );
77 static int Direct3DVoutUnlockSurface( vout_thread_t *, picture_t * );
79 static int Direct3DVoutCreateScene ( vout_thread_t * );
80 static void Direct3DVoutReleaseScene ( vout_thread_t * );
81 static void Direct3DVoutRenderScene ( vout_thread_t *, picture_t * );
83 static int DesktopCallback( vlc_object_t *p_this, char const *psz_cmd,
84 vlc_value_t oldval, vlc_value_t newval,
87 /*****************************************************************************
89 *****************************************************************************/
91 static bool IsVistaOrAbove(void)
94 winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
96 if( GetVersionEx(&winVer) )
98 if( winVer.dwMajorVersion > 5 )
100 /* Windows Vista or above, make this module the default */
104 /* Windows XP or lower, make sure this module isn't the default */
108 static int OpenVideoXP( vlc_object_t *obj )
110 return IsVistaOrAbove() ? VLC_EGENERIC : OpenVideo( obj );
113 static int OpenVideoVista( vlc_object_t *obj )
115 return IsVistaOrAbove() ? OpenVideo( obj ) : VLC_EGENERIC;
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." )
123 set_shortname( "Direct3D" )
124 set_category( CAT_VIDEO )
125 set_subcategory( SUBCAT_VIDEO_VOUT )
127 add_bool( "direct3d-desktop", false, NULL, DESKTOP_TEXT, DESKTOP_LONGTEXT,
130 set_description( N_("DirectX 3D video output") )
131 set_capability( "video output", 50 )
132 add_shortcut( "direct3d" )
133 set_callbacks( OpenVideoXP, CloseVideo )
135 /* FIXME: Hack to avoid unregistering our window class */
136 linked_with_a_crap_library_which_uses_atexit ()
139 set_capability( "video output", 150 )
140 add_shortcut( "direct3d" )
141 set_callbacks( OpenVideoVista, CloseVideo )
145 /* check if we registered a window class because we need to
148 if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
149 UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
152 /*****************************************************************************
154 *****************************************************************************
155 *****************************************************************************/
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
164 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)
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 )
174 vout_thread_t * p_vout = (vout_thread_t *)p_this;
176 /* Allocate structure */
177 p_vout->p_sys = calloc( 1, sizeof( vout_sys_t ) );
178 if( p_vout->p_sys == NULL )
181 if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) )
183 msg_Err( p_vout, "Direct3D could not be initialized !");
184 Direct3DVoutRelease( p_vout );
185 free( p_vout->p_sys );
189 /* Initialisations */
190 p_vout->pf_init = Init;
191 p_vout->pf_end = End;
192 p_vout->pf_manage = Manage;
193 p_vout->pf_render = Direct3DVoutRenderScene;
194 p_vout->pf_display = FirstDisplay;
195 p_vout->pf_control = Control;
197 if( CommonInit( p_vout ) )
200 p_vout->p_sys->b_desktop = false;
201 var_Create( p_vout, "directx-hw-yuv", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
202 var_Create( p_vout, "directx-device", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
204 /* Trigger a callback right now */
205 var_Create( p_vout, "direct3d-desktop", VLC_VAR_BOOL|VLC_VAR_DOINHERIT );
206 val.psz_string = _("Desktop");
207 var_Change( p_vout, "direct3d-desktop", VLC_VAR_SETTEXT, &val, NULL );
208 var_AddCallback( p_vout, "direct3d-desktop", DesktopCallback, NULL );
209 var_TriggerCallback( p_vout, "direct3d-desktop" );
214 CloseVideo( VLC_OBJECT(p_vout) );
218 /*****************************************************************************
219 * CloseVideo: destroy Sys video thread output method
220 *****************************************************************************
221 * Terminate an output method created by Create
222 *****************************************************************************/
223 static void CloseVideo( vlc_object_t *p_this )
225 vout_thread_t * p_vout = (vout_thread_t *)p_this;
227 Direct3DVoutRelease( p_vout );
229 CommonClean( p_vout );
231 free( p_vout->p_sys );
234 /*****************************************************************************
235 * Init: initialize Direct3D video thread output method
236 *****************************************************************************/
237 static int Init( vout_thread_t *p_vout )
241 p_vout->p_sys->b_hw_yuv = var_GetBool( p_vout, "directx-hw-yuv" );
243 /* Initialise Direct3D */
244 if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) )
246 msg_Err( p_vout, "cannot initialize Direct3D" );
250 /* Initialize the output structure.
251 * Since Direct3D can do rescaling for us, stick to the default
252 * coordinates and aspect. */
253 p_vout->output.i_width = p_vout->render.i_width;
254 p_vout->output.i_height = p_vout->render.i_height;
255 p_vout->output.i_aspect = p_vout->render.i_aspect;
256 p_vout->fmt_out = p_vout->fmt_in;
257 UpdateRects( p_vout, true );
259 /* create picture pool */
260 p_vout->output.i_chroma = 0;
261 i_ret = Direct3DVoutCreatePictures(p_vout, 1);
262 if( VLC_SUCCESS != i_ret )
264 msg_Err(p_vout, "Direct3D picture pool initialization failed !");
269 i_ret = Direct3DVoutCreateScene(p_vout);
270 if( VLC_SUCCESS != i_ret )
272 msg_Err(p_vout, "Direct3D scene initialization failed !");
273 Direct3DVoutReleasePictures(p_vout);
277 /* Change the window title bar text */
278 EventThreadUpdateTitle( p_vout->p_sys->p_event, VOUT_TITLE " (Direct3D output)" );
280 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
284 /*****************************************************************************
285 * End: terminate Sys video thread output method
286 *****************************************************************************
287 * Terminate an output method created by Create.
288 * It is called at the end of the thread.
289 *****************************************************************************/
290 static void End( vout_thread_t *p_vout )
292 Direct3DVoutReleaseScene(p_vout);
293 Direct3DVoutReleasePictures(p_vout);
294 Direct3DVoutClose( p_vout );
297 /*****************************************************************************
298 * Manage: handle Sys events
299 *****************************************************************************
300 * This function should be called regularly by the video output thread.
301 * It returns a non null value if an error occurred.
302 *****************************************************************************/
303 static int Manage( vout_thread_t *p_vout )
305 CommonManage( p_vout );
310 if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
312 #if 0 /* need that when bicubic filter is available */
316 GetClientRect(p_vout->p_sys->hvideownd, &rect);
317 width = rect.right-rect.left;
318 height = rect.bottom-rect.top;
320 if( (width != p_vout->p_sys->d3dpp.BackBufferWidth)
321 || (height != p_vout->p_sys->d3dpp.BackBufferHeight) )
323 msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height);
324 // need to reset D3D device to resize back buffer
325 if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) )
329 p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
333 * Desktop mode change
335 if( p_vout->p_sys->i_changes & DX_DESKTOP_CHANGE )
337 /* Close the direct3d instance attached to the current output window. */
340 ExitFullscreen( p_vout );
342 EventThreadStop( p_vout->p_sys->p_event );
344 /* Open the direct3d output and attaches it to the new window */
345 p_vout->p_sys->b_desktop = !p_vout->p_sys->b_desktop;
346 p_vout->pf_display = FirstDisplay;
348 EventThreadStart( p_vout->p_sys->p_event );
353 p_vout->p_sys->i_changes &= ~DX_DESKTOP_CHANGE;
359 /*****************************************************************************
360 * Display: displays previously rendered output
361 *****************************************************************************
362 * This function sends the currently rendered image to the display, wait until
363 * it is displayed and switch the two rendering buffers, preparing next frame.
364 *****************************************************************************/
365 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
369 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
371 // Present the back buffer contents to the display
372 // stretching and filtering happens here
373 HRESULT hr = IDirect3DDevice9_Present(p_d3ddev,
374 &(p_vout->p_sys->rect_src_clipped),
377 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
381 ** this function is only used once when the first picture is received
382 ** this function will show the video window once a picture is ready
385 static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic )
387 /* get initial picture presented through D3D */
388 Display(p_vout, p_pic);
391 ** Video window is initially hidden, show it now since we got a
394 SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
402 /* use and restores proper display function for further pictures */
403 p_vout->pf_display = Display;
406 /*****************************************************************************
407 * DirectD3DVoutCreate: Initialize and instance of Direct3D9
408 *****************************************************************************
409 * This function initialize Direct3D and analyze available resources from
411 *****************************************************************************/
412 static int Direct3DVoutCreate( vout_thread_t *p_vout )
415 LPDIRECT3D9 p_d3dobj;
418 LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
420 p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
421 if( NULL == p_vout->p_sys->hd3d9_dll )
423 msg_Warn( p_vout, "cannot load d3d9.dll, aborting" );
428 (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll,
429 TEXT("Direct3DCreate9") );
430 if( OurDirect3DCreate9 == NULL )
432 msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" );
436 /* Create the D3D object. */
437 p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION );
438 if( NULL == p_d3dobj )
440 msg_Err( p_vout, "Could not create Direct3D9 instance.");
443 p_vout->p_sys->p_d3dobj = p_d3dobj;
446 ** Get device capabilities
448 ZeroMemory(&d3dCaps, sizeof(d3dCaps));
449 hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
452 msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr);
455 /* TODO: need to test device capabilities and select the right render function */
460 /*****************************************************************************
461 * DirectD3DVoutRelease: release an instance of Direct3D9
462 *****************************************************************************/
464 static void Direct3DVoutRelease( vout_thread_t *p_vout )
466 if( p_vout->p_sys->p_d3dobj )
468 IDirect3D9_Release(p_vout->p_sys->p_d3dobj);
469 p_vout->p_sys->p_d3dobj = NULL;
471 if( NULL != p_vout->p_sys->hd3d9_dll )
473 FreeLibrary(p_vout->p_sys->hd3d9_dll);
474 p_vout->p_sys->hd3d9_dll = NULL;
478 static int Direct3DFillPresentationParameters(vout_thread_t *p_vout, D3DPRESENT_PARAMETERS *d3dpp)
480 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
481 D3DDISPLAYMODE d3ddm;
485 ** Get the current desktop display mode, so we can set up a back
486 ** buffer of the same format
488 hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
491 msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr);
495 /* keep a copy of current desktop format */
496 p_vout->p_sys->bbFormat = d3ddm.Format;
498 /* Set up the structure used to create the D3DDevice. */
499 ZeroMemory( d3dpp, sizeof(D3DPRESENT_PARAMETERS) );
500 d3dpp->Flags = D3DPRESENTFLAG_VIDEO;
501 d3dpp->Windowed = TRUE;
502 d3dpp->hDeviceWindow = p_vout->p_sys->hvideownd;
503 d3dpp->BackBufferWidth = p_vout->output.i_width;
504 d3dpp->BackBufferHeight = p_vout->output.i_height;
505 d3dpp->SwapEffect = D3DSWAPEFFECT_COPY;
506 d3dpp->MultiSampleType = D3DMULTISAMPLE_NONE;
507 d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
508 d3dpp->BackBufferFormat = d3ddm.Format;
509 d3dpp->BackBufferCount = 1;
510 d3dpp->EnableAutoDepthStencil = FALSE;
515 /*****************************************************************************
516 * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations
517 *****************************************************************************
518 * This function creates Direct3D device
519 * this must be called from the vout thread for performance reason, as
520 * all Direct3D Device APIs are used in a non multithread safe environment
521 *****************************************************************************/
522 static int Direct3DVoutOpen( vout_thread_t *p_vout )
524 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
525 LPDIRECT3DDEVICE9 p_d3ddev;
526 D3DPRESENT_PARAMETERS d3dpp;
529 if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
532 // Create the D3DDevice
533 hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT,
534 D3DDEVTYPE_HAL, p_vout->p_sys->hvideownd,
535 D3DCREATE_SOFTWARE_VERTEXPROCESSING|
536 D3DCREATE_MULTITHREADED,
540 msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr);
543 p_vout->p_sys->p_d3ddev = p_d3ddev;
545 msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" );
549 /*****************************************************************************
550 * DirectD3DClose: release the Direct3D9 device
551 *****************************************************************************/
552 static void Direct3DVoutClose( vout_thread_t *p_vout )
554 if( p_vout->p_sys->p_d3ddev )
556 IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev);
557 p_vout->p_sys->p_d3ddev = NULL;
560 p_vout->p_sys->hmonitor = NULL;
563 /*****************************************************************************
564 * DirectD3DClose: reset the Direct3D9 device
565 *****************************************************************************
566 * All resources must be deallocated before the reset occur, they will be
567 * realllocated once the reset has been performed successfully
568 *****************************************************************************/
569 static int Direct3DVoutResetDevice( vout_thread_t *p_vout )
571 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
572 D3DPRESENT_PARAMETERS d3dpp;
575 if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout, &d3dpp) )
578 // release all D3D objects
579 Direct3DVoutReleaseScene( p_vout );
580 Direct3DVoutReleasePictures( p_vout );
582 hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp);
586 if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1))
587 || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) )
589 msg_Dbg(p_vout, "%s failed !", __FUNCTION__);
594 msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
600 static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target,
601 const D3DFORMAT *formats, size_t count)
603 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
606 for( c=0; c<count; ++c )
609 D3DFORMAT format = formats[c];
610 /* test whether device can create a surface of that format */
611 hr = IDirect3D9_CheckDeviceFormat(p_d3dobj, D3DADAPTER_DEFAULT,
612 D3DDEVTYPE_HAL, target, 0, D3DRTYPE_SURFACE, format);
615 /* test whether device can perform color-conversion
616 ** from that format to target format
618 hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
619 D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
624 // found a compatible format
628 msg_Dbg( p_vout, "selected surface pixel format is UYVY");
631 msg_Dbg( p_vout, "selected surface pixel format is YUY2");
633 case D3DFMT_X8R8G8B8:
634 msg_Dbg( p_vout, "selected surface pixel format is X8R8G8B8");
636 case D3DFMT_A8R8G8B8:
637 msg_Dbg( p_vout, "selected surface pixel format is A8R8G8B8");
640 msg_Dbg( p_vout, "selected surface pixel format is R8G8B8");
643 msg_Dbg( p_vout, "selected surface pixel format is R5G6B5");
645 case D3DFMT_X1R5G5B5:
646 msg_Dbg( p_vout, "selected surface pixel format is X1R5G5B5");
649 msg_Dbg( p_vout, "selected surface pixel format is 0x%0X", format);
654 else if( D3DERR_NOTAVAILABLE != hr )
656 msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
660 return D3DFMT_UNKNOWN;
663 static D3DFORMAT Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
665 //if( p_vout->p_sys->b_hw_yuv && ! _got_vista_or_above )
666 if( p_vout->p_sys->b_hw_yuv )
668 /* it sounds like vista does not support YUV surfaces at all */
673 static const D3DFORMAT formats[] =
674 { D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
675 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
681 /* typically 3D textures don't support planar format
682 ** fallback to packed version and use CPU for the conversion
684 static const D3DFORMAT formats[] =
685 { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
686 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
690 static const D3DFORMAT formats[] =
691 { D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
692 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
699 case VLC_CODEC_RGB15:
701 static const D3DFORMAT formats[] =
703 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
705 case VLC_CODEC_RGB16:
707 static const D3DFORMAT formats[] =
709 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
711 case VLC_CODEC_RGB24:
713 static const D3DFORMAT formats[] =
714 { D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 };
715 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
717 case VLC_CODEC_RGB32:
719 static const D3DFORMAT formats[] =
720 { D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 };
721 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
725 /* use display default format */
726 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
727 D3DDISPLAYMODE d3ddm;
729 HRESULT hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
733 ** some professional cards could use some advanced pixel format as default,
734 ** make sure we stick with chromas that we can handle internally
736 switch( d3ddm.Format )
739 case D3DFMT_X8R8G8B8:
740 case D3DFMT_A8R8G8B8:
742 case D3DFMT_X1R5G5B5:
743 msg_Dbg( p_vout, "defaulting to adapter pixel format");
744 return Direct3DVoutSelectFormat(p_vout, target, &d3ddm.Format, 1);
747 /* if we fall here, that probably means that we need to render some YUV format */
748 static const D3DFORMAT formats[] =
749 { D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
750 msg_Dbg( p_vout, "defaulting to built-in pixel format");
751 return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
757 return D3DFMT_UNKNOWN;
760 static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format)
765 p_vout->output.i_chroma = VLC_CODEC_YUYV;
768 p_vout->output.i_chroma = VLC_CODEC_UYVY;
771 p_vout->output.i_chroma = VLC_CODEC_RGB24;
772 p_vout->output.i_rmask = 0xff0000;
773 p_vout->output.i_gmask = 0x00ff00;
774 p_vout->output.i_bmask = 0x0000ff;
776 case D3DFMT_X8R8G8B8:
777 case D3DFMT_A8R8G8B8:
778 p_vout->output.i_chroma = VLC_CODEC_RGB32;
779 p_vout->output.i_rmask = 0x00ff0000;
780 p_vout->output.i_gmask = 0x0000ff00;
781 p_vout->output.i_bmask = 0x000000ff;
784 p_vout->output.i_chroma = VLC_CODEC_RGB16;
785 p_vout->output.i_rmask = (0x1fL)<<11;
786 p_vout->output.i_gmask = (0x3fL)<<5;
787 p_vout->output.i_bmask = (0x1fL)<<0;
789 case D3DFMT_X1R5G5B5:
790 p_vout->output.i_chroma = VLC_CODEC_RGB15;
791 p_vout->output.i_rmask = (0x1fL)<<10;
792 p_vout->output.i_gmask = (0x1fL)<<5;
793 p_vout->output.i_bmask = (0x1fL)<<0;
801 /*****************************************************************************
802 * Direct3DVoutCreatePictures: allocate a vector of identical pictures
803 *****************************************************************************
804 * Each picture has an associated offscreen surface in video memory
805 * depending on hardware capabilities the picture chroma will be as close
806 * as possible to the orginal render chroma to reduce CPU conversion overhead
807 * and delegate this work to video card GPU
808 *****************************************************************************/
809 static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics )
811 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
815 // if vout is already running, use current chroma, otherwise choose from upstream
816 int i_chroma = p_vout->output.i_chroma ? p_vout->output.i_chroma
817 : p_vout->render.i_chroma;
819 I_OUTPUTPICTURES = 0;
822 ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to
823 ** the requested chroma which is usable by the hardware in an offscreen surface, as they
824 ** typically support more formats than textures
826 format = Direct3DVoutFindFormat(p_vout, i_chroma, p_vout->p_sys->bbFormat);
827 if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) )
829 msg_Err(p_vout, "surface pixel format is not supported.");
833 for( c=0; c<i_num_pics; )
836 LPDIRECT3DSURFACE9 p_d3dsurf;
837 picture_t *p_pic = p_vout->p_picture+c;
839 hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev,
840 p_vout->render.i_width,
841 p_vout->render.i_height,
848 msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr);
849 Direct3DVoutReleasePictures(p_vout);
853 /* fill surface with black color */
854 IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
856 /* assign surface to internal structure */
857 p_pic->p_sys = (void *)p_d3dsurf;
859 /* Now that we've got our direct-buffer, we can finish filling in the
860 * picture_t structures */
861 switch( p_vout->output.i_chroma )
864 p_pic->p->i_lines = p_vout->output.i_height;
865 p_pic->p->i_visible_lines = p_vout->output.i_height;
866 p_pic->p->i_pixel_pitch = 1;
867 p_pic->p->i_visible_pitch = p_vout->output.i_width *
868 p_pic->p->i_pixel_pitch;
871 case VLC_CODEC_RGB15:
872 case VLC_CODEC_RGB16:
873 p_pic->p->i_lines = p_vout->output.i_height;
874 p_pic->p->i_visible_lines = p_vout->output.i_height;
875 p_pic->p->i_pixel_pitch = 2;
876 p_pic->p->i_visible_pitch = p_vout->output.i_width *
877 p_pic->p->i_pixel_pitch;
880 case VLC_CODEC_RGB24:
881 p_pic->p->i_lines = p_vout->output.i_height;
882 p_pic->p->i_visible_lines = p_vout->output.i_height;
883 p_pic->p->i_pixel_pitch = 3;
884 p_pic->p->i_visible_pitch = p_vout->output.i_width *
885 p_pic->p->i_pixel_pitch;
888 case VLC_CODEC_RGB32:
889 p_pic->p->i_lines = p_vout->output.i_height;
890 p_pic->p->i_visible_lines = p_vout->output.i_height;
891 p_pic->p->i_pixel_pitch = 4;
892 p_pic->p->i_visible_pitch = p_vout->output.i_width *
893 p_pic->p->i_pixel_pitch;
898 p_pic->p->i_lines = p_vout->output.i_height;
899 p_pic->p->i_visible_lines = p_vout->output.i_height;
900 p_pic->p->i_pixel_pitch = 2;
901 p_pic->p->i_visible_pitch = p_vout->output.i_width *
902 p_pic->p->i_pixel_pitch;
906 Direct3DVoutReleasePictures(p_vout);
909 p_pic->i_status = DESTROYED_PICTURE;
910 p_pic->i_type = DIRECT_PICTURE;
911 p_pic->b_slow = true;
912 p_pic->pf_lock = Direct3DVoutLockSurface;
913 p_pic->pf_unlock = Direct3DVoutUnlockSurface;
914 PP_OUTPUTPICTURE[c] = p_pic;
916 I_OUTPUTPICTURES = ++c;
919 msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c );
924 /*****************************************************************************
925 * Direct3DVoutReleasePictures: destroy a picture vector
926 *****************************************************************************
927 * release all video resources used for pictures
928 *****************************************************************************/
929 static void Direct3DVoutReleasePictures( vout_thread_t *p_vout)
931 size_t i_num_pics = I_OUTPUTPICTURES;
933 for( c=0; c<i_num_pics; ++c )
935 picture_t *p_pic = p_vout->p_picture+c;
938 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
944 IDirect3DSurface9_Release(p_d3dsurf);
948 msg_Dbg( p_vout, "%u Direct3D pictures released.", c);
950 I_OUTPUTPICTURES = 0;
953 /*****************************************************************************
954 * Direct3DVoutLockSurface: Lock surface and get picture data pointer
955 *****************************************************************************
956 * This function locks a surface and get the surface descriptor which amongst
957 * other things has the pointer to the picture data.
958 *****************************************************************************/
959 static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic )
962 D3DLOCKED_RECT d3drect;
963 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
965 if( NULL == p_d3dsurf )
968 /* Lock the surface to get a valid pointer to the picture buffer */
969 hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0);
972 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
976 /* fill in buffer info in first plane */
977 p_pic->p->p_pixels = d3drect.pBits;
978 p_pic->p->i_pitch = d3drect.Pitch;
983 /*****************************************************************************
984 * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface().
985 *****************************************************************************/
986 static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
989 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
991 if( NULL == p_d3dsurf )
994 /* Unlock the Surface */
995 hr = IDirect3DSurface9_UnlockRect(p_d3dsurf);
998 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1004 /*****************************************************************************
1005 * Direct3DVoutCreateScene: allocate and initialize a 3D scene
1006 *****************************************************************************
1007 * for advanced blending/filtering a texture needs be used in a 3D scene.
1008 *****************************************************************************/
1010 static int Direct3DVoutCreateScene( vout_thread_t *p_vout )
1012 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
1013 LPDIRECT3DTEXTURE9 p_d3dtex;
1014 LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1019 ** Create a texture for use when rendering a scene
1020 ** for performance reason, texture format is identical to backbuffer
1021 ** which would usually be a RGB format
1023 hr = IDirect3DDevice9_CreateTexture(p_d3ddev,
1024 p_vout->render.i_width,
1025 p_vout->render.i_height,
1027 D3DUSAGE_RENDERTARGET,
1028 p_vout->p_sys->bbFormat,
1034 msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr);
1035 return VLC_EGENERIC;
1039 ** Create a vertex buffer for use when rendering scene
1041 hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev,
1042 sizeof(CUSTOMVERTEX)*4,
1043 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1044 D3DFVF_CUSTOMVERTEX,
1050 msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1051 IDirect3DTexture9_Release(p_d3dtex);
1052 return VLC_EGENERIC;
1055 p_vout->p_sys->p_d3dtex = p_d3dtex;
1056 p_vout->p_sys->p_d3dvtc = p_d3dvtc;
1058 // Texture coordinates outside the range [0.0, 1.0] are set
1059 // to the texture color at 0.0 or 1.0, respectively.
1060 IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1061 IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1063 // Set linear filtering quality
1064 IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1065 IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1067 // set maximum ambient light
1068 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1071 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1073 // Turn off the zbuffer
1074 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1077 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE);
1080 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE);
1083 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE);
1086 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE);
1087 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
1088 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
1089 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE);
1090 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10);
1091 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
1093 // Set texture states
1094 IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
1095 IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
1096 IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
1098 // turn off alpha operation
1099 IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1101 msg_Dbg( p_vout, "Direct3D scene created successfully");
1106 /*****************************************************************************
1107 * Direct3DVoutReleaseScene
1108 *****************************************************************************/
1109 static void Direct3DVoutReleaseScene( vout_thread_t *p_vout )
1111 LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex;
1112 LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1116 IDirect3DVertexBuffer9_Release(p_d3dvtc);
1117 p_vout->p_sys->p_d3dvtc = NULL;
1122 IDirect3DTexture9_Release(p_d3dtex);
1123 p_vout->p_sys->p_d3dtex = NULL;
1125 msg_Dbg( p_vout, "Direct3D scene released successfully");
1128 /*****************************************************************************
1129 * Render: copy picture surface into a texture and render into a scene
1130 *****************************************************************************
1131 * This function is intented for higher end 3D cards, with pixel shader support
1132 * and at least 64 MB of video RAM.
1133 *****************************************************************************/
1134 static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic )
1136 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
1137 LPDIRECT3DTEXTURE9 p_d3dtex;
1138 LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
1139 LPDIRECT3DSURFACE9 p_d3dsrc, p_d3ddest;
1140 CUSTOMVERTEX *p_vertices;
1142 float f_width, f_height;
1144 // check if device is still available
1145 hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1148 if( (D3DERR_DEVICENOTRESET != hr)
1149 || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) )
1151 // device is not usable at present (lost device, out of video mem ?)
1155 p_d3dtex = p_vout->p_sys->p_d3dtex;
1156 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1158 /* Clear the backbuffer and the zbuffer */
1159 hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET,
1160 D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
1163 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1167 /* retrieve picture surface */
1168 p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1169 if( NULL == p_d3dsrc )
1171 msg_Dbg( p_vout, "no surface to render ?");
1175 /* retrieve texture top-level surface */
1176 hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest);
1179 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1183 /* Copy picture surface into texture surface, color space conversion happens here */
1184 hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE);
1185 IDirect3DSurface9_Release(p_d3ddest);
1188 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1192 /* Update the vertex buffer */
1193 hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (&p_vertices), D3DLOCK_DISCARD);
1196 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1200 /* Setup vertices */
1201 f_width = (float)(p_vout->output.i_width);
1202 f_height = (float)(p_vout->output.i_height);
1204 /* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
1205 /* http://www.sjbrown.co.uk/2003/05/01/fix-directx-rasterisation/ */
1206 p_vertices[0].x = -0.5f; // left
1207 p_vertices[0].y = -0.5f; // top
1208 p_vertices[0].z = 0.0f;
1209 p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1210 p_vertices[0].rhw = 1.0f;
1211 p_vertices[0].tu = 0.0f;
1212 p_vertices[0].tv = 0.0f;
1214 p_vertices[1].x = f_width - 0.5f; // right
1215 p_vertices[1].y = -0.5f; // top
1216 p_vertices[1].z = 0.0f;
1217 p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1218 p_vertices[1].rhw = 1.0f;
1219 p_vertices[1].tu = 1.0f;
1220 p_vertices[1].tv = 0.0f;
1222 p_vertices[2].x = f_width - 0.5f; // right
1223 p_vertices[2].y = f_height - 0.5f; // bottom
1224 p_vertices[2].z = 0.0f;
1225 p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1226 p_vertices[2].rhw = 1.0f;
1227 p_vertices[2].tu = 1.0f;
1228 p_vertices[2].tv = 1.0f;
1230 p_vertices[3].x = -0.5f; // left
1231 p_vertices[3].y = f_height - 0.5f; // bottom
1232 p_vertices[3].z = 0.0f;
1233 p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255);
1234 p_vertices[3].rhw = 1.0f;
1235 p_vertices[3].tu = 0.0f;
1236 p_vertices[3].tv = 1.0f;
1238 hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc);
1241 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1246 hr = IDirect3DDevice9_BeginScene(p_d3ddev);
1249 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1253 // Setup our texture. Using textures introduces the texture stage states,
1254 // which govern how textures get blended together (in the case of multiple
1255 // textures) and lighting information. In this case, we are modulating
1256 // (blending) our texture with the diffuse color of the vertices.
1257 hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex);
1260 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1261 IDirect3DDevice9_EndScene(p_d3ddev);
1265 // Render the vertex buffer contents
1266 hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX));
1269 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1270 IDirect3DDevice9_EndScene(p_d3ddev);
1274 // we use FVF instead of vertex shader
1275 hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX);
1278 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1279 IDirect3DDevice9_EndScene(p_d3ddev);
1284 hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1287 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1288 IDirect3DDevice9_EndScene(p_d3ddev);
1293 hr = IDirect3DDevice9_EndScene(p_d3ddev);
1296 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1302 /*****************************************************************************
1303 * DesktopCallback: desktop mode variable callback
1304 *****************************************************************************/
1305 static int DesktopCallback( vlc_object_t *p_this, char const *psz_cmd,
1306 vlc_value_t oldval, vlc_value_t newval,
1309 VLC_UNUSED( psz_cmd );
1310 VLC_UNUSED( oldval );
1311 VLC_UNUSED( p_data );
1313 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1315 if( (newval.b_bool && !p_vout->p_sys->b_desktop) ||
1316 (!newval.b_bool && p_vout->p_sys->b_desktop) )
1318 playlist_t *p_playlist = pl_Hold( p_vout );
1322 /* Modify playlist as well because the vout might have to be
1324 var_Create( p_playlist, "direct3d-desktop", VLC_VAR_BOOL );
1325 var_Set( p_playlist, "direct3d-desktop", newval );
1326 pl_Release( p_vout );
1329 p_vout->p_sys->i_changes |= DX_DESKTOP_CHANGE;