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 p_vout->p_sys->b_desktop = false;
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;
199 if( CommonInit( p_vout ) )
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 );
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" );
215 CloseVideo( VLC_OBJECT(p_vout) );
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 )
226 vout_thread_t * p_vout = (vout_thread_t *)p_this;
228 CommonClean( p_vout );
230 Direct3DVoutRelease( p_vout );
232 free( p_vout->p_sys );
235 /*****************************************************************************
236 * Init: initialize Direct3D video thread output method
237 *****************************************************************************/
238 static int Init( vout_thread_t *p_vout )
242 p_vout->p_sys->b_hw_yuv = var_GetBool( p_vout, "directx-hw-yuv" );
244 /* Initialise Direct3D */
245 if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) )
247 msg_Err( p_vout, "cannot initialize Direct3D" );
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 );
260 /* create picture pool */
261 p_vout->output.i_chroma = 0;
262 i_ret = Direct3DVoutCreatePictures(p_vout, 1);
263 if( VLC_SUCCESS != i_ret )
265 msg_Err(p_vout, "Direct3D picture pool initialization failed !");
270 i_ret = Direct3DVoutCreateScene(p_vout);
271 if( VLC_SUCCESS != i_ret )
273 msg_Err(p_vout, "Direct3D scene initialization failed !");
274 Direct3DVoutReleasePictures(p_vout);
278 /* Change the window title bar text */
279 EventThreadUpdateTitle( p_vout->p_sys->p_event, VOUT_TITLE " (Direct3D output)" );
281 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
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 )
293 Direct3DVoutReleaseScene(p_vout);
294 Direct3DVoutReleasePictures(p_vout);
295 Direct3DVoutClose( p_vout );
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 )
306 vout_sys_t *p_sys = p_vout->p_sys;
308 CommonManage( p_vout );
313 if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE )
315 #if 0 /* need that when bicubic filter is available */
319 GetClientRect(p_vout->p_sys->hvideownd, &rect);
320 width = rect.right-rect.left;
321 height = rect.bottom-rect.top;
323 if( (width != p_vout->p_sys->d3dpp.BackBufferWidth)
324 || (height != p_vout->p_sys->d3dpp.BackBufferHeight) )
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) )
332 p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE;
336 * Desktop mode change
338 if( p_vout->p_sys->i_changes & DX_DESKTOP_CHANGE )
340 /* Close the direct3d instance attached to the current output window. */
343 ExitFullscreen( p_vout );
345 EventThreadStop( p_vout->p_sys->p_event );
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;
352 memset(&cfg, 0, sizeof(cfg));
353 cfg.use_desktop = p_vout->p_sys->b_desktop;
356 EventThreadStart( p_vout->p_sys->p_event, &hwnd, &cfg );
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;
367 p_vout->p_sys->i_changes &= ~DX_DESKTOP_CHANGE;
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 )
383 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
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,
392 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
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
400 static void FirstDisplay( vout_thread_t *p_vout, picture_t *p_pic )
402 /* get initial picture presented through D3D */
403 Display(p_vout, p_pic);
406 ** Video window is initially hidden, show it now since we got a
409 SetWindowPos( p_vout->p_sys->hvideownd, 0, 0, 0, 0, 0,
417 /* use and restores proper display function for further pictures */
418 p_vout->pf_display = Display;
421 /*****************************************************************************
422 * DirectD3DVoutCreate: Initialize and instance of Direct3D9
423 *****************************************************************************
424 * This function initialize Direct3D and analyze available resources from
426 *****************************************************************************/
427 static int Direct3DVoutCreate( vout_thread_t *p_vout )
430 LPDIRECT3D9 p_d3dobj;
433 LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
435 p_vout->p_sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
436 if( NULL == p_vout->p_sys->hd3d9_dll )
438 msg_Warn( p_vout, "cannot load d3d9.dll, aborting" );
443 (void *)GetProcAddress( p_vout->p_sys->hd3d9_dll,
444 TEXT("Direct3DCreate9") );
445 if( OurDirect3DCreate9 == NULL )
447 msg_Err( p_vout, "Cannot locate reference to Direct3DCreate9 ABI in DLL" );
451 /* Create the D3D object. */
452 p_d3dobj = OurDirect3DCreate9( D3D_SDK_VERSION );
453 if( NULL == p_d3dobj )
455 msg_Err( p_vout, "Could not create Direct3D9 instance.");
458 p_vout->p_sys->p_d3dobj = p_d3dobj;
461 ** Get device capabilities
463 ZeroMemory(&d3dCaps, sizeof(d3dCaps));
464 hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps);
467 msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr);
470 /* TODO: need to test device capabilities and select the right render function */
475 /*****************************************************************************
476 * DirectD3DVoutRelease: release an instance of Direct3D9
477 *****************************************************************************/
479 static void Direct3DVoutRelease( vout_thread_t *p_vout )
481 if( p_vout->p_sys->p_d3dobj )
483 IDirect3D9_Release(p_vout->p_sys->p_d3dobj);
484 p_vout->p_sys->p_d3dobj = NULL;
486 if( NULL != p_vout->p_sys->hd3d9_dll )
488 FreeLibrary(p_vout->p_sys->hd3d9_dll);
489 p_vout->p_sys->hd3d9_dll = NULL;
493 static int Direct3DFillPresentationParameters(vout_thread_t *p_vout)
495 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
496 D3DDISPLAYMODE d3ddm;
500 ** Get the current desktop display mode, so we can set up a back
501 ** buffer of the same format
503 hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm );
506 msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr);
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;
525 const unsigned i_adapter_count = IDirect3D9_GetAdapterCount(p_d3dobj);
526 for( unsigned i = 1; i < i_adapter_count; i++ )
528 hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, i, &d3ddm );
531 d3dpp->BackBufferWidth = __MAX(d3dpp->BackBufferWidth, d3ddm.Width);
532 d3dpp->BackBufferHeight = __MAX(d3dpp->BackBufferHeight, d3ddm.Height);
535 RECT *display = &p_vout->p_sys->rect_display;
538 display->right = d3dpp->BackBufferWidth;
539 display->bottom = d3dpp->BackBufferHeight;
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 )
553 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
554 LPDIRECT3DDEVICE9 p_d3ddev;
557 if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout) )
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 );
568 msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr);
571 p_vout->p_sys->p_d3ddev = p_d3ddev;
573 msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" );
577 /*****************************************************************************
578 * DirectD3DClose: release the Direct3D9 device
579 *****************************************************************************/
580 static void Direct3DVoutClose( vout_thread_t *p_vout )
582 if( p_vout->p_sys->p_d3ddev )
584 IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev);
585 p_vout->p_sys->p_d3ddev = NULL;
588 p_vout->p_sys->hmonitor = NULL;
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 )
599 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
602 if( VLC_SUCCESS != Direct3DFillPresentationParameters(p_vout) )
605 // release all D3D objects
606 Direct3DVoutReleaseScene( p_vout );
607 Direct3DVoutReleasePictures( p_vout );
609 hr = IDirect3DDevice9_Reset(p_d3ddev, &p_vout->p_sys->d3dpp);
613 if( (VLC_SUCCESS != Direct3DVoutCreatePictures(p_vout, 1))
614 || (VLC_SUCCESS != Direct3DVoutCreateScene(p_vout)) )
616 msg_Dbg(p_vout, "%s failed !", __FUNCTION__);
621 msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr);
627 static int Direct3DVoutCheckFormat( vout_thread_t *p_vout,
628 D3DFORMAT target, D3DFORMAT format )
630 LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;
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);
638 /* test whether device can perform color-conversion
639 ** from that format to target format
641 hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
648 if( D3DERR_NOTAVAILABLE != hr )
649 msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
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 },
681 static const d3d_format_t *Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
683 for( unsigned pass = 0; pass < 2; pass++ )
685 const vlc_fourcc_t *p_chromas;
687 if( pass == 0 && p_vout->p_sys->b_hw_yuv && vlc_fourcc_IsYUV( i_chroma ) )
688 p_chromas = vlc_fourcc_GetYUVFallback( i_chroma );
690 p_chromas = vlc_fourcc_GetRGBFallback( i_chroma );
694 for( unsigned i = 0; p_chromas[i] != 0; i++ )
696 for( unsigned j = 0; p_d3d_formats[j].name; j++ )
698 const d3d_format_t *p_format = &p_d3d_formats[j];
700 if( p_format->fourcc != p_chromas[i] )
703 msg_Warn( p_vout, "trying surface pixel format: %s",
705 if( !Direct3DVoutCheckFormat( p_vout, target, p_format->format ) )
707 msg_Dbg( p_vout, "selected surface pixel format is %s",
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 )
727 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
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;
734 I_OUTPUTPICTURES = 0;
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
741 const d3d_format_t *p_format = Direct3DVoutFindFormat(p_vout, i_chroma, p_vout->p_sys->d3dpp.BackBufferFormat);
744 msg_Err(p_vout, "surface pixel format is not supported.");
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;
753 for( c=0; c<i_num_pics; )
756 LPDIRECT3DSURFACE9 p_d3dsurf;
757 picture_t *p_pic = p_vout->p_picture+c;
759 hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev,
760 p_vout->render.i_width,
761 p_vout->render.i_height,
768 msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr);
769 Direct3DVoutReleasePictures(p_vout);
773 /* fill surface with black color */
774 IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
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 );
779 /* assign surface to internal structure */
780 p_pic->p_sys = (void *)p_d3dsurf;
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 )
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;
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;
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;
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;
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;
832 for( int n = 0; n < p_pic->i_planes; n++ )
834 const unsigned d = 1 + (n > 0);
835 plane_t *p = &p_pic->p[n];
837 p->i_pixel_pitch = 1;
839 p->i_visible_lines = p_vout->output.i_height / d;
840 p->i_visible_pitch = p_vout->output.i_width / d;
844 Direct3DVoutReleasePictures(p_vout);
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;
854 I_OUTPUTPICTURES = ++c;
857 msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c );
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)
869 size_t i_num_pics = I_OUTPUTPICTURES;
871 for( c=0; c<i_num_pics; ++c )
873 picture_t *p_pic = p_vout->p_picture+c;
876 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
882 IDirect3DSurface9_Release(p_d3dsurf);
886 msg_Dbg( p_vout, "%u Direct3D pictures released.", c);
888 I_OUTPUTPICTURES = 0;
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 )
900 D3DLOCKED_RECT d3drect;
901 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
903 if( NULL == p_d3dsurf )
906 /* Lock the surface to get a valid pointer to the picture buffer */
907 hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, 0);
910 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
914 /* fill in buffer info in first plane */
915 p_pic->p->p_pixels = d3drect.pBits;
916 p_pic->p->i_pitch = d3drect.Pitch;
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 )
923 for( int n = 1; n < p_pic->i_planes; n++ )
925 const plane_t *o = &p_pic->p[n-1];
926 plane_t *p = &p_pic->p[n];
928 p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
929 p->i_pitch = d3drect.Pitch / 2;
931 /* The d3d buffer is always allocated as YV12 */
932 if( vlc_fourcc_AreUVPlanesSwapped( p_pic->format.i_chroma, VLC_CODEC_YV12 ) )
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;
943 /*****************************************************************************
944 * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface().
945 *****************************************************************************/
946 static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic )
949 LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys;
951 if( NULL == p_d3dsurf )
954 /* Unlock the Surface */
955 hr = IDirect3DSurface9_UnlockRect(p_d3dsurf);
958 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
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 *****************************************************************************/
970 static int Direct3DVoutCreateScene( vout_thread_t *p_vout )
972 LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev;
973 LPDIRECT3DTEXTURE9 p_d3dtex;
974 LPDIRECT3DVERTEXBUFFER9 p_d3dvtc;
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
983 hr = IDirect3DDevice9_CreateTexture(p_d3ddev,
984 p_vout->p_sys->d3dpp.BackBufferWidth,
985 p_vout->p_sys->d3dpp.BackBufferHeight,
987 D3DUSAGE_RENDERTARGET,
988 p_vout->p_sys->d3dpp.BackBufferFormat,
994 msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr);
999 ** Create a vertex buffer for use when rendering scene
1001 hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev,
1002 sizeof(CUSTOMVERTEX)*4,
1003 D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
1004 D3DFVF_CUSTOMVERTEX,
1010 msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr);
1011 IDirect3DTexture9_Release(p_d3dtex);
1012 return VLC_EGENERIC;
1015 p_vout->p_sys->p_d3dtex = p_d3dtex;
1016 p_vout->p_sys->p_d3dvtc = p_d3dvtc;
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);
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);
1027 // set maximum ambient light
1028 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1031 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE);
1033 // Turn off the zbuffer
1034 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE);
1037 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE);
1040 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE);
1043 IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE);
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);
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);
1058 // turn off alpha operation
1059 IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
1061 msg_Dbg( p_vout, "Direct3D scene created successfully");
1066 /*****************************************************************************
1067 * Direct3DVoutReleaseScene
1068 *****************************************************************************/
1069 static void Direct3DVoutReleaseScene( vout_thread_t *p_vout )
1071 LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex;
1072 LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
1076 IDirect3DVertexBuffer9_Release(p_d3dvtc);
1077 p_vout->p_sys->p_d3dvtc = NULL;
1082 IDirect3DTexture9_Release(p_d3dtex);
1083 p_vout->p_sys->p_d3dtex = NULL;
1085 msg_Dbg( p_vout, "Direct3D scene released successfully");
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 )
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;
1102 float f_width, f_height;
1104 // check if device is still available
1105 hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev);
1108 if( (D3DERR_DEVICENOTRESET != hr)
1109 || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout)) )
1111 // device is not usable at present (lost device, out of video mem ?)
1115 p_d3dtex = p_vout->p_sys->p_d3dtex;
1116 p_d3dvtc = p_vout->p_sys->p_d3dvtc;
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 );
1123 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1126 /* retrieve picture surface */
1127 p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys;
1128 if( NULL == p_d3dsrc )
1130 msg_Dbg( p_vout, "no surface to render ?");
1134 /* retrieve texture top-level surface */
1135 hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest);
1138 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
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;
1147 hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, &src, p_d3ddest, &dst, D3DTEXF_LINEAR);
1148 IDirect3DSurface9_Release(p_d3ddest);
1151 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1155 /* Update the vertex buffer */
1156 hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (&p_vertices), D3DLOCK_DISCARD);
1159 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1163 /* Setup vertices */
1164 f_width = (float)p_vout->p_sys->d3dpp.BackBufferWidth;
1165 f_height = (float)p_vout->p_sys->d3dpp.BackBufferHeight;
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;
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;
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;
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;
1201 hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc);
1204 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1209 hr = IDirect3DDevice9_BeginScene(p_d3ddev);
1212 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
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);
1223 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1224 IDirect3DDevice9_EndScene(p_d3ddev);
1228 // Render the vertex buffer contents
1229 hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX));
1232 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1233 IDirect3DDevice9_EndScene(p_d3ddev);
1237 // we use FVF instead of vertex shader
1238 hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX);
1241 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1242 IDirect3DDevice9_EndScene(p_d3ddev);
1247 hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2);
1250 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
1251 IDirect3DDevice9_EndScene(p_d3ddev);
1256 hr = IDirect3DDevice9_EndScene(p_d3ddev);
1259 msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr);
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,
1271 VLC_UNUSED( psz_cmd );
1272 VLC_UNUSED( oldval );
1273 VLC_UNUSED( p_data );
1275 vout_thread_t *p_vout = (vout_thread_t *)p_this;
1277 if( (newval.b_bool && !p_vout->p_sys->b_desktop) ||
1278 (!newval.b_bool && p_vout->p_sys->b_desktop) )
1280 playlist_t *p_playlist = pl_Hold( p_vout );
1284 /* Modify playlist as well because the vout might have to be
1286 var_Create( p_playlist, "direct3d-desktop", VLC_VAR_BOOL );
1287 var_Set( p_playlist, "direct3d-desktop", newval );
1288 pl_Release( p_vout );
1291 p_vout->p_sys->i_changes |= DX_DESKTOP_CHANGE;