1
mirror of https://code.videolan.org/videolan/vlc synced 2024-07-21 07:24:15 +02:00
vlc/modules/video_output/directx/direct3d.c
2006-11-11 21:44:34 +00:00

1502 lines
55 KiB
C

/*****************************************************************************
* direct3d.c: Windows Direct3D video output module
*****************************************************************************
* Copyright (C) 2006 the VideoLAN team
*$Id$
*
* Authors: Damien Fouilleul <damienf@videolan.org>
*
* 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 <errno.h> /* ENOMEM */
#include <stdlib.h> /* free() */
#include <string.h> /* strerror() */
#include <vlc/vlc.h>
#include <vlc/intf.h>
#include <vlc/vout.h>
#include <windows.h>
#include <d3d9.h>
#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; c<count; ++c )
{
HRESULT hr;
D3DFORMAT format = formats[c];
hr = IDirect3D9_CheckDeviceFormatConversion(p_d3dobj,
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
format, target);
if( SUCCEEDED(hr) )
{
// found a compatible format
switch( format )
{
case D3DFMT_UYVY:
msg_Dbg( p_vout, "selected surface pixel format is UYVY");
break;
case D3DFMT_YUY2:
msg_Dbg( p_vout, "selected surface pixel format is YUY2");
break;
case D3DFMT_X8R8G8B8:
msg_Dbg( p_vout, "selected surface pixel format is X8R8G8B8");
break;
case D3DFMT_A8R8G8B8:
msg_Dbg( p_vout, "selected surface pixel format is A8R8G8B8");
break;
case D3DFMT_R8G8B8:
msg_Dbg( p_vout, "selected surface pixel format is R8G8B8");
break;
case D3DFMT_R5G6B5:
msg_Dbg( p_vout, "selected surface pixel format is R5G6B5");
break;
case D3DFMT_X1R5G5B5:
msg_Dbg( p_vout, "selected surface pixel format is X1R5G5B5");
break;
default:
msg_Dbg( p_vout, "selected surface pixel format is 0x%0X", format);
break;
}
return format;
}
else if( D3DERR_NOTAVAILABLE != hr )
{
msg_Err( p_vout, "Could not query adapter supported formats. (hr=0x%lX)", hr);
break;
}
}
return D3DFMT_UNKNOWN;
}
D3DFORMAT Direct3DVoutFindFormat(vout_thread_t *p_vout, int i_chroma, D3DFORMAT target)
{
switch( i_chroma )
{
case VLC_FOURCC('U','Y','V','Y'):
case VLC_FOURCC('U','Y','N','V'):
case VLC_FOURCC('Y','4','2','2'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_UYVY, D3DFMT_YUY2, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('I','4','2','0'):
case VLC_FOURCC('I','4','2','2'):
case VLC_FOURCC('Y','V','1','2'):
{
/* typically 3D textures don't support planar format
** fallback to packed version and use pixel
** shader or CPU for the conversion
*/
static const D3DFORMAT formats[] =
{ D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('Y','U','Y','2'):
case VLC_FOURCC('Y','U','N','V'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_YUY2, D3DFMT_UYVY, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R5G6B5, D3DFMT_X1R5G5B5 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('R', 'V', '1', '5'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_X1R5G5B5 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('R', 'V', '1', '6'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_R5G6B5 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('R', 'V', '2', '4'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_R8G8B8, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
case VLC_FOURCC('R', 'V', '3', '2'):
{
static const D3DFORMAT formats[] =
{ D3DFMT_A8R8G8B8, D3DFMT_X8R8G8B8 };
return Direct3DVoutSelectFormat(p_vout, target, formats, sizeof(formats)/sizeof(D3DFORMAT));
}
default:
;
}
return D3DFMT_UNKNOWN;
}
static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format)
{
switch( format )
{
case D3DFMT_YUY2:
p_vout->output.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; c<i_num_pics; )
{
LPDIRECT3DSURFACE9 p_d3dsurf;
picture_t *p_pic = p_vout->p_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; c<i_num_pics; ++c )
{
picture_t *p_pic = p_vout->p_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;
}
}