//----------------------------------------------------------------------------- // Copyright (c) 2012 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 "platform/platform.h" #include "gfx/gfxInit.h" #include "gfx/gfxTextureManager.h" #include "gfx/gfxAPI.h" #include "console/console.h" #include "windowManager/platformWindowMgr.h" #include "core/module.h" Vector GFXInit::smAdapters( __FILE__, __LINE__ ); GFXInit::RegisterDeviceSignal* GFXInit::smRegisterDeviceSignal; MODULE_BEGIN( GFX ) MODULE_INIT { GFXInit::init(); if( engineAPI::gUseConsoleInterop ) GFXDevice::initConsole(); GFXTextureManager::init(); } MODULE_SHUTDOWN { GFXDevice::destroy(); GFXInit::cleanup(); } MODULE_END; IMPLEMENT_STATIC_CLASS( GFXInit, GFXAPI, "Functions for tracking GFX adapters and initializing them into devices." ); ConsoleDoc( "@class GFXInit\n" "@ingroup GFX\n" "@brief Functions for tracking GFX adapters and initializing them into devices.\n" ); inline static void _GFXInitReportAdapters(Vector &adapters) { for (U32 i = 0; i < adapters.size(); i++) { switch (adapters[i]->mType) { case OpenGL: Con::printf(" OpenGL device found"); break; case NullDevice: Con::printf(" Null device found"); break; case Direct3D11: Con::printf(" Direct 3D (version 11.x) device found"); break; default : Con::printf(" Unknown device found"); break; } } } inline static void _GFXInitGetInitialRes(GFXVideoMode &vm) { // cache the desktop size of the main screen GFXVideoMode desktopVm = GFXInit::getDesktopResolution(); // load pref variables, properly choose windowed / fullscreen const String resString = Con::getVariable("$pref::Video::mode"); // Parse video mode settings from pref string vm.parseFromString(resString); } GFXInit::RegisterDeviceSignal& GFXInit::getRegisterDeviceSignal() { if (smRegisterDeviceSignal) return *smRegisterDeviceSignal; smRegisterDeviceSignal = new RegisterDeviceSignal(); return *smRegisterDeviceSignal; } void GFXInit::init() { // init only once. static bool doneOnce = false; if(doneOnce) return; doneOnce = true; Con::printf( "GFX Init:" ); //find our adapters Vector adapters( __FILE__, __LINE__ ); GFXInit::enumerateAdapters(); GFXInit::getAdapters(&adapters); if(!adapters.size()) Con::errorf("Could not find a display adapter"); //loop through and tell the user what kind of adapters we found _GFXInitReportAdapters(adapters); Con::printf( "" ); } void GFXInit::cleanup() { while( smAdapters.size() ) { GFXAdapter* adapter = smAdapters.last(); smAdapters.decrement(); delete adapter; } if( smRegisterDeviceSignal ) SAFE_DELETE( smRegisterDeviceSignal ); } bool GFXInit::compareAdapterOutputDevice(const GFXAdapter* adapter, const char* outputDevice) { // If the adapter doesn't have an output display device, then it supports all of them if(!adapter->mOutputName[0]) return true; // Try and match the first part of the output device display name. For example, // an adapter->mOutputName of "\\.\DISPLAY1" might correspond to a display name // of "\\.\DISPLAY1\Monitor0". If two monitors are set up in duplicate mode then // they will have the same 'display' part in their display name. return (dStrstr(outputDevice, adapter->mOutputName) == outputDevice); } GFXAdapter* GFXInit::getAdapterOfType( GFXAdapterType type, const char* outputDevice ) { bool testOutputDevice = false; if(outputDevice && outputDevice[0]) testOutputDevice = true; for( U32 i = 0; i < smAdapters.size(); i++ ) { if( smAdapters[i]->mType == type ) { if(testOutputDevice) { // Check if the output display device also matches if(compareAdapterOutputDevice(smAdapters[i], outputDevice)) { return smAdapters[i]; } } else { // No need to also test the output display device, so return return smAdapters[i]; } } } return NULL; } GFXAdapter* GFXInit::getAdapterOfType(GFXAdapterType type, S32 outputDeviceIndex) { for (U32 i = 0; i < smAdapters.size(); i++) { if (smAdapters[i]->mType == type) { if (smAdapters[i]->mIndex == outputDeviceIndex) { return smAdapters[i]; } } } return NULL; } GFXAdapter* GFXInit::chooseAdapter( GFXAdapterType type, const char* outputDevice) { GFXAdapter* adapter = GFXInit::getAdapterOfType(type, outputDevice); if(!adapter && type != OpenGL) { Con::errorf("The requested renderer, %s, doesn't seem to be available." " Trying the default, OpenGL.", getAdapterNameFromType(type)); adapter = GFXInit::getAdapterOfType(OpenGL, outputDevice); } if(!adapter) { Con::errorf("The OpenGL renderer doesn't seem to be available. Trying the GFXNulDevice."); adapter = GFXInit::getAdapterOfType(NullDevice, ""); } AssertFatal( adapter, "There is no rendering device available whatsoever."); return adapter; } GFXAdapter* GFXInit::chooseAdapter(GFXAdapterType type, S32 outputDeviceIndex) { GFXAdapter* adapter = GFXInit::getAdapterOfType(type, outputDeviceIndex); if (!adapter && type != OpenGL) { Con::errorf("The requested renderer, %s, doesn't seem to be available." " Trying the default, OpenGL.", getAdapterNameFromType(type)); adapter = GFXInit::getAdapterOfType(OpenGL, outputDeviceIndex); } if (!adapter) { Con::errorf("The OpenGL renderer doesn't seem to be available. Trying the GFXNulDevice."); adapter = GFXInit::getAdapterOfType(NullDevice, 0); } AssertFatal(adapter, "There is no rendering device available whatsoever."); return adapter; } const char* GFXInit::getAdapterNameFromType(GFXAdapterType type) { // must match GFXAdapterType order static const char* _names[] = { "OpenGL", "D3D11", "NullDevice" }; if( type < 0 || type >= GFXAdapterType_Count ) { Con::errorf( "GFXInit::getAdapterNameFromType - Invalid renderer type, defaulting to OpenGL" ); return _names[OpenGL]; } return _names[type]; } GFXAdapterType GFXInit::getAdapterTypeFromName(const char* name) { S32 ret = -1; for(S32 i = 0; i < GFXAdapterType_Count; i++) { if( !dStricmp( getAdapterNameFromType((GFXAdapterType)i), name ) ) ret = i; } if( ret == -1 ) { Con::errorf( "GFXInit::getAdapterTypeFromName - Invalid renderer name, defaulting to D3D11" ); ret = Direct3D11; } return (GFXAdapterType)ret; } GFXAdapter *GFXInit::getBestAdapterChoice() { // Get the user's preference for device... const String renderer = Con::getVariable("$pref::Video::displayDevice"); const String outputDevice = Con::getVariable("$pref::Video::displayOutputDevice"); const String adapterDevice = Con::getVariable("$Video::forceDisplayAdapter"); GFXAdapterType adapterType = getAdapterTypeFromName(renderer.c_str());; GFXAdapter *adapter = NULL; if (adapterDevice.isEmpty()) { adapter = chooseAdapter(adapterType, outputDevice.c_str()); } else { S32 adapterIdx = dAtoi(adapterDevice.c_str()); if (adapterIdx == -1) adapter = chooseAdapter(NullDevice, outputDevice.c_str()); else adapter = chooseAdapter(adapterType, adapterIdx); } // Did they have one? Return it. if(adapter) return adapter; // Didn't have one. So make it up. Find the highest SM available. Prefer // D3D to GL because if we have a D3D device at all we're on windows, // and in an unknown situation on Windows D3D is probably the safest bet. // // If D3D is unavailable, we're not on windows, so GL is de facto the // best choice! F32 highestSMDX = 0.f, highestSMGL = 0.f; GFXAdapter *foundAdapterGL = NULL, *foundAdapter11 = NULL; for (S32 i = 0; imType) { case Direct3D11: if (currAdapter->mShaderModel > highestSMDX) { highestSMDX = currAdapter->mShaderModel; foundAdapter11 = currAdapter; } break; case OpenGL: if (currAdapter->mShaderModel > highestSMGL) { highestSMGL = currAdapter->mShaderModel; foundAdapterGL = currAdapter; } break; default: break; } } // Return best found in order DX11, GL if (foundAdapter11) return foundAdapter11; if (foundAdapterGL) return foundAdapterGL; // Uh oh - we didn't find anything. Grab whatever we can that's not Null... for(S32 i=0; imType != NullDevice) return smAdapters[i]; // Dare we return a null device? No. Just return NULL. return NULL; } GFXVideoMode GFXInit::getInitialVideoMode() { GFXVideoMode vm; _GFXInitGetInitialRes(vm); return vm; } S32 GFXInit::getAdapterCount() { return smAdapters.size(); } void GFXInit::getAdapters(Vector *adapters) { adapters->clear(); for (U32 k = 0; k < smAdapters.size(); k++) adapters->push_back(smAdapters[k]); } GFXVideoMode GFXInit::getDesktopResolution() { GFXVideoMode resVm; // Retrieve Resolution Information. resVm.bitDepth = WindowManager->getDesktopBitDepth(); resVm.resolution = WindowManager->getDesktopResolution(); resVm.fullScreen = false; resVm.refreshRate = 60; // Return results return resVm; } void GFXInit::enumerateAdapters() { // Call each device class and have it report any adapters it supports. if(smAdapters.size()) { // CodeReview Seems like this is ok to just ignore? [bjg, 5/19/07] //Con::warnf("GFXInit::enumerateAdapters - already have a populated adapter list, aborting re-analysis."); return; } getRegisterDeviceSignal().trigger(GFXInit::smAdapters); } GFXDevice *GFXInit::createDevice( GFXAdapter *adapter ) { Con::printf("Attempting to create GFX device: %s [%s]", adapter->getName(), adapter->getOutputName()); GFXDevice* temp = adapter->mCreateDeviceInstanceDelegate(adapter->mIndex); if (temp) { Con::printf("Device created, setting adapter and enumerating modes"); temp->setAdapter(*adapter); temp->enumerateVideoModes(); temp->getVideoModeList(); } else Con::errorf("Failed to create GFX device"); GFXDevice::getDeviceEventSignal().trigger(GFXDevice::deCreate); return temp; } DefineEngineFunction( getDesktopResolution, Point3F, (),, "Returns the width, height, and bitdepth of the screen/desktop.\n\n@ingroup GFX" ) { GFXVideoMode res = GFXInit::getDesktopResolution(); return Point3F( res.resolution.x, res.resolution.y, res.bitDepth ); } DefineEngineStaticMethod( GFXInit, getAdapterCount, S32, (),, "Return the number of graphics adapters available. @ingroup GFX") { return GFXInit::getAdapterCount(); } DefineEngineStaticMethod( GFXInit, getAdapterName, String, ( S32 index ),, "Returns the name of the graphics adapter.\n" "@param index The index of the adapter." ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if(index >= 0 && index < adapters.size()) return adapters[index]->mName; Con::errorf( "GFXInit::getAdapterName - Out of range adapter index." ); return String::EmptyString; } DefineEngineStaticMethod( GFXInit, getAdapterOutputName, String, ( S32 index ),, "Returns the name of the graphics adapter's output display device.\n" "@param index The index of the adapter." ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if(index >= 0 && index < adapters.size()) return adapters[index]->mOutputName; Con::errorf( "GFXInit::getAdapterOutputName - Out of range adapter index." ); return String::EmptyString; } DefineEngineStaticMethod( GFXInit, getAdapterType, GFXAdapterType, ( S32 index ),, "Returns the type (D3D11, GL, Null) of a graphics adapter.\n" "@param index The index of the adapter." ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if( index >= 0 && index < adapters.size()) return adapters[index]->mType; Con::errorf( "GFXInit::getAdapterType - Out of range adapter index." ); return GFXAdapterType_Count; } DefineEngineStaticMethod( GFXInit, getAdapterShaderModel, F32, ( S32 index ),, "Returns the supported shader model of the graphics adapter or -1 if the index is bad.\n" "@param index The index of the adapter." ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if(index < 0 || index >= adapters.size()) { Con::errorf("GFXInit::getAdapterShaderModel - Out of range adapter index."); return -1.0f; } return adapters[index]->mShaderModel; } DefineEngineStaticMethod( GFXInit, getDefaultAdapterIndex, S32, (),, "Returns the index of the default graphics adapter. This is the graphics device " "which will be used to initialize the engine." ) { GFXAdapter *a = GFXInit::getBestAdapterChoice(); // We have to find the index of the adapter in the list to // return an index that makes sense to the script. Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); for(S32 i=0; imIndex == a->mIndex && adapters[i]->mType == a->mType ) return i; Con::warnf( "GFXInit::getDefaultAdapterIndex - Didn't find the adapter in the list!" ); return -1; } DefineEngineStaticMethod( GFXInit, getAdapterModeCount, S32, ( S32 index ),, "Gets the number of modes available on the specified adapter.\n\n" "@param index Index of the adapter to get modes from.\n" "@return The number of video modes supported by the adapter or -1 if the given adapter was not found." ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if( index < 0 || index >= adapters.size()) { Con::warnf( "GFXInit::getAdapterModeCount - The index was out of range." ); return -1; } return adapters[index]->mAvailableModes.size(); } DefineEngineStaticMethod( GFXInit, getAdapterMode, String, ( S32 index, S32 modeIndex ),, "Gets the details of the specified adapter mode.\n\n" "@param index Index of the adapter to query.\n" "@param modeIndex Index of the mode to get data from.\n" "@return A video mode string in the format 'width height fullscreen bitDepth refreshRate aaLevel'.\n" "@see GuiCanvas::getVideoMode()" ) { Vector adapters( __FILE__, __LINE__ ); GFXInit::getAdapters(&adapters); if ( index < 0 || index >= adapters.size() ) { Con::warnf( "GFXInit::getAdapterMode - The adapter index was out of range." ); return String::EmptyString; } if ( modeIndex < 0 || modeIndex >= adapters[index]->mAvailableModes.size() ) { Con::warnf( "GFXInit::getAdapterMode - The mode index was out of range." ); return String::EmptyString; } const GFXVideoMode &vm = adapters[index]->mAvailableModes[modeIndex]; return vm.toString(); } DefineEngineStaticMethod( GFXInit, createNullDevice, void, (),, "Create the NULL graphics device used for testing or headless operation." ) { // Enumerate things for GFX before we have an active device. GFXInit::enumerateAdapters(); // Create a device. GFXAdapter *a = GFXInit::chooseAdapter(NullDevice, ""); GFXDevice *newDevice = GFX; // Do we have a global device already? (This is the site if you want // to start rendering to multiple devices simultaneously) if(newDevice == NULL) newDevice = GFXInit::createDevice(a); newDevice->setAllowRender( false ); }