tge/engine/platformWin32/winD3DVideo.cc
2017-04-17 06:17:10 -06:00

730 lines
24 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platformWin32/platformGL.h"
#include "platformWin32/platformWin32.h"
#include "platform/platformAudio.h"
#include "platformWin32/winD3DVideo.h"
#include "console/console.h"
#include "math/mPoint.h"
#include "platform/event.h"
#include "platform/gameInterface.h"
#include "console/consoleInternal.h"
#include "console/ast.h"
#if defined(TORQUE_DEBUG)
# define OPENGL2D3D_DLL "opengl2d3d_DEBUG.dll"
# define GLU2D3D_DLL "glu2d3d_DEBUG.dll"
#else
# define OPENGL2D3D_DLL "opengl2d3d.dll"
# define GLU2D3D_DLL "glu2d3d.dll"
#endif
//------------------------------------------------------------------------------
bool D3DDevice::smCanSwitchBitDepth = true;
bool D3DDevice::smStay16 = false;
static void profileSystem(const char *vendor, const char *renderer)
{
Con::setBoolVariable("$pref::Video::safeModeOn", true);
Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", false);
Con::setBoolVariable("$pref::OpenGL::disableSubImage", false);
Con::setBoolVariable("$pref::OpenGL::noEnvColor", true);
Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", false);
Con::setBoolVariable("$pref::Interior::lockArrays", true);
Con::setBoolVariable("$pref::TS::skipFirstFog", false);
Con::setBoolVariable("$pref::OpenGL::disableEXTFogCoord", false);
Con::setVariable("$pref::Video::profiledVendor", vendor);
Con::setVariable("$pref::Video::profiledRenderer", renderer);
}
//------------------------------------------------------------------------------
D3DDevice::D3DDevice()
{
initDevice();
}
//------------------------------------------------------------------------------
void D3DDevice::initDevice()
{
// Set the device name:
mDeviceName = "D3D";
// Set some initial conditions:
mResolutionList.clear();
// Enumerate all available resolutions:
DEVMODE devMode;
U32 modeNum = 0;
U32 stillGoing = true;
while ( stillGoing )
{
dMemset( &devMode, 0, sizeof( devMode ) );
devMode.dmSize = sizeof( devMode );
stillGoing = EnumDisplaySettings( NULL, modeNum++, &devMode );
if ( devMode.dmPelsWidth >= 640 && devMode.dmPelsHeight >= 480
&& ( devMode.dmBitsPerPel == 16 || (devMode.dmBitsPerPel == 32 && !smStay16) ) &&
( smCanSwitchBitDepth || devMode.dmBitsPerPel == winState.desktopBitsPixel ) )
{
// Only add this resolution if it is not already in the list:
bool alreadyInList = false;
for ( U32 i = 0; i < mResolutionList.size(); i++ )
{
if ( devMode.dmPelsWidth == mResolutionList[i].w
&& devMode.dmPelsHeight == mResolutionList[i].h
&& devMode.dmBitsPerPel == mResolutionList[i].bpp )
{
alreadyInList = true;
break;
}
}
if ( !alreadyInList )
{
Resolution newRes( devMode.dmPelsWidth, devMode.dmPelsHeight, devMode.dmBitsPerPel );
mResolutionList.push_back( newRes );
}
}
}
}
//------------------------------------------------------------------------------
bool D3DDevice::activate( U32 width, U32 height, U32 bpp, bool fullScreen )
{
bool needResurrect = false;
// If the rendering context exists, delete it:
if ( winState.hGLRC )
{
Con::printf( "Killing the texture manager..." );
Game->textureKill();
needResurrect = true;
Con::printf( "Making the rendering context not current..." );
if ( !dwglMakeCurrent( NULL, NULL ) )
{
AssertFatal( false, "D3DDevice::activate\ndwglMakeCurrent( NULL, NULL ) failed!" );
return false;
}
Con::printf( "Deleting the rendering context ..." );
if ( !dwglDeleteContext( winState.hGLRC ) )
{
AssertFatal( false, "D3DDevice::activate\ndwglDeleteContext failed!" );
return false;
}
winState.hGLRC = NULL;
}
// If the window already exists, kill it so we can start fresh:
if ( winState.appWindow )
{
Con::printf( "Releasing the device context..." );
ReleaseDC( winState.appWindow, winState.appDC );
winState.appDC = NULL;
Con::printf( "Destroying the window..." );
DestroyWindow( winState.appWindow );
winState.appWindow = NULL;
}
// If OpenGL library already loaded, shut it down and reload it:
if ( winState.hinstOpenGL )
GL_Shutdown();
GL_Init(OPENGL2D3D_DLL,GLU2D3D_DLL);
// Set the resolution:
if ( !setScreenMode( width, height, bpp, ( fullScreen || mFullScreenOnly ), true, false ) )
return false;
// Get original gamma ramp
mRestoreGamma = GetDeviceGammaRamp(winState.appDC, mOriginalRamp);
// Output some driver info to the console:
const char* vendorString = (const char*) glGetString( GL_VENDOR );
const char* rendererString = (const char*) glGetString( GL_RENDERER );
const char* versionString = (const char*) glGetString( GL_VERSION );
Con::printf( "OpenGL driver information:" );
if ( vendorString )
Con::printf( " Vendor: %s", vendorString );
if ( rendererString )
Con::printf( " Renderer: %s", rendererString );
if ( versionString )
Con::printf( " Version: %s", versionString );
if ( needResurrect )
{
// Reload the textures:
Con::printf( "Resurrecting the texture manager..." );
Game->textureResurrect();
}
GL_EXT_Init();
Con::setVariable( "$pref::Video::displayDevice", mDeviceName );
Con::setBoolVariable( "$SwapIntervalSupported", false );
Con::setBoolVariable( "$pref::OpenGL::allowTexGen", false );
Con::setBoolVariable( "$pref::environmentMaps", false );
// only do this for the first session
if (!Con::getBoolVariable("$DisableSystemProfiling") && (dStrcmp(vendorString, Con::getVariable("$pref::Video::profiledVendor")) ||
dStrcmp(rendererString, Con::getVariable("$pref::Video::profiledRenderer"))))
profileSystem(vendorString, rendererString);
return true;
}
//------------------------------------------------------------------------------
void D3DDevice::shutdown()
{
if ( winState.hGLRC )
{
if (mRestoreGamma)
SetDeviceGammaRamp(winState.appDC, mOriginalRamp);
dwglMakeCurrent( NULL, NULL );
dwglDeleteContext( winState.hGLRC );
winState.hGLRC = NULL;
}
if ( winState.appDC )
{
ReleaseDC( winState.appWindow, winState.appDC );
winState.appDC = NULL;
}
if ( smIsFullScreen || (smStay16 && winState.desktopBitsPixel != 16))
ChangeDisplaySettings( NULL, 0 );
GL_Shutdown();
}
//------------------------------------------------------------------------------
// This is the real workhorse function of the DisplayDevice...
//
bool D3DDevice::setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt, bool repaint )
{
HWND curtain = NULL;
char errorMessage[256];
Resolution newRes( width, height, bpp );
bool newFullScreen = fullScreen;
bool safeModeOn = Con::getBoolVariable( "$pref::Video::safeModeOn" );
if ( !newFullScreen && mFullScreenOnly )
{
Con::warnf( ConsoleLogEntry::General, "D3DDevice::setScreenMode - device or desktop color depth does not allow windowed mode!" );
newFullScreen = true;
}
if ( !newFullScreen && ( newRes.w >= winState.desktopWidth || newRes.h >= winState.desktopHeight ) )
{
Con::warnf( ConsoleLogEntry::General, "D3DDevice::setScreenMode -- can't switch to resolution larger than desktop in windowed mode!" );
// Find the largest standard resolution that will fit on the desktop:
U32 resIndex;
for ( resIndex = mResolutionList.size() - 1; resIndex > 0; resIndex-- )
{
if ( mResolutionList[resIndex].w < winState.desktopWidth
&& mResolutionList[resIndex].h < winState.desktopHeight )
break;
}
newRes = mResolutionList[resIndex];
}
if ( newRes.w < 640 || newRes.h < 480 )
{
Con::warnf( ConsoleLogEntry::General, "D3DDevice::setScreenMode -- can't go smaller than 640x480!" );
return false;
}
if ( newFullScreen )
{
if (newRes.bpp != 16 && mFullScreenOnly)
newRes.bpp = 16;
// Match the new resolution to one in the list:
U32 resIndex = 0;
U32 bestScore = 0, thisScore = 0;
for ( int i = 0; i < mResolutionList.size(); i++ )
{
if ( newRes == mResolutionList[i] )
{
resIndex = i;
break;
}
else
{
thisScore = abs( S32( newRes.w ) - S32( mResolutionList[i].w ) )
+ abs( S32( newRes.h ) - S32( mResolutionList[i].h ) )
+ ( newRes.bpp == mResolutionList[i].bpp ? 0 : 1 );
if ( !bestScore || ( thisScore < bestScore ) )
{
bestScore = thisScore;
resIndex = i;
}
}
}
newRes = mResolutionList[resIndex];
}
else
// Basically ignore the bit depth parameter:
if (!smStay16)
newRes.bpp = winState.desktopBitsPixel;
else
newRes.bpp = 16;
// Return if already at this resolution:
if ( !forceIt && newRes == smCurrentRes && newFullScreen == smIsFullScreen )
return true;
Con::printf( "Setting screen mode to %dx%dx%d (%s)...", newRes.w, newRes.h, newRes.bpp, ( newFullScreen ? "fs" : "w" ) );
bool needResurrect = false;
// oh just always do it
//if ( ( newRes.bpp != smCurrentRes.bpp ) || ( safeModeOn && ( ( smIsFullScreen != newFullScreen ) || newFullScreen ) ) )
if (true)
{
// Delete the rendering context:
if ( winState.hGLRC )
{
if (!Video::smNeedResurrect)
{
Con::printf( "Killing the texture manager..." );
Game->textureKill();
needResurrect = true;
}
Con::printf( "Making the rendering context not current..." );
if ( !dwglMakeCurrent( NULL, NULL ) )
{
AssertFatal( false, "D3DDevice::setScreenMode\ndwglMakeCurrent( NULL, NULL ) failed!" );
return false;
}
Con::printf( "Deleting the rendering context..." );
if ( !dwglDeleteContext( winState.hGLRC ) )
{
AssertFatal( false, "D3DDevice::setScreenMode\ndwglDeleteContext failed!" );
return false;
}
winState.hGLRC = NULL;
}
// Release the device context:
if ( winState.appDC )
{
Con::printf( "Releasing the device context..." );
ReleaseDC( winState.appWindow, winState.appDC );
winState.appDC = NULL;
}
// Destroy the window:
if ( winState.appWindow )
{
Con::printf( "Destroying the window..." );
DestroyWindow( winState.appWindow );
winState.appWindow = NULL;
}
}
else if ( smIsFullScreen != newFullScreen )
{
// Change the window style:
Con::printf( "Changing the window style..." );
S32 windowStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
if ( newFullScreen )
windowStyle |= ( WS_MAXIMIZE | WS_VISIBLE);
else
windowStyle |= ( WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX );
if ( !SetWindowLong( winState.appWindow, GWL_STYLE, windowStyle ) )
Platform::AlertOK( "Error", "Failed to change the window style!" );
}
if( winState.appWindow && !newFullScreen )
ShowWindow( winState.appWindow, SW_NORMAL );
else
ShowWindow( winState.appWindow, SW_MAXIMIZE );
U32 test;
if ( newFullScreen )
{
// Change the display settings:
DEVMODE devMode;
dMemset( &devMode, 0, sizeof( devMode ) );
devMode.dmSize = sizeof( devMode );
devMode.dmPelsWidth = newRes.w;
devMode.dmPelsHeight = newRes.h;
devMode.dmBitsPerPel = newRes.bpp;
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
Con::printf( "Changing the display settings to %dx%dx%d...", newRes.w, newRes.h, newRes.bpp );
curtain = CreateCurtain( newRes.w, newRes.h );
test = ChangeDisplaySettings( &devMode, CDS_FULLSCREEN );
if ( test != DISP_CHANGE_SUCCESSFUL )
{
smIsFullScreen = false;
Con::setBoolVariable( "$pref::Video::fullScreen", false );
ChangeDisplaySettings( NULL, 0 );
Con::errorf( ConsoleLogEntry::General, "D3DDevice::setScreenMode - ChangeDisplaySettings failed." );
switch( test )
{
case DISP_CHANGE_RESTART:
Platform::AlertOK( "Display Change Failed", "You must restart your machine to get the specified mode." );
break;
case DISP_CHANGE_BADMODE:
Platform::AlertOK( "Display Change Failed", "The specified mode is not supported by this device." );
break;
default:
Platform::AlertOK( "Display Change Failed", "Hardware failed to switch to the specified mode." );
break;
};
DestroyWindow( curtain );
return false;
}
else
smIsFullScreen = true;
}
else if ( smIsFullScreen || (smStay16 && winState.desktopBitsPixel != 16))
{
if (!smStay16 || winState.desktopBitsPixel == 16)
{
Con::printf( "Changing to the desktop display settings (%dx%dx%d)...", winState.desktopWidth, winState.desktopHeight, winState.desktopBitsPixel );
ChangeDisplaySettings( NULL, 0 );
}
else
{
// Change the display settings:
DEVMODE devMode;
dMemset( &devMode, 0, sizeof( devMode ) );
devMode.dmSize = sizeof( devMode );
devMode.dmBitsPerPel = newRes.bpp;
devMode.dmFields = DM_BITSPERPEL;
Con::printf( "Changing the display settings to %dx%dx%d...", winState.desktopWidth, winState.desktopHeight, newRes.bpp );
test = ChangeDisplaySettings( &devMode, 0 );
if ( test != DISP_CHANGE_SUCCESSFUL )
{
smIsFullScreen = true;
Con::setBoolVariable( "$pref::Video::fullScreen", true );
ChangeDisplaySettings( NULL, 0 );
Con::errorf( ConsoleLogEntry::General, "D3DDevice::setScreenMode - ChangeDisplaySettings failed." );
switch ( test )
{
case DISP_CHANGE_RESTART:
Platform::AlertOK( "Display Change Failed", "You must restart your machine to get the specified mode." );
break;
case DISP_CHANGE_BADMODE:
Platform::AlertOK( "Display Change Failed", "The specified mode is not supported by this device." );
break;
default:
Platform::AlertOK( "Display Change Failed", "Hardware failed to switch to the specified mode." );
break;
};
return false;
}
}
smIsFullScreen = false;
}
Con::setBoolVariable( "$pref::Video::fullScreen", smIsFullScreen );
bool newWindow = false;
if ( !winState.appWindow )
{
Con::printf( "Creating a new %swindow...", ( fullScreen ? "full-screen " : "" ) );
winState.appWindow = CreateOpenGLWindow( newRes.w, newRes.h, newFullScreen );
if ( !winState.appWindow )
{
AssertFatal( false, "D3DDevice::setScreenMode\nFailed to create a new window!" );
return false;
}
newWindow = true;
}
// Move the window:
if ( !newFullScreen )
{
// Adjust the window rect to compensate for the window style:
RECT windowRect;
windowRect.left = windowRect.top = 0;
windowRect.right = newRes.w;
windowRect.bottom = newRes.h;
AdjustWindowRect( &windowRect, GetWindowLong( winState.appWindow, GWL_STYLE ), false );
U32 adjWidth = windowRect.right - windowRect.left;
U32 adjHeight = windowRect.bottom - windowRect.top;
// Center the window on the desktop:
U32 xPos = ( winState.desktopWidth - adjWidth ) / 2;
U32 yPos = ( winState.desktopHeight - adjHeight ) / 2;
test = SetWindowPos( winState.appWindow, 0, xPos, yPos, adjWidth, adjHeight, SWP_NOZORDER );
if ( !test )
{
dSprintf( errorMessage, 255, "D3DDevice::setScreenMode\nSetWindowPos failed trying to move the window to (%d,%d) and size it to %dx%d.", xPos, yPos, newRes.w, newRes.h );
AssertFatal( false, errorMessage );
return false;
}
}
else if ( !newWindow )
{
// Move and size the window to take up the whole screen:
if ( !SetWindowPos( winState.appWindow, 0, 0, 0, newRes.w, newRes.h, SWP_NOZORDER ) )
{
dSprintf( errorMessage, 255, "D3DDevice::setScreenMode\nSetWindowPos failed to move the window to (0,0) and size it to %dx%d.", newRes.w, newRes.h );
AssertFatal( false, errorMessage );
return false;
}
}
if ( !winState.appDC )
{
// Get a new device context:
Con::printf( "Acquiring a new device context..." );
winState.appDC = GetDC( winState.appWindow );
if ( !winState.appDC )
{
AssertFatal( false, "D3DDevice::setScreenMode\nGetDC failed to get a valid device context!" );
return false;
}
}
if ( newWindow )
{
// Set the pixel format of the new window:
PIXELFORMATDESCRIPTOR pfd;
dwglDescribePixelFormat( winState.appDC, 1, sizeof( pfd ), &pfd );
if ( !SetPixelFormat( winState.appDC, 1, &pfd ) )
{
//AssertFatal( false, "D3DDevice::setScreenMode\nFailed to set the pixel format!" );
//return false;
}
Con::printf( "Pixel format set:" );
Con::printf( " %d color bits, %d depth bits, %d stencil bits", pfd.cColorBits, pfd.cDepthBits, pfd.cStencilBits );
}
if ( !winState.hGLRC )
{
// Create a new rendering context:
Con::printf( "Creating a new rendering context..." );
winState.hGLRC = dwglCreateContext( winState.appDC );
if ( !winState.hGLRC )
{
AssertFatal( false, "D3DDevice::setScreenMode\ndwglCreateContext failed to create an OpenGL rendering context!" );
return false;
}
// Make the new rendering context current:
Con::printf( "Making the new rendering context current..." );
if ( !dwglMakeCurrent( winState.appDC, winState.hGLRC ) )
{
AssertFatal( false, "D3DDevice::setScreenMode\ndwglMakeCurrent failed to make the rendering context current!" );
return false;
}
if ( needResurrect )
{
// Reload the textures:
Con::printf( "Resurrecting the texture manager..." );
Game->textureResurrect();
}
}
smCurrentRes = newRes;
Platform::setWindowSize( newRes.w, newRes.h );
char tempBuf[15];
dSprintf( tempBuf, sizeof( tempBuf ), "%d %d %d", smCurrentRes.w, smCurrentRes.h, smCurrentRes.bpp );
Con::setVariable( "$pref::Video::resolution", tempBuf );
if ( curtain )
DestroyWindow( curtain );
// Doesn't hurt to do this even it isn't necessary:
ShowWindow( winState.appWindow, SW_SHOW );
SetForegroundWindow( winState.appWindow );
SetFocus( winState.appWindow );
if ( repaint )
Con::evaluate( "resetCanvas();" );
return true;
}
//------------------------------------------------------------------------------
void D3DDevice::swapBuffers()
{
dwglSwapBuffers( winState.appDC );
}
//------------------------------------------------------------------------------
const char* D3DDevice::getDriverInfo()
{
// Output some driver info to the console:
const char* vendorString = (const char*) glGetString( GL_VENDOR );
const char* rendererString = (const char*) glGetString( GL_RENDERER );
const char* versionString = (const char*) glGetString( GL_VERSION );
const char* extensionsString = (const char*) glGetString( GL_EXTENSIONS );
U32 bufferLen = ( vendorString ? dStrlen( vendorString ) : 0 )
+ ( rendererString ? dStrlen( rendererString ) : 0 )
+ ( versionString ? dStrlen( versionString ) : 0 )
+ ( extensionsString ? dStrlen( extensionsString ) : 0 )
+ 4;
char* returnString = Con::getReturnBuffer( bufferLen );
dSprintf( returnString, bufferLen, "%s\t%s\t%s\t%s",
( vendorString ? vendorString : "" ),
( rendererString ? rendererString : "" ),
( versionString ? versionString : "" ),
( extensionsString ? extensionsString : "" ) );
return( returnString );
}
//------------------------------------------------------------------------------
bool D3DDevice::getGammaCorrection(F32 &g)
{
U16 ramp[256*3];
if (!GetDeviceGammaRamp(winState.appDC, ramp))
return false;
F32 csum = 0.0;
U32 ccount = 0;
for (U16 i = 0; i < 256; ++i)
{
if (i != 0 && ramp[i] != 0 && ramp[i] != 65535)
{
F64 b = (F64) i/256.0;
F64 a = (F64) ramp[i]/65535.0;
F32 c = (F32) (mLog(a)/mLog(b));
csum += c;
++ccount;
}
}
g = csum/ccount;
return true;
}
//------------------------------------------------------------------------------
bool D3DDevice::setGammaCorrection(F32 g)
{
U16 ramp[256*3];
for (U16 i = 0; i < 256; ++i)
ramp[i] = mPow((F32) i/256.0f, g) * 65535.0f;
dMemcpy(&ramp[256],ramp,256*sizeof(U16));
dMemcpy(&ramp[512],ramp,256*sizeof(U16));
return SetDeviceGammaRamp(winState.appDC, ramp);
}
//------------------------------------------------------------------------------
bool D3DDevice::setVerticalSync( bool on )
{
on;
return( false );
}
//------------------------------------------------------------------------------
DisplayDevice* D3DDevice::create()
{
bool result = false;
OSVERSIONINFO OSVersionInfo;
dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) );
OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if ( GetVersionEx( &OSVersionInfo ) &&
(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
OSVersionInfo.dwMajorVersion <= 4) )
return NULL;
// This shouldn't happen, but just to be safe...
if ( winState.hinstOpenGL )
GL_Shutdown();
if (!GL_Init(OPENGL2D3D_DLL,GLU2D3D_DLL))
return NULL;
// Create a test window to see if OpenGL hardware acceleration is available:
WNDCLASS wc;
dMemset(&wc, 0, sizeof(wc));
wc.style = CS_OWNDC;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = winState.appInstance;
wc.lpszClassName = dT( "D3DTest" );
RegisterClass( &wc );
HWND testWindow = CreateWindow(
dT("D3DTest"),
dT(""),
WS_POPUP,
0, 0, 640, 480,
NULL,
NULL,
winState.appInstance,
NULL );
if ( testWindow )
{
HDC testDC = GetDC( testWindow );
if ( testDC )
{
PIXELFORMATDESCRIPTOR pfd;
result = dwglDescribePixelFormat( testDC, 1, sizeof( pfd ), &pfd );
ReleaseDC( testWindow, testDC );
}
DestroyWindow( testWindow );
}
UnregisterClass( dT("D3DTest"), winState.appInstance );
GL_Shutdown();
if ( result )
{
D3DDevice* newD3DDevice = new D3DDevice();
if ( newD3DDevice )
{
DisplayDevice *glDev = Video::getDevice("OpenGL");
if( glDev != NULL )
newD3DDevice->mFullScreenOnly = glDev->isFullScreenOnly();
else
newD3DDevice->mFullScreenOnly = false;
return (DisplayDevice*) newD3DDevice;
}
else
return NULL;
}
else
return NULL;
}