2006-07-20 15:44:04 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* 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'):
|
2006-11-11 22:44:34 +01:00
|
|
|
case VLC_FOURCC('I','4','2','2'):
|
2006-07-20 15:44:04 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-09-02 16:32:19 +02:00
|
|
|
static int Direct3DVoutSetOutputFormat(vout_thread_t *p_vout, D3DFORMAT format)
|
2006-07-20 15:44:04 +02:00
|
|
|
{
|
|
|
|
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);
|
2006-09-02 16:32:19 +02:00
|
|
|
if( VLC_SUCCESS != Direct3DVoutSetOutputFormat(p_vout, format) )
|
2006-07-20 15:44:04 +02:00
|
|
|
{
|
|
|
|
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,
|
2006-10-05 00:55:57 +02:00
|
|
|
D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0 );
|
2006-07-20 15:44:04 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|