llfeaturemanager.cpp

Go to the documentation of this file.
00001 
00032 #include <iostream>
00033 #include <fstream>
00034 
00035 #include "llviewerprecompiledheaders.h"
00036 
00037 #include <boost/regex.hpp>
00038 
00039 #include "llfeaturemanager.h"
00040 #include "lldir.h"
00041 
00042 #include "llsys.h"
00043 #include "llgl.h"
00044 #include "llsecondlifeurls.h"
00045 
00046 #include "llviewercontrol.h"
00047 #include "llworld.h"
00048 #include "lldrawpoolterrain.h"
00049 #include "llviewerimagelist.h"
00050 #include "llwindow.h"
00051 #include "llui.h"
00052 #include "llcontrol.h"
00053 #include "llboost.h"
00054 #include "llweb.h"
00055 
00056 #if LL_WINDOWS
00057 #include "lldxhardware.h"
00058 #endif
00059 
00060 //
00061 // externs
00062 //
00063 extern LLMemoryInfo gSysMemory;
00064 extern LLCPUInfo gSysCPU;
00065 
00066 #if LL_DARWIN
00067 const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
00068 #elif LL_LINUX
00069 const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
00070 #elif LL_SOLARIS
00071 const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
00072 #else
00073 const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
00074 #endif
00075 
00076 const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
00077 
00078 LLFeatureInfo::LLFeatureInfo(const char *name, const BOOL available, const F32 level) : mValid(TRUE)
00079 {
00080         mName = name;
00081         mAvailable = available;
00082         mRecommendedLevel = level;
00083 }
00084 
00085 LLFeatureList::LLFeatureList(const char *name)
00086 {
00087         mName = name;
00088 }
00089 
00090 LLFeatureList::~LLFeatureList()
00091 {
00092 }
00093 
00094 void LLFeatureList::addFeature(const char *name, const BOOL available, const F32 level)
00095 {
00096         if (mFeatures.count(name))
00097         {
00098                 LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
00099         }
00100 
00101         LLFeatureInfo fi(name, available, level);
00102         mFeatures[name] = fi;
00103 }
00104 
00105 BOOL LLFeatureList::isFeatureAvailable(const char *name)
00106 {
00107         if (mFeatures.count(name))
00108         {
00109                 return mFeatures[name].mAvailable;
00110         }
00111 
00112         LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
00113         
00114         // changing this to TRUE so you have to explicitly disable 
00115         // something for it to be disabled
00116         return TRUE;
00117 }
00118 
00119 F32 LLFeatureList::getRecommendedValue(const char *name)
00120 {
00121         if (mFeatures.count(name) && isFeatureAvailable(name))
00122         {
00123                 return mFeatures[name].mRecommendedLevel;
00124         }
00125 
00126         LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
00127         return 0;
00128 }
00129 
00130 BOOL LLFeatureList::maskList(LLFeatureList &mask)
00131 {
00132         //llinfos << "Masking with " << mask.mName << llendl;
00133         //
00134         // Lookup the specified feature mask, and overlay it on top of the
00135         // current feature mask.
00136         //
00137 
00138         LLFeatureInfo mask_fi;
00139 
00140         feature_map_t::iterator feature_it;
00141         for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
00142         {
00143                 mask_fi = feature_it->second;
00144                 //
00145                 // Look for the corresponding feature
00146                 //
00147                 if (!mFeatures.count(mask_fi.mName))
00148                 {
00149                         LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
00150                         continue;
00151                 }
00152 
00153                 LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
00154                 if (mask_fi.mAvailable && !cur_fi.mAvailable)
00155                 {
00156                         LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
00157                         continue;
00158                 }
00159                 cur_fi.mAvailable = mask_fi.mAvailable;
00160                 cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
00161                 LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
00162                                 << " Feature " << mask_fi.mName
00163                                 << " Mask: " << mask_fi.mRecommendedLevel
00164                                 << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
00165         }
00166 
00167         LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
00168                 // Will conditionally call dump only if the above message will be logged, thanks 
00169                 // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
00170                 dump();
00171         LL_CONT << LL_ENDL;
00172 
00173         return TRUE;
00174 }
00175 
00176 void LLFeatureList::dump()
00177 {
00178         LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
00179         LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL;
00180 
00181         LLFeatureInfo fi;
00182         feature_map_t::iterator feature_it;
00183         for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
00184         {
00185                 fi = feature_it->second;
00186                 LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
00187         }
00188         LL_DEBUGS("RenderInit") << LL_ENDL;
00189 }
00190 
00191 LLFeatureList *LLFeatureManager::findMask(const char *name)
00192 {
00193         if (mMaskList.count(name))
00194         {
00195                 return mMaskList[name];
00196         }
00197 
00198         return NULL;
00199 }
00200 
00201 BOOL LLFeatureManager::maskFeatures(const char *name)
00202 {
00203         LLFeatureList *maskp = findMask(name);
00204         if (!maskp)
00205         {
00206                 LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
00207                 return FALSE;
00208         }
00209         LL_DEBUGS("RenderInit") << "Applying Feature Mask: " << name << LL_ENDL;
00210         return maskList(*maskp);
00211 }
00212 
00213 BOOL LLFeatureManager::loadFeatureTables()
00214 {
00215         // *TODO - if I or anyone else adds something else to the skipped list
00216         // make this data driven.  Put it in the feature table and parse it
00217         // correctly
00218         mSkippedFeatures.insert("RenderAnisotropic");
00219         mSkippedFeatures.insert("RenderGamma");
00220         mSkippedFeatures.insert("RenderVBOEnable");
00221         mSkippedFeatures.insert("RenderFogRatio");
00222 
00223         std::string data_path = gDirUtilp->getAppRODataDir();
00224 
00225         data_path += gDirUtilp->getDirDelimiter();
00226 
00227         data_path += FEATURE_TABLE_FILENAME;
00228 
00229 
00230         char    name[MAX_STRING+1];      /*Flawfinder: ignore*/
00231 
00232         llifstream file;
00233         U32             version;
00234         
00235         file.open(data_path.c_str());    /*Flawfinder: ignore*/
00236 
00237         if (!file)
00238         {
00239                 LL_WARNS("RenderInit") << "Unable to open feature table!" << LL_ENDL;
00240                 return FALSE;
00241         }
00242 
00243         // Check file version
00244         file >> name;
00245         file >> version;
00246         if (strcmp(name, "version"))
00247         {
00248                 LL_WARNS("RenderInit") << data_path << " does not appear to be a valid feature table!" << LL_ENDL;
00249                 return FALSE;
00250         }
00251 
00252         mTableVersion = version;
00253 
00254         LLFeatureList *flp = NULL;
00255         while (!file.eof())
00256         {
00257                 char buffer[MAX_STRING];                 /*Flawfinder: ignore*/
00258                 name[0] = 0;
00259 
00260                 file >> name;
00261                 
00262                 if (strlen(name) >= 2 &&         /*Flawfinder: ignore*/
00263                         name[0] == '/' && 
00264                         name[1] == '/')
00265                 {
00266                         // This is a comment.
00267                         file.getline(buffer, MAX_STRING);
00268                         continue;
00269                 }
00270 
00271                 if (strlen(name) == 0)           /*Flawfinder: ignore*/
00272                 {
00273                         // This is a blank line
00274                         file.getline(buffer, MAX_STRING);
00275                         continue;
00276                 }
00277 
00278                 if (!strcmp(name, "list"))
00279                 {
00280                         if (flp)
00281                         {
00282                                 //flp->dump();
00283                         }
00284                         // It's a new mask, create it.
00285                         file >> name;
00286                         if (mMaskList.count(name))
00287                         {
00288                                 LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL;
00289                         }
00290 
00291                         flp = new LLFeatureList(name);
00292                         mMaskList[name] = flp;
00293                 }
00294                 else
00295                 {
00296                         if (!flp)
00297                         {
00298                                 LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
00299                         }
00300                         S32 available;
00301                         F32 recommended;
00302                         file >> available >> recommended;
00303                         flp->addFeature(name, available, recommended);
00304                 }
00305         }
00306         file.close();
00307 
00308         return TRUE;
00309 }
00310 
00311 void LLFeatureManager::loadGPUClass()
00312 {
00313         std::string data_path = gDirUtilp->getAppRODataDir();
00314 
00315         data_path += gDirUtilp->getDirDelimiter();
00316 
00317         data_path += GPU_TABLE_FILENAME;
00318 
00319         // defaults
00320         mGPUClass = GPU_CLASS_UNKNOWN;
00321         mGPUString = gGLManager.getRawGLString();
00322         mGPUSupported = FALSE;
00323 
00324         llifstream file;
00325                 
00326         file.open(data_path.c_str());            /*Flawfinder: ignore*/
00327 
00328         if (!file)
00329         {
00330                 LL_WARNS("RenderInit") << "Unable to open GPU table: " << data_path << "!" << LL_ENDL;
00331                 return;
00332         }
00333 
00334         std::string renderer = gGLManager.getRawGLString();
00335         for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
00336         {
00337                 *i = tolower(*i);
00338         }
00339         
00340         while (!file.eof())
00341         {
00342                 char buffer[MAX_STRING];                 /*Flawfinder: ignore*/
00343                 buffer[0] = 0;
00344 
00345                 file.getline(buffer, MAX_STRING);
00346                 
00347                 if (strlen(buffer) >= 2 &&       /*Flawfinder: ignore*/
00348                         buffer[0] == '/' && 
00349                         buffer[1] == '/')
00350                 {
00351                         // This is a comment.
00352                         continue;
00353                 }
00354 
00355                 if (strlen(buffer) == 0)         /*Flawfinder: ignore*/
00356                 {
00357                         // This is a blank line
00358                         continue;
00359                 }
00360 
00361                 // setup the tokenizer
00362                 std::string buf(buffer);
00363                 std::string cls, label, expr, supported;
00364                 boost_tokenizer tokens(buf, boost::char_separator<char>("\t\n"));
00365                 boost_tokenizer::iterator token_iter = tokens.begin();
00366 
00367                 // grab the label, pseudo regular expression, and class
00368                 if(token_iter != tokens.end())
00369                 {
00370                         label = *token_iter++;
00371                 }
00372                 if(token_iter != tokens.end())
00373                 {
00374                         expr = *token_iter++;
00375                 }
00376                 if(token_iter != tokens.end())
00377                 {
00378                         cls = *token_iter++;
00379                 }
00380                 if(token_iter != tokens.end())
00381                 {
00382                         supported = *token_iter++;
00383                 }
00384 
00385                 if (label.empty() || expr.empty() || cls.empty() || supported.empty())
00386                 {
00387                         continue;
00388                 }
00389         
00390                 for (U32 i = 0; i < expr.length(); i++)  /*Flawfinder: ignore*/
00391                 {
00392                         expr[i] = tolower(expr[i]);
00393                 }
00394 
00395                 // run the regular expression against the renderer
00396                 boost::regex re(expr.c_str());
00397                 if(boost::regex_search(renderer, re))
00398                 {
00399                         // if we found it, stop!
00400                         file.close();
00401                         LL_INFOS("RenderInit") << "GPU is " << label << llendl;
00402                         mGPUString = label;
00403                         mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10);
00404                         mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10);
00405                         file.close();
00406                         return;
00407                 }
00408         }
00409         file.close();
00410 
00411         LL_WARNS("RenderInit") << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << LL_ENDL;
00412 }
00413 
00414 void LLFeatureManager::cleanupFeatureTables()
00415 {
00416         std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
00417         mMaskList.clear();
00418 }
00419 
00420 void LLFeatureManager::init()
00421 {
00422         // load the tables
00423         loadFeatureTables();
00424 
00425         // get the gpu class
00426         loadGPUClass();
00427 
00428         // apply the base masks, so we know if anything is disabled
00429         applyBaseMasks();
00430 }
00431 
00432 void LLFeatureManager::applyRecommendedSettings()
00433 {
00434         // apply saved settings
00435         // cap the level at 2 (high)
00436         S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2));
00437 
00438         llinfos << "Applying Recommended Features" << llendl;
00439 
00440         setGraphicsLevel(level, false);
00441         gSavedSettings.setU32("RenderQualityPerformance", level);
00442         gSavedSettings.setBOOL("RenderCustomSettings", FALSE);
00443 
00444         // now apply the tweaks to draw distance
00445         // these are double negatives, because feature masks only work by
00446         // downgrading values, so i needed to make a true value go to false
00447         // for certain cards, thus the awkward name, "Disregard..."
00448         if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
00449         {
00450                 gSavedSettings.setF32("RenderFarClip", 96.0f);
00451         }
00452         else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
00453         {
00454                 gSavedSettings.setF32("RenderFarClip", 128.0f);
00455         }
00456 
00457 
00458 }
00459 
00460 void LLFeatureManager::applyFeatures(bool skipFeatures)
00461 {
00462         // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
00463 
00464 #ifndef LL_RELEASE_FOR_DOWNLOAD
00465         dump();
00466 #endif
00467 
00468         // scroll through all of these and set their corresponding control value
00469         for(feature_map_t::iterator mIt = mFeatures.begin(); 
00470                 mIt != mFeatures.end(); 
00471                 ++mIt)
00472         {
00473                 // skip features you want to skip
00474                 // do this for when you don't want to change certain settings
00475                 if(skipFeatures)
00476                 {
00477                         if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
00478                         {
00479                                 continue;
00480                         }
00481                 }
00482 
00483                 // get the control setting
00484                 LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
00485                 if(ctrl == NULL)
00486                 {
00487                         llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl;
00488                         continue;
00489                 }
00490 
00491                 // handle all the different types
00492                 if(ctrl->isType(TYPE_BOOLEAN))
00493                 {
00494                         gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first.c_str()));
00495                 }
00496                 else if (ctrl->isType(TYPE_S32))
00497                 {
00498                         gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first.c_str()));
00499                 }
00500                 else if (ctrl->isType(TYPE_U32))
00501                 {
00502                         gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first.c_str()));
00503                 }
00504                 else if (ctrl->isType(TYPE_F32))
00505                 {
00506                         gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first.c_str()));
00507                 }
00508                 else
00509                 {
00510                         llwarns << "AHHH! Control variable is not a numeric type!" << llendl;
00511                 }
00512         }
00513 }
00514 
00515 void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures)
00516 {
00517         applyBaseMasks();
00518 
00519         switch (level)
00520         {
00521                 case 0:
00522                         maskFeatures("Low");                    
00523                         break;
00524                 case 1:
00525                         maskFeatures("Mid");
00526                         break;
00527                 case 2:
00528                         maskFeatures("High");
00529                         break;
00530                 case 3:
00531                         maskFeatures("Ultra");
00532                         break;
00533                 default:
00534                         maskFeatures("Low");
00535                         break;
00536         }
00537 
00538         applyFeatures(skipFeatures);
00539 }
00540 
00541 void LLFeatureManager::applyBaseMasks()
00542 {
00543         // reapply masks
00544         mFeatures.clear();
00545 
00546         LLFeatureList* maskp = findMask("all");
00547         if(maskp == NULL)
00548         {
00549                 LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL;
00550                 return;
00551         }
00552 
00553         mFeatures = maskp->getFeatures();
00554 
00555         // mask class
00556         if (mGPUClass >= 0 && mGPUClass < 4)
00557         {
00558                 const char* class_table[] =
00559                 {
00560                         "Class0",
00561                         "Class1",
00562                         "Class2",
00563                         "Class3"
00564                 };
00565 
00566                 LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
00567                 maskFeatures(class_table[mGPUClass]);
00568         }
00569         else
00570         {
00571                 LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
00572                 maskFeatures("Unknown");
00573         }
00574 
00575         // now all those wacky ones
00576         if (!gGLManager.mHasFragmentShader)
00577         {
00578                 maskFeatures("NoPixelShaders");
00579         }
00580         if (!gGLManager.mHasVertexShader)
00581         {
00582                 maskFeatures("NoVertexShaders");
00583         }
00584         if (gGLManager.mIsNVIDIA)
00585         {
00586                 maskFeatures("NVIDIA");
00587         }
00588         if (gGLManager.mIsGF2or4MX)
00589         {
00590                 maskFeatures("GeForce2");
00591         }
00592         if (gGLManager.mIsATI)
00593         {
00594                 maskFeatures("ATI");
00595         }
00596         if (gGLManager.mIsGFFX)
00597         {
00598                 maskFeatures("GeForceFX");
00599         }
00600         if (gGLManager.mIsIntel)
00601         {
00602                 maskFeatures("Intel");
00603         }
00604         if (gGLManager.mGLVersion < 1.5f)
00605         {
00606                 maskFeatures("OpenGLPre15");
00607         }
00608 
00609         // now mask by gpu string
00610         // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
00611         std::string gpustr = mGPUString;
00612         for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
00613         {
00614                 if (*iter == ' ')
00615                 {
00616                         *iter = '_';
00617                 }
00618         }
00619 
00620         //llinfos << "Masking features from gpu table match: " << gpustr << llendl;
00621         maskFeatures(gpustr.c_str());
00622 
00623         // now mask cpu type ones
00624         if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
00625         {
00626                 maskFeatures("RAM256MB");
00627         }
00628         
00629 #if LL_SOLARIS && defined(__sparc)      //  even low MHz SPARCs are fast
00630 #error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
00631         if (gSysCPU.getMhz() < 800)
00632 #else
00633         if (gSysCPU.getMhz() < 1100)
00634 #endif
00635         {
00636                 maskFeatures("CPUSlow");
00637         }
00638 
00639         if (isSafe())
00640         {
00641                 maskFeatures("safe");
00642         }
00643 }

Generated on Fri May 16 08:33:21 2008 for SecondLife by  doxygen 1.5.5