/***************************************************************************** * direct3d.c: Windows Direct3D video output module ***************************************************************************** * Copyright (C) 2006 the VideoLAN team *$Id$ * * Authors: Damien Fouilleul * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble: * * This plugin will use YUV overlay if supported, using overlay will result in * the best video quality (hardware filering when rescaling the picture) * and the fastest display as it requires less processing. * * If YUV overlay is not supported this plugin will use RGB offscreen video * surfaces that will be blitted onto the primary surface (display) to * effectively display the pictures. This fallback method also enables us to * display video in window mode. * *****************************************************************************/ #include /* ENOMEM */ #include /* free() */ #include /* strerror() */ #include #include #include #include #include #include "vout.h" /***************************************************************************** * Local prototypes. *****************************************************************************/ static int OpenVideo ( vlc_object_t * ); static void CloseVideo ( vlc_object_t * ); static int Init ( vout_thread_t * ); static void End ( vout_thread_t * ); static int Manage ( vout_thread_t * ); static void Display ( vout_thread_t *, picture_t * ); static int Direct3DVoutCreate ( vout_thread_t * ); static void Direct3DVoutRelease ( vout_thread_t * ); static int Direct3DVoutOpen ( vout_thread_t * ); static void Direct3DVoutClose ( vout_thread_t * ); static int Direct3DVoutResetDevice( vout_thread_t *, UINT , UINT ); static int Direct3DVoutCreatePictures ( vout_thread_t *, size_t ); static void Direct3DVoutReleasePictures ( vout_thread_t * ); static int Direct3DVoutLockSurface ( vout_thread_t *, picture_t * ); static int Direct3DVoutUnlockSurface( vout_thread_t *, picture_t * ); static void Direct3DVoutRenderDefault ( vout_thread_t *, picture_t * ); static int Direct3DVoutCreateScene ( vout_thread_t * ); static void Direct3DVoutReleaseScene ( vout_thread_t * ); static void Direct3DVoutRenderScene ( vout_thread_t *, picture_t * ); /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin(); set_shortname( "Direct3D" ); set_category( CAT_VIDEO ); set_subcategory( SUBCAT_VIDEO_VOUT ); set_description( _("DirectX 3D video output") ); set_capability( "video output", 150 ); add_shortcut( "direct3d" ); set_callbacks( OpenVideo, CloseVideo ); /* FIXME: Hack to avoid unregistering our window class */ linked_with_a_crap_library_which_uses_atexit( ); vlc_module_end(); #if 0 /* FIXME */ /* check if we registered a window class because we need to * unregister it */ WNDCLASS wndclass; if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) ) UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) ); #endif /***************************************************************************** * CUSTOMVERTEX: ***************************************************************************** *****************************************************************************/ typedef struct { FLOAT x,y,z; // vertex untransformed position FLOAT rhw; // eye distance D3DCOLOR diffuse; // diffuse color FLOAT tu, tv; // texture relative coordinates } CUSTOMVERTEX; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) /***************************************************************************** * OpenVideo: allocate DirectX video thread output method ***************************************************************************** * This function allocates and initialize the DirectX vout method. *****************************************************************************/ static int OpenVideo( vlc_object_t *p_this ) { vout_thread_t * p_vout = (vout_thread_t *)p_this; vlc_value_t val; /* Allocate structure */ p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); if( p_vout->p_sys == NULL ) { msg_Err( p_vout, "out of memory" ); return VLC_ENOMEM; } memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); if( VLC_SUCCESS != Direct3DVoutCreate( p_vout ) ) { msg_Err( p_vout, "Direct3D could not be initialized !"); goto error; } /* Initialisations */ p_vout->pf_init = Init; p_vout->pf_end = End; p_vout->pf_manage = Manage; p_vout->pf_render = Direct3DVoutRenderScene; p_vout->pf_display = Display; p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL; p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL; p_vout->p_sys->i_changes = 0; p_vout->p_sys->b_wallpaper = 0; vlc_mutex_init( p_vout, &p_vout->p_sys->lock ); SetRectEmpty( &p_vout->p_sys->rect_display ); SetRectEmpty( &p_vout->p_sys->rect_parent ); p_vout->p_sys->b_cursor_hidden = 0; p_vout->p_sys->i_lastmoved = mdate(); /* Set main window's size */ p_vout->p_sys->i_window_width = p_vout->i_window_width; p_vout->p_sys->i_window_height = p_vout->i_window_height; /* Create the DirectXEventThread, this thread is created by us to isolate * the Win32 PeekMessage function calls. We want to do this because * Windows can stay blocked inside this call for a long time, and when * this happens it thus blocks vlc's video_output thread. * DirectXEventThread will take care of the creation of the video * window (because PeekMessage has to be called from the same thread which * created the window). */ msg_Dbg( p_vout, "creating DirectXEventThread" ); p_vout->p_sys->p_event = vlc_object_create( p_vout, sizeof(event_thread_t) ); p_vout->p_sys->p_event->p_vout = p_vout; if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread", E_(DirectXEventThread), 0, 1 ) ) { msg_Err( p_vout, "cannot create DirectXEventThread" ); vlc_object_destroy( p_vout->p_sys->p_event ); p_vout->p_sys->p_event = NULL; goto error; } if( p_vout->p_sys->p_event->b_error ) { msg_Err( p_vout, "DirectXEventThread failed" ); goto error; } vlc_object_attach( p_vout->p_sys->p_event, p_vout ); msg_Dbg( p_vout, "DirectXEventThread running" ); /* Variable to indicate if the window should be on top of others */ /* Trigger a callback right now */ var_Get( p_vout, "video-on-top", &val ); var_Set( p_vout, "video-on-top", val ); /* disable screensaver by temporarily changing system settings */ p_vout->p_sys->i_spi_lowpowertimeout = 0; p_vout->p_sys->i_spi_powerofftimeout = 0; p_vout->p_sys->i_spi_screensavetimeout = 0; var_Get( p_vout, "disable-screensaver", &val); if( val.b_bool ) { msg_Dbg(p_vout, "disabling screen saver"); SystemParametersInfo(SPI_GETLOWPOWERTIMEOUT, 0, &(p_vout->p_sys->i_spi_lowpowertimeout), 0); if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, 0, NULL, 0); } SystemParametersInfo(SPI_GETPOWEROFFTIMEOUT, 0, &(p_vout->p_sys->i_spi_powerofftimeout), 0); if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, 0, NULL, 0); } SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &(p_vout->p_sys->i_spi_screensavetimeout), 0); if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, 0, NULL, 0); } } return VLC_SUCCESS; error: CloseVideo( VLC_OBJECT(p_vout) ); return VLC_EGENERIC; } /***************************************************************************** * CloseVideo: destroy Sys video thread output method ***************************************************************************** * Terminate an output method created by Create *****************************************************************************/ static void CloseVideo( vlc_object_t *p_this ) { vout_thread_t * p_vout = (vout_thread_t *)p_this; Direct3DVoutRelease( p_vout ); if( p_vout->p_sys->p_event ) { vlc_object_detach( p_vout->p_sys->p_event ); /* Kill DirectXEventThread */ p_vout->p_sys->p_event->b_die = VLC_TRUE; /* we need to be sure DirectXEventThread won't stay stuck in * GetMessage, so we send a fake message */ if( p_vout->p_sys->hwnd ) { PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0); } vlc_thread_join( p_vout->p_sys->p_event ); vlc_object_destroy( p_vout->p_sys->p_event ); } vlc_mutex_destroy( &p_vout->p_sys->lock ); /* restore screensaver system settings */ if( 0 != p_vout->p_sys->i_spi_lowpowertimeout ) { SystemParametersInfo(SPI_SETLOWPOWERTIMEOUT, p_vout->p_sys->i_spi_lowpowertimeout, NULL, 0); } if( 0 != p_vout->p_sys->i_spi_powerofftimeout ) { SystemParametersInfo(SPI_SETPOWEROFFTIMEOUT, p_vout->p_sys->i_spi_powerofftimeout, NULL, 0); } if( 0 != p_vout->p_sys->i_spi_screensavetimeout ) { SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, p_vout->p_sys->i_spi_screensavetimeout, NULL, 0); } if( p_vout->p_sys ) { free( p_vout->p_sys ); p_vout->p_sys = NULL; } } /***************************************************************************** * Init: initialize Direct3D video thread output method *****************************************************************************/ static int Init( vout_thread_t *p_vout ) { int i_ret; /* Initialise Direct3D */ if( VLC_SUCCESS != Direct3DVoutOpen( p_vout ) ) { msg_Err( p_vout, "cannot initialize Direct3D" ); return VLC_EGENERIC; } /* Initialize the output structure. * Since Direct3D can do rescaling for us, stick to the default * coordinates and aspect. */ p_vout->output.i_width = p_vout->render.i_width; p_vout->output.i_height = p_vout->render.i_height; p_vout->output.i_aspect = p_vout->render.i_aspect; p_vout->fmt_out = p_vout->fmt_in; E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); /* create picture pool */ i_ret = Direct3DVoutCreatePictures(p_vout, 1); if( VLC_SUCCESS != i_ret ) { msg_Err(p_vout, "Direct3D picture pool initialization failed !"); return i_ret; } /* create scene */ i_ret = Direct3DVoutCreateScene(p_vout); if( VLC_SUCCESS != i_ret ) { msg_Err(p_vout, "Direct3D scene initialization failed !"); Direct3DVoutReleasePictures(p_vout); return i_ret; } p_vout->fmt_out.i_chroma = p_vout->output.i_chroma; return VLC_SUCCESS; } /***************************************************************************** * End: terminate Sys video thread output method ***************************************************************************** * Terminate an output method created by Create. * It is called at the end of the thread. *****************************************************************************/ static void End( vout_thread_t *p_vout ) { Direct3DVoutReleaseScene(p_vout); Direct3DVoutReleasePictures(p_vout); Direct3DVoutClose( p_vout ); } /***************************************************************************** * Manage: handle Sys events ***************************************************************************** * This function should be called regularly by the video output thread. * It returns a non null value if an error occurred. *****************************************************************************/ static int Manage( vout_thread_t *p_vout ) { WINDOWPLACEMENT window_placement; /* If we do not control our window, we check for geometry changes * ourselves because the parent might not send us its events. */ vlc_mutex_lock( &p_vout->p_sys->lock ); if( p_vout->p_sys->hparent && !p_vout->b_fullscreen ) { RECT rect_parent; POINT point; vlc_mutex_unlock( &p_vout->p_sys->lock ); GetClientRect( p_vout->p_sys->hparent, &rect_parent ); point.x = point.y = 0; ClientToScreen( p_vout->p_sys->hparent, &point ); OffsetRect( &rect_parent, point.x, point.y ); if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) ) { p_vout->p_sys->rect_parent = rect_parent; SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, rect_parent.right - rect_parent.left, rect_parent.bottom - rect_parent.top, 0 ); } } else { vlc_mutex_unlock( &p_vout->p_sys->lock ); } /* * Position Change */ if( p_vout->p_sys->i_changes & DX_POSITION_CHANGE ) { #if 0 /* need that when bicubic filter is available */ RECT rect; UINT width, height; GetClientRect(p_vout->p_sys->hvideownd, &rect); width = rect.right-rect.left; height = rect.bottom-rect.top; if( (width != p_vout->p_sys->d3dpp.BackBufferWidth) || (height != p_vout->p_sys->d3dpp.BackBufferHeight) ) { msg_Dbg(p_vout, "resizing device back buffers to (%lux%lu)", width, height); // need to reset D3D device to resize back buffer if( VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, width, height) ) return VLC_EGENERIC; } #endif p_vout->p_sys->i_changes &= ~DX_POSITION_CHANGE; } /* Check for cropping / aspect changes */ if( p_vout->i_changes & VOUT_CROP_CHANGE || p_vout->i_changes & VOUT_ASPECT_CHANGE ) { p_vout->i_changes &= ~VOUT_CROP_CHANGE; p_vout->i_changes &= ~VOUT_ASPECT_CHANGE; p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset; p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset; p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width; p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height; p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect; p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num; p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den; p_vout->output.i_aspect = p_vout->fmt_in.i_aspect; E_(DirectXUpdateRects)( p_vout, VLC_TRUE ); } /* We used to call the Win32 PeekMessage function here to read the window * messages. But since window can stay blocked into this function for a * long time (for example when you move your window on the screen), I * decided to isolate PeekMessage in another thread. */ /* * Fullscreen change */ if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE ) { vlc_value_t val; HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ? p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd; p_vout->b_fullscreen = ! p_vout->b_fullscreen; /* We need to switch between Maximized and Normal sized window */ window_placement.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement( hwnd, &window_placement ); if( p_vout->b_fullscreen ) { /* Change window style, no borders and no title bar */ int i_style = WS_CLIPCHILDREN | WS_VISIBLE; SetWindowLong( hwnd, GWL_STYLE, i_style ); if( p_vout->p_sys->hparent ) { /* Retrieve current window position so fullscreen will happen * on the right screen */ POINT point = {0,0}; RECT rect; ClientToScreen( p_vout->p_sys->hwnd, &point ); GetClientRect( p_vout->p_sys->hwnd, &rect ); SetWindowPos( hwnd, 0, point.x, point.y, rect.right, rect.bottom, SWP_NOZORDER|SWP_FRAMECHANGED ); GetWindowPlacement( hwnd, &window_placement ); } /* Maximize window */ window_placement.showCmd = SW_SHOWMAXIMIZED; SetWindowPlacement( hwnd, &window_placement ); SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); if( p_vout->p_sys->hparent ) { RECT rect; GetClientRect( hwnd, &rect ); SetParent( p_vout->p_sys->hwnd, hwnd ); SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, rect.right, rect.bottom, SWP_NOZORDER|SWP_FRAMECHANGED ); } SetForegroundWindow( hwnd ); } else { /* Change window style, no borders and no title bar */ SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style ); /* Normal window */ window_placement.showCmd = SW_SHOWNORMAL; SetWindowPlacement( hwnd, &window_placement ); SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); if( p_vout->p_sys->hparent ) { RECT rect; GetClientRect( p_vout->p_sys->hparent, &rect ); SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent ); SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0, rect.right, rect.bottom, SWP_NOZORDER|SWP_FRAMECHANGED ); ShowWindow( hwnd, SW_HIDE ); SetForegroundWindow( p_vout->p_sys->hparent ); } /* Make sure the mouse cursor is displayed */ PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 ); } /* Update the object variable and trigger callback */ val.b_bool = p_vout->b_fullscreen; var_Set( p_vout, "fullscreen", val ); p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE; p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE; } /* * Pointer change */ if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden && (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 ) { POINT point; HWND hwnd; /* Hide the cursor only if it is inside our window */ GetCursorPos( &point ); hwnd = WindowFromPoint(point); if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd ) { PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 ); } else { p_vout->p_sys->i_lastmoved = mdate(); } } /* * "Always on top" status change */ if( p_vout->p_sys->b_on_top_change ) { vlc_value_t val; HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE ); var_Get( p_vout, "video-on-top", &val ); /* Set the window on top if necessary */ if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) ) { CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_CHECKED ); SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE ); } else /* The window shouldn't be on top */ if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST ) ) { CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP, MF_BYCOMMAND | MFS_UNCHECKED ); SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE ); } p_vout->p_sys->b_on_top_change = VLC_FALSE; } /* Check if the event thread is still running */ if( p_vout->p_sys->p_event->b_die ) { return VLC_EGENERIC; /* exit */ } return VLC_SUCCESS; } /***************************************************************************** * Display: displays previously rendered output ***************************************************************************** * This function sends the currently rendered image to the display, wait until * it is displayed and switch the two rendering buffers, preparing next frame. *****************************************************************************/ static void Display( vout_thread_t *p_vout, picture_t *p_pic ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; // Present the backbuffer contents to the display HRESULT hr = IDirect3DDevice9_Present(p_d3ddev, NULL, NULL, NULL, NULL); if( FAILED(hr) ) msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); } /***************************************************************************** * DirectD3DVoutCreate: Initialize and instance of Direct3D9 ***************************************************************************** * This function initialize Direct3D and analyze available resources from * default adapter. *****************************************************************************/ static int Direct3DVoutCreate( vout_thread_t *p_vout ) { HRESULT hr; LPDIRECT3D9 p_d3dobj; D3DCAPS9 d3dCaps; /* Create the D3D object. */ p_d3dobj = Direct3DCreate9( D3D_SDK_VERSION ); if( NULL == p_d3dobj ) { msg_Err( p_vout, "Could not create Direct3D9 instance."); return VLC_EGENERIC; } p_vout->p_sys->p_d3dobj = p_d3dobj; /* ** Get device capabilities */ ZeroMemory(&d3dCaps, sizeof(d3dCaps)); hr = IDirect3D9_GetDeviceCaps(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps); if( FAILED(hr) ) { msg_Err( p_vout, "Could not read adapter capabilities. (hr=0x%lX)", hr); return VLC_EGENERIC; } /* TODO: need to test device capabilities and select the right render function */ return VLC_SUCCESS; } /***************************************************************************** * DirectD3DVoutRelease: release an instance of Direct3D9 *****************************************************************************/ static void Direct3DVoutRelease( vout_thread_t *p_vout ) { if( p_vout->p_sys->p_d3dobj ) { IDirect3D9_Release(p_vout->p_sys->p_d3dobj); p_vout->p_sys->p_d3dobj = NULL; } } /***************************************************************************** * DirectD3DVoutOpen: Takes care of all the Direct3D9 initialisations ***************************************************************************** * This function creates Direct3D device * this must be called from the vout thread for performance reason, as * all Direct3D Device APIs are used in a non multithread safe environment *****************************************************************************/ static int Direct3DVoutOpen( vout_thread_t *p_vout ) { HRESULT hr = S_OK; LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj;; D3DDISPLAYMODE d3ddm; LPDIRECT3DDEVICE9 p_d3ddev; /* ** Get the current desktop display mode, so we can set up a back ** buffer of the same format */ hr = IDirect3D9_GetAdapterDisplayMode(p_d3dobj, D3DADAPTER_DEFAULT, &d3ddm ); if( FAILED(hr)) { msg_Err( p_vout, "Could not read adapter display mode. (hr=0x%lX)", hr); return VLC_EGENERIC; } /* Set up the structure used to create the D3DDevice. */ ZeroMemory( &p_vout->p_sys->d3dpp, sizeof(D3DPRESENT_PARAMETERS) ); p_vout->p_sys->d3dpp.Windowed = TRUE; p_vout->p_sys->d3dpp.hDeviceWindow = p_vout->p_sys->hvideownd; p_vout->p_sys->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; p_vout->p_sys->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; p_vout->p_sys->d3dpp.BackBufferFormat = d3ddm.Format; p_vout->p_sys->d3dpp.BackBufferCount = 1; p_vout->p_sys->d3dpp.EnableAutoDepthStencil = TRUE; p_vout->p_sys->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; // Create the D3DDevice hr = IDirect3D9_CreateDevice(p_d3dobj, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &(p_vout->p_sys->d3dpp), &p_d3ddev ); if( FAILED(hr) ) { msg_Err(p_vout, "Could not create the D3D device! (hr=0x%lX)", hr); return VLC_EGENERIC; } p_vout->p_sys->p_d3ddev = p_d3ddev; msg_Dbg( p_vout, "Direct3D device adapter successfully initialized" ); return VLC_SUCCESS; } /***************************************************************************** * DirectD3DClose: release the Direct3D9 device *****************************************************************************/ static void Direct3DVoutClose( vout_thread_t *p_vout ) { if( p_vout->p_sys->p_d3ddev ) { IDirect3DDevice9_Release(p_vout->p_sys->p_d3ddev); p_vout->p_sys->p_d3ddev = NULL; } p_vout->p_sys->hmonitor = NULL; } /***************************************************************************** * DirectD3DClose: reset the Direct3D9 device ***************************************************************************** * All resources must be deallocated before the reset occur, they will be * realllocated once the reset has been performed successfully *****************************************************************************/ static int Direct3DVoutResetDevice( vout_thread_t *p_vout, UINT i_width, UINT i_height ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; D3DPRESENT_PARAMETERS d3dpp; HRESULT hr; memcpy(&d3dpp, &(p_vout->p_sys->d3dpp), sizeof(d3dpp)); if( i_width ) d3dpp.BackBufferWidth = i_width; if( i_height ) d3dpp.BackBufferHeight = i_height; // release all D3D objects Direct3DVoutReleaseScene( p_vout ); Direct3DVoutReleasePictures( p_vout ); hr = IDirect3DDevice9_Reset(p_d3ddev, &d3dpp); if( SUCCEEDED(hr) ) { // re-create them if( (VLC_SUCCESS == Direct3DVoutCreatePictures(p_vout, 1)) && (VLC_SUCCESS == Direct3DVoutCreateScene(p_vout)) ) { p_vout->p_sys->d3dpp.BackBufferWidth = i_width; p_vout->p_sys->d3dpp.BackBufferHeight = i_height; return VLC_SUCCESS; } return VLC_EGENERIC; } else { msg_Err(p_vout, "%s failed ! (hr=%08lX)", __FUNCTION__, hr); return VLC_EGENERIC; } return VLC_SUCCESS; } static D3DFORMAT Direct3DVoutSelectFormat( vout_thread_t *p_vout, D3DFORMAT target, const D3DFORMAT *formats, size_t count) { LPDIRECT3D9 p_d3dobj = p_vout->p_sys->p_d3dobj; size_t c; for( c=0; coutput.i_chroma = VLC_FOURCC('Y', 'U', 'Y', '2'); break; case D3DFMT_UYVY: p_vout->output.i_chroma = VLC_FOURCC('U', 'Y', 'V', 'Y'); break; case D3DFMT_X8R8G8B8: case D3DFMT_A8R8G8B8: /* ** FIXME: some custom masks are not handled properly in rgb_yuv converter, ** ARGB do NOT work ! */ p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '3', '2'); p_vout->output.i_rmask = 0x000000ff; p_vout->output.i_gmask = 0x0000ff00; p_vout->output.i_bmask = 0x00ff0000; p_vout->output.i_lrshift = 8; p_vout->output.i_lgshift = 16; p_vout->output.i_lbshift = 24; break; case D3DFMT_R5G6B5: p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '6'); # if defined( WORDS_BIGENDIAN ) p_vout->output.i_rmask = (0x1fL)<<11; p_vout->output.i_gmask = (0x3fL)<<5; p_vout->output.i_bmask = (0x1fL)<<0; //p_vout->output.i_rshift = 11; //p_vout->output.i_gshift = 5; //p_vout->output.i_bshift = 0; # else /* ** FIXME: in little endian mode, following masking is not byte aligned, ** therefore green bits will not be sequentially merged ! */ p_vout->output.i_rmask = (0x1fL)<<0; p_vout->output.i_gmask = (0x3fL)<<5; p_vout->output.i_bmask = (0x1fL)<<11; //p_vout->output.i_rshift = 0; //p_vout->output.i_gshift = 5; //p_vout->output.i_bshift = 11; # endif break; case D3DFMT_X1R5G5B5: p_vout->output.i_chroma = VLC_FOURCC('R', 'V', '1', '5'); # if defined( WORDS_BIGENDIAN ) p_vout->output.i_rmask = (0x1fL)<<10; p_vout->output.i_gmask = (0x1fL)<<5; p_vout->output.i_bmask = (0x1fL)<<0; //p_vout->output.i_rshift = 10; //p_vout->output.i_gshift = 5; //p_vout->output.i_bshift = 0; # else /* ** FIXME: in little endian mode, following masking is not byte aligned, ** therefore green bits will not be sequentially merged ! */ p_vout->output.i_rmask = (0x1fL)<<1; p_vout->output.i_gmask = (0x1fL)<<6; p_vout->output.i_bmask = (0x1fL)<<11; //p_vout->output.i_rshift = 1; //p_vout->output.i_gshift = 5; //p_vout->output.i_bshift = 11; # endif break; default: return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * Direct3DVoutCreatePictures: allocate a vector of identical pictures ***************************************************************************** * Each picture has an associated offscreen surface in video memory * depending on hardware capabilities the picture chroma will be as close * as possible to the orginal render chroma to reduce CPU conversion overhead * and delegate this work to video card GPU *****************************************************************************/ static int Direct3DVoutCreatePictures( vout_thread_t *p_vout, size_t i_num_pics ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; D3DFORMAT format = p_vout->p_sys->d3dpp.BackBufferFormat; HRESULT hr; size_t c; I_OUTPUTPICTURES = 0; /* ** find the appropriate D3DFORMAT for the render chroma, the format will be the closest to ** the requested chroma which is usable by the hardware in an offscreen surface, as they ** typically support more formats than textures */ format = Direct3DVoutFindFormat(p_vout, p_vout->render.i_chroma, format); if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) ) { msg_Err(p_vout, "surface pixel format is not supported."); return VLC_EGENERIC; } for( c=0; cp_picture+c; hr = IDirect3DDevice9_CreateOffscreenPlainSurface(p_d3ddev, p_vout->render.i_width, p_vout->render.i_height, format, D3DPOOL_DEFAULT, &p_d3dsurf, NULL); if( FAILED(hr) ) { msg_Err(p_vout, "Failed to create picture surface. (hr=0x%lx)", hr); Direct3DVoutReleasePictures(p_vout); return VLC_EGENERIC; } /* fill surface with default color */ IDirect3DDevice9_ColorFill(p_d3ddev, p_d3dsurf, NULL, D3DCOLOR_ARGB(0xFF, 0, 0, 0) ); /* assign surface to internal structure */ p_pic->p_sys = (void *)p_d3dsurf; /* Now that we've got our direct-buffer, we can finish filling in the * picture_t structures */ switch( p_vout->output.i_chroma ) { case VLC_FOURCC('R','G','B','2'): p_pic->p->i_lines = p_vout->output.i_height; p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pixel_pitch = 1; p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; p_pic->i_planes = 1; break; case VLC_FOURCC('R','V','1','5'): case VLC_FOURCC('R','V','1','6'): p_pic->p->i_lines = p_vout->output.i_height; p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pixel_pitch = 2; p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; p_pic->i_planes = 1; break; case VLC_FOURCC('R','V','2','4'): p_pic->p->i_lines = p_vout->output.i_height; p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pixel_pitch = 3; p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; p_pic->i_planes = 1; break; case VLC_FOURCC('R','V','3','2'): p_pic->p->i_lines = p_vout->output.i_height; p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pixel_pitch = 4; p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; p_pic->i_planes = 1; break; case VLC_FOURCC('U','Y','V','Y'): case VLC_FOURCC('Y','U','Y','2'): p_pic->p->i_lines = p_vout->output.i_height; p_pic->p->i_visible_lines = p_vout->output.i_height; p_pic->p->i_pixel_pitch = 2; p_pic->p->i_visible_pitch = p_vout->output.i_width * p_pic->p->i_pixel_pitch; p_pic->i_planes = 1; break; default: Direct3DVoutReleasePictures(p_vout); return VLC_EGENERIC; } p_pic->i_status = DESTROYED_PICTURE; p_pic->i_type = DIRECT_PICTURE; p_pic->b_slow = VLC_TRUE; p_pic->pf_lock = Direct3DVoutLockSurface; p_pic->pf_unlock = Direct3DVoutUnlockSurface; PP_OUTPUTPICTURE[c] = p_pic; I_OUTPUTPICTURES = ++c; } msg_Dbg( p_vout, "%u Direct3D pictures created successfully", c ); return VLC_SUCCESS; } /***************************************************************************** * Direct3DVoutReleasePictures: destroy a picture vector ***************************************************************************** * release all video resources used for pictures *****************************************************************************/ static void Direct3DVoutReleasePictures( vout_thread_t *p_vout) { size_t i_num_pics = I_OUTPUTPICTURES; size_t c; for( c=0; cp_picture+c; if( p_pic->p_sys ) { LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; p_pic->p_sys = NULL; if( p_d3dsurf ) { IDirect3DSurface9_Release(p_d3dsurf); } } } msg_Dbg( p_vout, "%u Direct3D pictures released.", c); I_OUTPUTPICTURES = 0; } /***************************************************************************** * Direct3DVoutLockSurface: Lock surface and get picture data pointer ***************************************************************************** * This function locks a surface and get the surface descriptor which amongst * other things has the pointer to the picture data. *****************************************************************************/ static int Direct3DVoutLockSurface( vout_thread_t *p_vout, picture_t *p_pic ) { HRESULT hr; D3DLOCKED_RECT d3drect; LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; if( NULL == p_d3dsurf ) return VLC_EGENERIC; /* Lock the surface to get a valid pointer to the picture buffer */ hr = IDirect3DSurface9_LockRect(p_d3dsurf, &d3drect, NULL, D3DLOCK_DISCARD); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return VLC_EGENERIC; } /* fill in buffer info in first plane */ p_pic->p->p_pixels = d3drect.pBits; p_pic->p->i_pitch = d3drect.Pitch; return VLC_SUCCESS; } /***************************************************************************** * Direct3DVoutUnlockSurface: Unlock a surface locked by Direct3DLockSurface(). *****************************************************************************/ static int Direct3DVoutUnlockSurface( vout_thread_t *p_vout, picture_t *p_pic ) { HRESULT hr; LPDIRECT3DSURFACE9 p_d3dsurf = (LPDIRECT3DSURFACE9)p_pic->p_sys; if( NULL == p_d3dsurf ) return VLC_EGENERIC; /* Unlock the Surface */ hr = IDirect3DSurface9_UnlockRect(p_d3dsurf); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return VLC_EGENERIC; } return VLC_SUCCESS; } /***************************************************************************** * Direct3DVoutCreateScene: allocate and initialize a 3D scene ***************************************************************************** * for advanced blending/filtering a texture needs be used in a 3D scene. *****************************************************************************/ static int Direct3DVoutCreateScene( vout_thread_t *p_vout ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; LPDIRECT3DTEXTURE9 p_d3dtex; LPDIRECT3DVERTEXBUFFER9 p_d3dvtc; HRESULT hr; /* ** Create a texture for use when rendering a scene ** for performance reason, texture format is identical to backbuffer ** which would usually be a RGB format */ hr = IDirect3DDevice9_CreateTexture(p_d3ddev, p_vout->render.i_width, p_vout->render.i_height, 1, D3DUSAGE_RENDERTARGET, p_vout->p_sys->d3dpp.BackBufferFormat, D3DPOOL_DEFAULT, &p_d3dtex, NULL); if( FAILED(hr)) { msg_Err(p_vout, "Failed to create texture. (hr=0x%lx)", hr); return VLC_EGENERIC; } /* ** Create a vertex buffer for use when rendering scene */ hr = IDirect3DDevice9_CreateVertexBuffer(p_d3ddev, sizeof(CUSTOMVERTEX)*4, D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &p_d3dvtc, NULL); if( FAILED(hr) ) { msg_Err(p_vout, "Failed to create vertex buffer. (hr=0x%lx)", hr); IDirect3DTexture9_Release(p_d3dtex); return VLC_EGENERIC; } p_vout->p_sys->p_d3dtex = p_d3dtex; p_vout->p_sys->p_d3dvtc = p_d3dvtc; // Texture coordinates outside the range [0.0, 1.0] are set // to the texture color at 0.0 or 1.0, respectively. IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); // Set linear filtering quality IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); IDirect3DDevice9_SetSamplerState(p_d3ddev, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); // set maximum ambient light IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255)); // Turn off culling IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_CULLMODE, D3DCULL_NONE); // Turn off the zbuffer IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ZENABLE, D3DZB_FALSE); // Turn off lights IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_LIGHTING, FALSE); // Enable dithering IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DITHERENABLE, TRUE); // disable stencil IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_STENCILENABLE, FALSE); // manage blending IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHABLENDENABLE, TRUE); IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHATESTENABLE,TRUE); IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAREF, 0x10); IDirect3DDevice9_SetRenderState(p_d3ddev, D3DRS_ALPHAFUNC,D3DCMP_GREATER); // Set texture states IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLOROP,D3DTOP_MODULATE); IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE); IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE); // turn off alpha operation IDirect3DDevice9_SetTextureStageState(p_d3ddev, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE); msg_Dbg( p_vout, "Direct3D scene created successfully"); return VLC_SUCCESS; } /***************************************************************************** * Direct3DVoutReleaseScene *****************************************************************************/ static void Direct3DVoutReleaseScene( vout_thread_t *p_vout ) { LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex; LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc; if( p_d3dvtc ) { IDirect3DVertexBuffer9_Release(p_d3dvtc); p_vout->p_sys->p_d3dvtc = NULL; } if( p_d3dtex ) { IDirect3DTexture9_Release(p_d3dtex); p_vout->p_sys->p_d3dtex = NULL; } msg_Dbg( p_vout, "Direct3D scene released successfully"); } /***************************************************************************** * Direct3DVoutRenderDefault: copy picture surface to display back buffer ***************************************************************************** * This function is intented for lower end video cards, without pixel shader * support or low video RAM *****************************************************************************/ static void Direct3DVoutRenderDefault( vout_thread_t *p_vout, picture_t *p_pic ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; LPDIRECT3DSURFACE9 p_d3dsrc, p_d3ddest; UINT iSwapChain, iSwapChains; HRESULT hr; // check if device is still available hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev); if( FAILED(hr) ) { if( (D3DERR_DEVICENOTRESET != hr) || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, 0, 0)) ) { // device is not usable at present (lost device, out of video mem ?) return; } } /* retrieve the number of swap chains */ iSwapChains = IDirect3DDevice9_GetNumberOfSwapChains(p_d3ddev); if( 0 == iSwapChains ) { msg_Dbg( p_vout, "no swap chain to render ?"); return; } /* retrieve picture surface */ p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys; if( NULL == p_d3dsrc ) { msg_Dbg( p_vout, "no surface to render ?"); return; } for( iSwapChain=0; iSwapChain < iSwapChains; ++iSwapChain ) { /* retrieve swap chain back buffer */ hr = IDirect3DDevice9_GetBackBuffer(p_d3ddev, iSwapChain, 0, D3DBACKBUFFER_TYPE_MONO, &p_d3ddest); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); continue; } /* Copy picture surface into texture surface, color space conversion happens here */ hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE); IDirect3DSurface9_Release(p_d3ddest); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } } } /***************************************************************************** * Render: copy picture surface into a texture and render into a scene ***************************************************************************** * This function is intented for higher end 3D cards, with pixel shader support * and at least 64 MB of video RAM. *****************************************************************************/ static void Direct3DVoutRenderScene( vout_thread_t *p_vout, picture_t *p_pic ) { LPDIRECT3DDEVICE9 p_d3ddev = p_vout->p_sys->p_d3ddev; LPDIRECT3DTEXTURE9 p_d3dtex = p_vout->p_sys->p_d3dtex; LPDIRECT3DVERTEXBUFFER9 p_d3dvtc = p_vout->p_sys->p_d3dvtc; LPDIRECT3DSURFACE9 p_d3dsrc, p_d3ddest; CUSTOMVERTEX *p_vertices; HRESULT hr; float f_width, f_height; // check if device is still available hr = IDirect3DDevice9_TestCooperativeLevel(p_d3ddev); if( FAILED(hr) ) { if( (D3DERR_DEVICENOTRESET != hr) || (VLC_SUCCESS != Direct3DVoutResetDevice(p_vout, 0, 0)) ) { // device is not usable at present (lost device, out of video mem ?) return; } } /* Clear the backbuffer and the zbuffer */ hr = IDirect3DDevice9_Clear( p_d3ddev, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 ); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } /* retrieve picture surface */ p_d3dsrc = (LPDIRECT3DSURFACE9)p_pic->p_sys; if( NULL == p_d3dsrc ) { msg_Dbg( p_vout, "no surface to render ?"); return; } /* retrieve texture top-level surface */ hr = IDirect3DTexture9_GetSurfaceLevel(p_d3dtex, 0, &p_d3ddest); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } /* Copy picture surface into texture surface, color space conversion happens here */ hr = IDirect3DDevice9_StretchRect(p_d3ddev, p_d3dsrc, NULL, p_d3ddest, NULL, D3DTEXF_NONE); IDirect3DSurface9_Release(p_d3ddest); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } /* Update the vertex buffer */ hr = IDirect3DVertexBuffer9_Lock(p_d3dvtc, 0, 0, (VOID **)(&p_vertices), D3DLOCK_DISCARD); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } /* Setup vertices */ f_width = (float)(p_vout->p_sys->d3dpp.BackBufferWidth); f_height = (float)(p_vout->p_sys->d3dpp.BackBufferHeight); p_vertices[0].x = 0.0f; // left p_vertices[0].y = 0.0f; // top p_vertices[0].z = 0.0f; p_vertices[0].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); p_vertices[0].rhw = 1.0f; p_vertices[0].tu = 0.0f; p_vertices[0].tv = 0.0f; p_vertices[1].x = f_width; // right p_vertices[1].y = 0.0f; // top p_vertices[1].z = 0.0f; p_vertices[1].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); p_vertices[1].rhw = 1.0f; p_vertices[1].tu = 1.0f; p_vertices[1].tv = 0.0f; p_vertices[2].x = f_width; // right p_vertices[2].y = f_height; // bottom p_vertices[2].z = 0.0f; p_vertices[2].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); p_vertices[2].rhw = 1.0f; p_vertices[2].tu = 1.0f; p_vertices[2].tv = 1.0f; p_vertices[3].x = 0.0f; // left p_vertices[3].y = f_height; // bottom p_vertices[3].z = 0.0f; p_vertices[3].diffuse = D3DCOLOR_ARGB(255, 255, 255, 255); p_vertices[3].rhw = 1.0f; p_vertices[3].tu = 0.0f; p_vertices[3].tv = 1.0f; hr= IDirect3DVertexBuffer9_Unlock(p_d3dvtc); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } // Begin the scene hr = IDirect3DDevice9_BeginScene(p_d3ddev); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } // Setup our texture. Using textures introduces the texture stage states, // which govern how textures get blended together (in the case of multiple // textures) and lighting information. In this case, we are modulating // (blending) our texture with the diffuse color of the vertices. hr = IDirect3DDevice9_SetTexture(p_d3ddev, 0, (LPDIRECT3DBASETEXTURE9)p_d3dtex); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); IDirect3DDevice9_EndScene(p_d3ddev); return; } // Render the vertex buffer contents hr = IDirect3DDevice9_SetStreamSource(p_d3ddev, 0, p_d3dvtc, 0, sizeof(CUSTOMVERTEX)); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); IDirect3DDevice9_EndScene(p_d3ddev); return; } // we use FVF instead of vertex shader hr = IDirect3DDevice9_SetVertexShader(p_d3ddev, NULL); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); IDirect3DDevice9_EndScene(p_d3ddev); return; } hr = IDirect3DDevice9_SetFVF(p_d3ddev, D3DFVF_CUSTOMVERTEX); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); IDirect3DDevice9_EndScene(p_d3ddev); return; } // draw rectangle hr = IDirect3DDevice9_DrawPrimitive(p_d3ddev, D3DPT_TRIANGLEFAN, 0, 2); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); IDirect3DDevice9_EndScene(p_d3ddev); return; } // End the scene hr = IDirect3DDevice9_EndScene(p_d3ddev); if( FAILED(hr) ) { msg_Dbg( p_vout, "%s:%d (hr=0x%0lX)", __FUNCTION__, __LINE__, hr); return; } }