//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "platformWin32/platformGL.h" #include "platformWin32/platformWin32.h" #include "platformWin32/winOGLVideo.h" #include "console/console.h" #include "math/mPoint.h" #include "platform/event.h" #include "game/gameInterface.h" #include "console/consoleInternal.h" #include "console/ast.h" #include "io/fileStream.h" //------------------------------------------------------------------------------ struct CardProfile { const char *vendor; // manufacturer const char *renderer; // driver name bool safeMode; // destroy rendering context for resolution change bool lockArray; // allow compiled vertex arrays bool subImage; // allow glTexSubImage* bool fogTexture; // require bound texture for combine extension bool noEnvColor; // no texture environment color bool clipHigh; // clip high resolutions bool deleteContext; // delete rendering context bool texCompress; // allow texture compression bool interiorLock; // lock arrays for Interior render bool skipFirstFog; // skip first two-pass fogging (dumb 3Dfx hack) bool only16; // inhibit 32-bit resolutions bool noArraysAlpha; // don't use glDrawArrays with a GL_ALPHA texture const char *proFile; // explicit profile of graphic settings }; struct OSCardProfile { const char *vendor; // manufacturer const char *renderer; // driver name bool allowOpenGL; // allow OpenGL driver bool preferOpenGL; // prefer OpenGL driver }; static Vector sCardProfiles(__FILE__, __LINE__); static Vector sOSCardProfiles(__FILE__, __LINE__); struct ProcessorProfile { U16 clock; // clock range max U16 adjust; // CPU adjust }; static U8 sNumProcessors = 4; static ProcessorProfile sProcessorProfiles[] = { { 400, 0 }, { 600, 5 }, { 800, 10 }, { 1000, 15 }, }; struct SettingProfile { U16 performance; // metric range max const char *settings; // default file }; static U8 sNumSettings = 3; static SettingProfile sSettingProfiles[] = { { 33, "LowProfile.cs" }, { 66, "MediumProfile.cs" }, { 100, "HighProfile.cs" }, }; //------------------------------------------------------------------------------ ConsoleFunction( addCardProfile, void, 16, 16, "(string vendor, string renderer," "bool safeMode, bool lockArray, bool subImage, bool fogTexture," "bool noEnvColor, bool clipHigh, bool deleteContext, bool texCompress" "bool interiorLock, bool skipFirstFog, bool only16," "bool noArraysAlpha, string proFile)" "" "Register a card profile with the card profile manager.\n\n" "Most of the parameters are fairly self-explanatory and very internal" " to the rendering code; however there are a few of note:\n" "@param vendor The vendor string the card claims.\n" "@param renderer The renderer string the card claims.\n" "@param proFile Name of the file where further configuration information is kept." ) { CardProfile profile; profile.vendor = dStrdup(argv[1]); profile.renderer = dStrdup(argv[2]); profile.safeMode = dAtob(argv[3]); profile.lockArray = dAtob(argv[4]); profile.subImage = dAtob(argv[5]); profile.fogTexture = dAtob(argv[6]); profile.noEnvColor = dAtob(argv[7]); profile.clipHigh = dAtob(argv[8]); profile.deleteContext = dAtob(argv[9]); profile.texCompress = dAtob(argv[10]); profile.interiorLock = dAtob(argv[11]); profile.skipFirstFog = dAtob(argv[12]); profile.only16 = dAtob(argv[13]); profile.noArraysAlpha = dAtob(argv[14]); if (strcmp(argv[15],"")) profile.proFile = dStrdup(argv[15]); else profile.proFile = NULL; sCardProfiles.push_back(profile); } ConsoleFunction( addOSCardProfile, void, 6,6, "(string vendor, string renderer, bool allowOpenGL," "Register with the profile manager what rendering mode is preferred with the " "specified card.") { OSCardProfile profile; profile.vendor = dStrdup(argv[1]); profile.renderer = dStrdup(argv[2]); profile.allowOpenGL = dAtob(argv[3]); profile.preferOpenGL = dAtob(argv[5]); sOSCardProfiles.push_back(profile); } static void clearCardProfiles() { while (sCardProfiles.size()) { dFree((char *) sCardProfiles.last().vendor); dFree((char *) sCardProfiles.last().renderer); dFree((char *) sCardProfiles.last().proFile); sCardProfiles.decrement(); } } static void clearOSCardProfiles() { while (sOSCardProfiles.size()) { dFree((char *) sOSCardProfiles.last().vendor); dFree((char *) sOSCardProfiles.last().renderer); sOSCardProfiles.decrement(); } } static void execScript(const char *scriptFile) { // execute the script FileStream str; if (!str.open(scriptFile, FileStream::Read)) return; U32 size = str.getStreamSize(); char *script = new char[size + 1]; str.read(size, script); str.close(); script[size] = 0; Con::executef(2, "eval", script); delete[] script; } static void profileSystem(const char *vendor, const char *renderer) { //Con::executef(2, "exec", "scripts/CardProfiles.cs"); execScript("CardProfiles.cs"); const char *arch; OSVERSIONINFO OSVersionInfo; const char *os = NULL; char osProfiles[64]; if (dStrstr(PlatformSystemInfo.processor.name,"AMD") != NULL) arch = "AMD"; else arch = "Intel"; dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVersionInfo ) ) { if (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) if ( OSVersionInfo.dwMinorVersion == 0 ) { if (dStrcmp(arch,"Intel") == 0) os = "W95"; } else if ( OSVersionInfo.dwMinorVersion == 10 ) if ( OSVersionInfo.szCSDVersion[1] != 'A' ) os = "W98"; else os = "W98SE"; else os = "WME"; else if ( OSVersionInfo.dwMajorVersion >= 5 ) os = "W2K"; if ( os != NULL ) { dSprintf(osProfiles,64,"%s%sCardProfiles.cs",arch,os); //Con::executef(2, "exec", osProfiles); execScript(osProfiles); } } const char *proFile = NULL; U32 i; for (i = 0; i < (U32)sCardProfiles.size(); ++i) if (dStrstr(vendor, sCardProfiles[i].vendor) && (!dStrcmp(sCardProfiles[i].renderer, "*") || dStrstr(renderer, sCardProfiles[i].renderer))) { Con::setBoolVariable("$pref::Video::safeModeOn", sCardProfiles[i].safeMode); Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", !sCardProfiles[i].lockArray); Con::setBoolVariable("$pref::OpenGL::disableSubImage", !sCardProfiles[i].subImage); Con::setBoolVariable("$pref::TS::fogTexture", sCardProfiles[i].fogTexture); Con::setBoolVariable("$pref::OpenGL::noEnvColor", sCardProfiles[i].noEnvColor); Con::setBoolVariable("$pref::Video::clipHigh", sCardProfiles[i].clipHigh); if (!sCardProfiles[i].deleteContext) { OSVERSIONINFO OSVersionInfo; // HACK: The Voodoo3/5 on W2K crash when deleting a rendering context // So we're not deleting it. // Oh, and the Voodoo3 returns a Banshee renderer string under W2K dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVersionInfo ) && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion == 5) Con::setBoolVariable("$pref::Video::deleteContext", false); else Con::setBoolVariable("$pref::Video::deleteContext", true); } else Con::setBoolVariable("$pref::Video::deleteContext", true); Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", !sCardProfiles[i].texCompress); Con::setBoolVariable("$pref::Interior::lockArrays", sCardProfiles[i].interiorLock); Con::setBoolVariable("$pref::TS::skipFirstFog", sCardProfiles[i].skipFirstFog); Con::setBoolVariable("$pref::Video::only16", sCardProfiles[i].only16); Con::setBoolVariable("$pref::OpenGL::noDrawArraysAlpha", sCardProfiles[i].noArraysAlpha); proFile = sCardProfiles[i].proFile; break; } // defaults U16 glProfile; if (!proFile) { // no driver GL profile -- make one via weighting GL extensions glProfile = 25; glProfile += gGLState.suppARBMultitexture * 25; glProfile += gGLState.suppLockedArrays * 15; glProfile += gGLState.suppVertexArrayRange * 10; glProfile += gGLState.suppTextureEnvCombine * 5; glProfile += gGLState.suppPackedPixels * 5; glProfile += gGLState.suppTextureCompression * 5; glProfile += gGLState.suppS3TC * 5; glProfile += gGLState.suppFXT1 * 5; Con::setBoolVariable("$pref::Video::safeModeOn", true); Con::setBoolVariable("$pref::OpenGL::disableEXTCompiledVertexArray", false); Con::setBoolVariable("$pref::OpenGL::disableSubImage", false); Con::setBoolVariable("$pref::TS::fogTexture", false); Con::setBoolVariable("$pref::OpenGL::noEnvColor", false); Con::setBoolVariable("$pref::Video::clipHigh", false); Con::setBoolVariable("$pref::Video::deleteContext", true); Con::setBoolVariable("$pref::OpenGL::disableARBTextureCompression", false); Con::setBoolVariable("$pref::Interior::lockArrays", true); Con::setBoolVariable("$pref::TS::skipFirstFog", false); Con::setBoolVariable("$pref::Video::only16", false); Con::setBoolVariable("$pref::OpenGL::noDrawArraysAlpha", false); } Con::setVariable("$pref::Video::profiledVendor", vendor); Con::setVariable("$pref::Video::profiledRenderer", renderer); if (!Con::getBoolVariable("$DisableSystemProfiling") && ( dStrcmp(vendor, Con::getVariable("$pref::Video::defaultsVendor")) || dStrcmp(renderer, Con::getVariable("$pref::Video::defaultsRenderer")) )) { if (proFile) { char settings[64]; dSprintf(settings,64,"%s.cs",proFile); //Con::executef(2, "exec", settings); execScript(settings); } else { U16 adjust; // match clock with profile for (i = 0; i < sNumProcessors; ++i) { adjust = sProcessorProfiles[i].adjust; if (PlatformSystemInfo.processor.mhz < sProcessorProfiles[i].clock) break; } const char *settings; // match performance metric with profile for (i = 0; i < sNumSettings; ++i) { settings = sSettingProfiles[i].settings; if (glProfile+adjust <= sSettingProfiles[i].performance) break; } //Con::executef(2, "exec", settings); execScript(settings); } bool match = false; for (i = 0; i < (U32)sOSCardProfiles.size(); ++i) if (dStrstr(vendor, sOSCardProfiles[i].vendor) && (!dStrcmp(sOSCardProfiles[i].renderer, "*") || dStrstr(renderer, sOSCardProfiles[i].renderer))) { Con::setBoolVariable("$pref::Video::allowOpenGL", sOSCardProfiles[i].allowOpenGL); Con::setBoolVariable("$pref::Video::preferOpenGL", sOSCardProfiles[i].preferOpenGL); match = true; break; } if (!match) { Con::setBoolVariable("$pref::Video::allowOpenGL", true); Con::setBoolVariable("$pref::Video::preferOpenGL", true); } Con::setVariable("$pref::Video::defaultsVendor", vendor); Con::setVariable("$pref::Video::defaultsRenderer", renderer); } clearCardProfiles(); clearOSCardProfiles(); } //------------------------------------------------------------------------------ OpenGLDevice::OpenGLDevice() { initDevice(); } //------------------------------------------------------------------------------ void OpenGLDevice::initDevice() { // Set the device name: mDeviceName = "OpenGL"; // Let Parent enumerate valid resolutions. Parent::initDevice(); } //------------------------------------------------------------------------------ bool OpenGLDevice::activate( U32 width, U32 height, U32 bpp, bool fullScreen ) { Con::printf( "Activating the OpenGL display device..." ); 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, "OpenGLDevice::activate\ndwglMakeCurrent( NULL, NULL ) failed!" ); return false; } Con::printf( "Deleting the rendering context ..." ); if ( !dwglDeleteContext( winState.hGLRC ) ) { AssertFatal( false, "OpenGLDevice::activate\ndwglDeleteContext failed!" ); return false; } winState.hGLRC = NULL; } // [neo, 5/31/2007 - #3174] if( winState.appMenu ) { DestroyMenu( winState.appMenu ); winState.appMenu = NULL; } // If the window already exists, kill it so we can start fresh: if ( winState.appWindow ) { if ( winState.appDC ) { 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( "opengl32", "glu32" ); static bool onceAlready = false; bool profiled = false; if ( !mFullScreenOnly && fullScreen && !onceAlready ) { OSVERSIONINFO OSVersionInfo; // HACK: The Voodoo5 on W2K will only work if the initial rendering // context is windowed. Can you believe this crap? dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); if ( GetVersionEx( &OSVersionInfo ) && OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion == 5 ) //MIN_RESOLUTION defined in platformWin32/platformGL.h if ( !setScreenMode( MIN_RESOLUTION_X, MIN_RESOLUTION_Y, bpp, false, true, false ) ) return false; else { const char* vendorString = (const char*) glGetString( GL_VENDOR ); const char* rendererString = (const char*) glGetString( GL_RENDERER ); // 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); profiled = true; } } onceAlready = true; } // 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 ); // only do this for the first session if (!profiled && !Con::getBoolVariable("$DisableSystemProfiling") && ( dStrcmp(vendorString, Con::getVariable("$pref::Video::profiledVendor")) || dStrcmp(rendererString, Con::getVariable("$pref::Video::profiledRenderer")) )) { profileSystem(vendorString, rendererString); profiled = true; } if (profiled) { U32 width, height, bpp; if (Con::getBoolVariable("$pref::Video::clipHigh", false)) for (S32 i = mResolutionList.size()-1; i >= 0; --i) if (mResolutionList[i].w > 1152 || mResolutionList[i].h > 864) mResolutionList.erase(i); if (Con::getBoolVariable("$pref::Video::only16", false)) for (S32 i = mResolutionList.size()-1; i >= 0; --i) if (mResolutionList[i].bpp == 32) mResolutionList.erase(i); bool bPrefFullScreen = Con::getBoolVariable("$pref::Video::fullScreen", true); if( bPrefFullScreen || fullScreen ) dSscanf(Con::getVariable("$pref::Video::resolution"), "%d %d %d", &width, &height, &bpp); else dSscanf(Con::getVariable("$pref::Video::windowedRes"), "%d %d %d", &width, &height, &bpp); setScreenMode(width, height, bpp, bPrefFullScreen || fullScreen, false, false); } // Do this here because we now know about the extensions: if ( gGLState.suppSwapInterval ) setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); Con::setBoolVariable("$pref::OpenGL::allowTexGen", true); return true; } //------------------------------------------------------------------------------ void OpenGLDevice::shutdown() { Con::printf( "Shutting down the OpenGL display device..." ); if ( winState.hGLRC ) { if (mRestoreGamma) SetDeviceGammaRamp(winState.appDC, mOriginalRamp); Con::printf( "Making the GL rendering context not current..." ); dwglMakeCurrent( NULL, NULL ); if ( Con::getBoolVariable("$pref::Video::deleteContext", true) ) { Con::printf( "Deleting the GL rendering context..." ); dwglDeleteContext( winState.hGLRC ); } winState.hGLRC = NULL; } if ( winState.appDC ) { Con::printf( "Releasing the device context..." ); ReleaseDC( winState.appWindow, winState.appDC ); winState.appDC = NULL; } if ( smIsFullScreen ) { Con::printf( "Restoring the desktop display settings (%dx%dx%d)...", winState.desktopWidth, winState.desktopHeight, winState.desktopBitsPixel ); ChangeDisplaySettings( NULL, 0 ); } } //------------------------------------------------------------------------------ // This is the real workhorse function of the DisplayDevice... // bool OpenGLDevice::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, "OpenGLDevice::setScreenMode - device or desktop color depth does not allow windowed mode!" ); newFullScreen = true; } if ( !newFullScreen && ( (S32)newRes.w >= winState.desktopClientWidth || (S32)newRes.h >= winState.desktopClientHeight ) ) { Con::warnf( ConsoleLogEntry::General, "OpenGLDevice::setScreenMode -- can't switch to resolution larger than desktop in windowed mode!" ); S32 resIndex = 0; S32 bestScore = 0, thisScore = 0; for ( S32 i = 0; i < mResolutionList.size(); i++ ) { if ( newRes == mResolutionList[i] ) { resIndex = i; break; } else if( (S32)newRes.h <= winState.desktopClientHeight && (S32)newRes.w <= winState.desktopClientWidth ) { 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; } } } } //MIN_RESOLUTION defined in platformWin32/platformGL.h if ( newRes.w < MIN_RESOLUTION_X || newRes.h < MIN_RESOLUTION_Y ) { Con::warnf( ConsoleLogEntry::General, "OpenGLDevice::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: newRes.bpp = winState.desktopBitsPixel; } // 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; if ( ( newRes.bpp != smCurrentRes.bpp ) || ( safeModeOn && ( ( smIsFullScreen != newFullScreen ) || newFullScreen ) ) ) { // 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, "OpenGLDevice::setScreenMode\ndwglMakeCurrent( NULL, NULL ) failed!" ); return false; } Con::printf( "Deleting the rendering context..." ); if ( Con::getBoolVariable("$pref::Video::deleteContext",true) && !dwglDeleteContext( winState.hGLRC ) ) { AssertFatal( false, "OpenGLDevice::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; } // [neo, 5/31/2007 - #3174] if( winState.appMenu ) { DestroyMenu( winState.appMenu ); winState.appMenu = 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_OVERLAPPEDWINDOW ); if ( winState.appWindow && !SetWindowLong( winState.appWindow, GWL_STYLE, windowStyle ) ) Con::errorf( "SetWindowLong 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, "OpenGLDevice::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 ) { Con::printf( "Changing to the desktop display settings (%dx%dx%d)...", winState.desktopWidth, winState.desktopHeight, winState.desktopBitsPixel ); ChangeDisplaySettings( NULL, 0 ); 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, true ); if ( !winState.appWindow ) { AssertFatal( false, "OpenGLDevice::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 = 0, yPos = 0; if(adjWidth < (U32)winState.desktopClientWidth) xPos = ( winState.desktopClientWidth - adjWidth ) / 2; if(adjHeight < (U32)winState.desktopClientHeight) yPos = ( winState.desktopClientHeight - adjHeight ) / 2; test = SetWindowPos( winState.appWindow, 0, xPos, yPos, adjWidth, adjHeight, SWP_NOZORDER ); if ( !test ) { dSprintf( errorMessage, 255, "OpenGLDevice::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; } AnimateWindow( winState.appWindow, 90, AW_BLEND ); } else if ( !newWindow ) { // Move and size the window to take up the whole screen: if ( !SetWindowPos( winState.appWindow, HWND_TOPMOST, 0, 0, newRes.w, newRes.h, SWP_NOACTIVATE) || !SetWindowPos( winState.appWindow, HWND_TOPMOST, 0, 0, newRes.w, newRes.h, SWP_NOZORDER) ) { dSprintf( errorMessage, 255, "OpenGLDevice::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; } } bool newDeviceContext = 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, "OpenGLDevice::setScreenMode\nGetDC failed to get a valid device context!" ); return false; } newDeviceContext = true; } if ( newWindow ) { // Set the pixel format of the new window: PIXELFORMATDESCRIPTOR pfd; CreatePixelFormat( &pfd, newRes.bpp, 24, 8, false ); S32 chosenFormat = ChooseBestPixelFormat( winState.appDC, &pfd ); if ( !chosenFormat ) { AssertFatal( false, "OpenGLDevice::setScreenMode\nNo valid pixel formats found!" ); return false; } dwglDescribePixelFormat( winState.appDC, chosenFormat, sizeof( pfd ), &pfd ); if ( !SetPixelFormat( winState.appDC, chosenFormat, &pfd ) ) { AssertFatal( false, "OpenGLDevice::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, "OpenGLDevice::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, "OpenGLDevice::setScreenMode\ndwglMakeCurrent failed to make the rendering context current!" ); return false; } // Just for kicks. Seems a relatively central place to put this... glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if ( needResurrect ) { // Reload the textures: Con::printf( "Resurrecting the texture manager..." ); Game->textureResurrect(); } } // Just for kicks. Seems a relatively central place to put this... glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if ( newDeviceContext && gGLState.suppSwapInterval ) setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); smCurrentRes = newRes; Platform::setWindowSize( newRes.w, newRes.h ); char tempBuf[15]; dSprintf( tempBuf, sizeof( tempBuf ), "%d %d %d", smCurrentRes.w, smCurrentRes.h, smCurrentRes.bpp ); if( fullScreen ) Con::setVariable( "$pref::Video::resolution", tempBuf ); else Con::setVariable( "$pref::Video::windowedRes", 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 (newFullScreen) { // Doesn't hurt to do this an extra time, and seems to help in some cases if ( !SetWindowPos( winState.appWindow, HWND_TOPMOST, 0, 0, newRes.w, newRes.h, SWP_NOACTIVATE) || !SetWindowPos( winState.appWindow, HWND_TOPMOST, 0, 0, newRes.w, newRes.h, SWP_NOZORDER) ) { dSprintf( errorMessage, 255, "OpenGLDevice::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; } } //Luma: Clear window at first, as it is showing previous gl color buffer stuff. glClearColor(0.0f,0.0f,0.0f,0.0f); glClear(GL_COLOR_BUFFER_BIT); if ( repaint ) Con::evaluate( "resetCanvas();" ); return true; } //------------------------------------------------------------------------------ void OpenGLDevice::swapBuffers() { dwglSwapBuffers( winState.appDC ); } //------------------------------------------------------------------------------ const char* OpenGLDevice::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 OpenGLDevice::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 OpenGLDevice::setGammaCorrection(F32 g) { U16 ramp[256*3]; for (U16 i = 0; i < 256; ++i) ramp[i] = (U16)((U16)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 OpenGLDevice::setVerticalSync( bool on ) { if ( !gGLState.suppSwapInterval ) return( false ); return( dwglSwapIntervalEXT( on ? 1 : 0 ) ); } //------------------------------------------------------------------------------ DisplayDevice* OpenGLDevice::create() { // Get Bit Depth Switching Info. enumerateBitDepths(); bool result = false; bool fullScreenOnly = false; //------------------------------------------------------------------------------ // This shouldn't happen, but just to be safe... //------------------------------------------------------------------------------ if ( winState.hinstOpenGL ) GL_Shutdown(); //------------------------------------------------------------------------------ // Initialize GL //------------------------------------------------------------------------------ if (!GL_Init( "opengl32", "glu32" )) 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("OGLTest"); RegisterClass( &wc ); //------------------------------------------------------------------------------ // Create the Test Window //------------------------------------------------------------------------------ //MIN_RESOLUTION defined in platformWin32/platformGL.h HWND testWindow = CreateWindow( dT("OGLTest"),dT(""), WS_POPUP, 0, 0, MIN_RESOLUTION_X, MIN_RESOLUTION_Y, NULL, NULL, winState.appInstance, NULL ); if ( !testWindow ) { // Unregister the Window Class UnregisterClass( dT("OGLTest"), winState.appInstance ); // Shutdown GL GL_Shutdown(); // Return Failure return NULL; } //------------------------------------------------------------------------------ // Attempt to Grab a handle to the DeviceContext of our window. //------------------------------------------------------------------------------ HDC testDC = GetDC( testWindow ); if ( !testDC ) { // Destroy the Window DestroyWindow( testWindow ); // Unregister the Window Class UnregisterClass( dT("OGLTest"), winState.appInstance ); // Shutdown GL GL_Shutdown(); // Return Failure. return NULL; } //------------------------------------------------------------------------------ // Create Pixel Format ( Default 16bpp ) //------------------------------------------------------------------------------ PIXELFORMATDESCRIPTOR pfd; CreatePixelFormat( &pfd, 16, 16, 8, false ); U32 chosenFormat = ChooseBestPixelFormat( testDC, &pfd ); if ( chosenFormat != 0 ) { dwglDescribePixelFormat( testDC, chosenFormat, sizeof( pfd ), &pfd ); result = !( pfd.dwFlags & PFD_GENERIC_FORMAT ); if ( result && winState.desktopBitsPixel < 16 && !smCanDo32Bit) { // If Windows 95 cannot switch bit depth, it should only attempt 16-bit cards // with a 16-bit desktop // See if we can get a 32-bit pixel format: PIXELFORMATDESCRIPTOR pfd; CreatePixelFormat( &pfd, 32, 24, 8, false ); S32 chosenFormat = ChooseBestPixelFormat( testDC, &pfd ); if ( chosenFormat != 0 ) { dwglDescribePixelFormat( winState.appDC, chosenFormat, sizeof( pfd ), &pfd ); if (pfd.cColorBits == 16) { Platform::AlertOK("Requires 16-Bit Desktop", "You must run in 16-bit color to run a Torque game.\nPlease quit the game, set your desktop color depth to 16-bit\nand then restart the application."); result = false; } } } } else if ( winState.desktopBitsPixel < 16 && smCanSwitchBitDepth ) { // Try again after changing the display to 16-bit: ReleaseDC( testWindow, testDC ); DestroyWindow( testWindow ); DEVMODE devMode; dMemset( &devMode, 0, sizeof( devMode ) ); devMode.dmSize = sizeof( devMode ); devMode.dmBitsPerPel = 16; devMode.dmFields = DM_BITSPERPEL; U32 test = ChangeDisplaySettings( &devMode, 0 ); if ( test == DISP_CHANGE_SUCCESSFUL ) { //MIN_RESOLUTION defined in platformWin32/platformGL.h testWindow = CreateWindow( dT("OGLTest"), dT(""), WS_OVERLAPPED | WS_CAPTION, 0, 0, MIN_RESOLUTION_X, MIN_RESOLUTION_Y, NULL, NULL, winState.appInstance, NULL ); if ( testWindow ) { testDC = GetDC( testWindow ); if ( testDC ) { CreatePixelFormat( &pfd, 16, 16, 8, false ); chosenFormat = ChooseBestPixelFormat( testDC, &pfd ); if ( chosenFormat != 0 ) { dwglDescribePixelFormat( testDC, chosenFormat, sizeof( pfd ), &pfd ); result = !( pfd.dwFlags & PFD_GENERIC_FORMAT ); if ( result ) fullScreenOnly = true; } } } } ChangeDisplaySettings( NULL, 0 ); } //------------------------------------------------------------------------------ // Can't do even 16 bit, alert user they need to upgrade. //------------------------------------------------------------------------------ else if ( winState.desktopBitsPixel < 16 && !smCanSwitchBitDepth ) { Platform::AlertOK("Requires 16-Bit Desktop", "You must run in 16-bit color to run a Torque game.\nPlease quit the game, set your desktop color depth to 16-bit\nand then restart the application."); } ReleaseDC( testWindow, testDC ); DestroyWindow( testWindow ); UnregisterClass( dT("OGLTest"), winState.appInstance ); GL_Shutdown(); if ( result ) { OpenGLDevice* newOGLDevice = new OpenGLDevice(); if ( newOGLDevice ) { newOGLDevice->mFullScreenOnly = fullScreenOnly; return (DisplayDevice*) newOGLDevice; } else return NULL; } else return NULL; }