llfeaturemanager.cpp

Go to the documentation of this file.
00001 
00032 #include <iostream>
00033 #include <fstream>
00034 
00035 #include "llviewerprecompiledheaders.h"
00036 
00037 #include "llfeaturemanager.h"
00038 #include "lldir.h"
00039 
00040 #include "llsys.h"
00041 #include "llgl.h"
00042 #include "llsecondlifeurls.h"
00043 
00044 #include "llviewercontrol.h"
00045 #include "llworld.h"
00046 #include "pipeline.h"
00047 #include "lldrawpoolterrain.h"
00048 #include "llviewerimagelist.h"
00049 #include "llwindow.h"
00050 #include "llui.h"
00051 
00052 #if LL_WINDOWS
00053 #include "lldxhardware.h"
00054 #endif
00055 
00056 //
00057 // externs
00058 //
00059 extern LLMemoryInfo gSysMemory;
00060 extern LLCPUInfo gSysCPU;
00061 extern void write_debug(const char *str);
00062 extern void write_debug(const std::string& str);
00063 
00064 #if LL_DARWIN
00065 const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
00066 #elif LL_LINUX
00067 const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
00068 #elif LL_SOLARIS
00069 const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
00070 #else
00071 const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
00072 #endif
00073 
00074 const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
00075 
00076 LLFeatureManager *gFeatureManagerp = NULL;
00077 
00078 LLFeatureInfo::LLFeatureInfo(const char *name, const BOOL available, const S32 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 S32 level)
00095 {
00096         if (mFeatures.count(name))
00097         {
00098                 llwarns << "LLFeatureList::Attempting to add preexisting feature " << name << llendl;
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         llwarns << "Feature " << name << " not on feature list!" << llendl;
00113         return FALSE;
00114 }
00115 
00116 S32 LLFeatureList::getRecommendedLevel(const char *name)
00117 {
00118         if (mFeatures.count(name))
00119         {
00120                 return mFeatures[name].mRecommendedLevel;
00121         }
00122 
00123         llwarns << "Feature " << name << " not on feature list!" << llendl;
00124         return -1;
00125 }
00126 
00127 BOOL LLFeatureList::maskList(LLFeatureList &mask)
00128 {
00129         //llinfos << "Masking with " << mask.mName << llendl;
00130         //
00131         // Lookup the specified feature mask, and overlay it on top of the
00132         // current feature mask.
00133         //
00134 
00135         LLFeatureInfo mask_fi;
00136 
00137         feature_map_t::iterator feature_it;
00138         for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
00139         {
00140                 mask_fi = feature_it->second;
00141                 //
00142                 // Look for the corresponding feature
00143                 //
00144                 if (!mFeatures.count(mask_fi.mName))
00145                 {
00146                         llwarns << "Feature " << mask_fi.mName << " in mask not in top level!" << llendl;
00147                         continue;
00148                 }
00149 
00150                 LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
00151                 if (mask_fi.mAvailable && !cur_fi.mAvailable)
00152                 {
00153                         llwarns << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << llendl;
00154                         continue;
00155                 }
00156                 cur_fi.mAvailable = mask_fi.mAvailable;
00157                 cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
00158 #ifndef LL_RELEASE_FOR_DOWNLOAD
00159                 llinfos << "Feature mask " << mask.mName
00160                                 << " Feature " << mask_fi.mName
00161                                 << " Mask: " << mask_fi.mRecommendedLevel
00162                                 << " Now: " << cur_fi.mRecommendedLevel << llendl;
00163 #endif
00164         }
00165 
00166 #if 0 && !LL_RELEASE_FOR_DOWNLOAD
00167         llinfos << "After applying mask " << mask.mName << llendl;
00168         dump();
00169 #endif
00170         return TRUE;
00171 }
00172 
00173 void LLFeatureList::dump()
00174 {
00175         llinfos << "Feature list: " << mName << llendl;
00176         llinfos << "--------------" << llendl;
00177 
00178         LLFeatureInfo fi;
00179         feature_map_t::iterator feature_it;
00180         for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
00181         {
00182                 fi = feature_it->second;
00183                 llinfos << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << llendl;
00184         }
00185         llinfos << llendl;
00186 }
00187 
00188 LLFeatureList *LLFeatureManager::findMask(const char *name)
00189 {
00190         if (mMaskList.count(name))
00191         {
00192                 return mMaskList[name];
00193         }
00194 
00195         return NULL;
00196 }
00197 
00198 BOOL LLFeatureManager::maskFeatures(const char *name)
00199 {
00200         LLFeatureList *maskp = findMask(name);
00201         if (!maskp)
00202         {
00203 //              llwarns << "Unknown feature mask " << name << llendl;
00204                 return FALSE;
00205         }
00206         llinfos << "Applying Feature Mask: " << name << llendl;
00207         return maskList(*maskp);
00208 }
00209 
00210 BOOL LLFeatureManager::loadFeatureTables()
00211 {
00212         std::string data_path = gDirUtilp->getAppRODataDir();
00213 
00214         data_path += gDirUtilp->getDirDelimiter();
00215 
00216         data_path += FEATURE_TABLE_FILENAME;
00217 
00218 
00219         char    name[MAX_STRING+1];      /*Flawfinder: ignore*/
00220 
00221         llifstream file;
00222         U32             version;
00223         
00224         file.open(data_path.c_str());    /*Flawfinder: ignore*/
00225 
00226         if (!file)
00227         {
00228                 llwarns << "Unable to open feature table!" << llendl;
00229                 return FALSE;
00230         }
00231 
00232         // Check file version
00233         file >> name;
00234         file >> version;
00235         if (strcmp(name, "version"))
00236         {
00237                 llwarns << data_path << " does not appear to be a valid feature table!" << llendl;
00238                 return FALSE;
00239         }
00240 
00241         mTableVersion = version;
00242 
00243         LLFeatureList *flp = NULL;
00244         while (!file.eof())
00245         {
00246                 char buffer[MAX_STRING];                 /*Flawfinder: ignore*/
00247                 name[0] = 0;
00248 
00249                 file >> name;
00250                 
00251                 if (strlen(name) >= 2 &&         /*Flawfinder: ignore*/
00252                         name[0] == '/' && 
00253                         name[1] == '/')
00254                 {
00255                         // This is a comment.
00256                         file.getline(buffer, MAX_STRING);
00257                         continue;
00258                 }
00259 
00260                 if (strlen(name) == 0)           /*Flawfinder: ignore*/
00261                 {
00262                         // This is a blank line
00263                         file.getline(buffer, MAX_STRING);
00264                         continue;
00265                 }
00266 
00267                 if (!strcmp(name, "list"))
00268                 {
00269                         if (flp)
00270                         {
00271                                 //flp->dump();
00272                         }
00273                         // It's a new mask, create it.
00274                         file >> name;
00275                         if (mMaskList.count(name))
00276                         {
00277                                 llerrs << "Overriding mask " << name << ", this is invalid!" << llendl;
00278                         }
00279 
00280                         if (!flp)
00281                         {
00282                                 //
00283                                 // The first one is always the default
00284                                 //
00285                                 flp = this;
00286                         }
00287                         else
00288                         {
00289                                 flp = new LLFeatureList(name);
00290                                 mMaskList[name] = flp;
00291                         }
00292 
00293                 }
00294                 else
00295                 {
00296                         if (!flp)
00297                         {
00298                                 llerrs << "Specified parameter before <list> keyword!" << llendl;
00299                         }
00300                         S32 available, recommended;
00301                         file >> available >> recommended;
00302                         flp->addFeature(name, available, recommended);
00303                 }
00304         }
00305         file.close();
00306 
00307         return TRUE;
00308 }
00309 
00310 void LLFeatureManager::loadGPUClass()
00311 {
00312         std::string data_path = gDirUtilp->getAppRODataDir();
00313 
00314         data_path += gDirUtilp->getDirDelimiter();
00315 
00316         data_path += GPU_TABLE_FILENAME;
00317 
00318         // defaults
00319         mGPUClass = 0;
00320         mGPUString = gGLManager.getRawGLString();
00321         
00322         llifstream file;
00323                 
00324         file.open(data_path.c_str());            /*Flawfinder: ignore*/
00325 
00326         if (!file)
00327         {
00328                 llwarns << "Unable to open GPU table: " << data_path << "!" << llendl;
00329                 return;
00330         }
00331 
00332         std::string renderer = gGLManager.getRawGLString();
00333         for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
00334         {
00335                 *i = tolower(*i);
00336         }
00337         
00338         while (!file.eof())
00339         {
00340                 char buffer[MAX_STRING];                 /*Flawfinder: ignore*/
00341                 buffer[0] = 0;
00342 
00343                 file.getline(buffer, MAX_STRING);
00344                 
00345                 if (strlen(buffer) >= 2 &&       /*Flawfinder: ignore*/
00346                         buffer[0] == '/' && 
00347                         buffer[1] == '/')
00348                 {
00349                         // This is a comment.
00350                         continue;
00351                 }
00352 
00353                 if (strlen(buffer) == 0)         /*Flawfinder: ignore*/
00354                 {
00355                         // This is a blank line
00356                         continue;
00357                 }
00358 
00359                 char* cls, *label, *expr;
00360                 
00361                 label = strtok(buffer, "\t");
00362                 expr = strtok(NULL, "\t");
00363                 cls = strtok(NULL, "\t");
00364 
00365                 if (label == NULL || expr == NULL || cls == NULL)
00366                 {
00367                         continue;
00368                 }
00369         
00370                 for (U32 i = 0; i < strlen(expr); i++)   /*Flawfinder: ignore*/
00371                 {
00372                         expr[i] = tolower(expr[i]);
00373                 }
00374                 
00375                 char* ex = strtok(expr, ".*");
00376                 char* rnd = (char*) renderer.c_str();
00377                 
00378                 while (ex != NULL && rnd != NULL)
00379                 {
00380                         rnd = strstr(rnd, ex);
00381                         ex = strtok(NULL, ".*");
00382                 }
00383                 
00384                 if (rnd != NULL)
00385                 {
00386                         file.close();
00387                         llinfos << "GPU is " << label << llendl;
00388                         mGPUString = label;
00389                         mGPUClass = (S32) strtol(cls, NULL, 10);        
00390                         file.close();
00391                         return;
00392                 }
00393         }
00394         file.close();
00395 
00396         llwarns << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << llendl;
00397 }
00398 
00399 void LLFeatureManager::cleanupFeatureTables()
00400 {
00401         std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
00402         mMaskList.clear();
00403 }
00404 
00405 
00406 void LLFeatureManager::initCPUFeatureMasks()
00407 {
00408         if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
00409         {
00410                 maskFeatures("RAM256MB");
00411         }
00412         
00413 #if LL_SOLARIS && defined(__sparc)      //  even low MHz SPARCs are fast
00414 #error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
00415         if (gSysCPU.getMhz() < 800)
00416 #else
00417         if (gSysCPU.getMhz() < 1100)
00418 #endif
00419         {
00420                 maskFeatures("CPUSlow");
00421         }
00422         if (isSafe())
00423         {
00424                 maskFeatures("safe");
00425         }
00426 }
00427 
00428 void LLFeatureManager::initGraphicsFeatureMasks()
00429 {
00430         loadGPUClass();
00431         
00432         if (mGPUClass >= 0 && mGPUClass < 4)
00433         {
00434                 const char* class_table[] =
00435                 {
00436                         "Class0",
00437                         "Class1",
00438                         "Class2",
00439                         "Class3"
00440                 };
00441 
00442                 llinfos << "Setting GPU Class to " << class_table[mGPUClass] << llendl;
00443                 maskFeatures(class_table[mGPUClass]);
00444         }
00445         
00446         if (!gGLManager.mHasFragmentShader)
00447         {
00448                 maskFeatures("NoPixelShaders");
00449         }
00450         if (!gGLManager.mHasVertexShader)
00451         {
00452                 maskFeatures("NoVertexShaders");
00453         }
00454         if (gGLManager.mIsNVIDIA)
00455         {
00456                 maskFeatures("NVIDIA");
00457         }
00458         if (gGLManager.mIsGF2or4MX)
00459         {
00460                 maskFeatures("GeForce2");
00461         }
00462         if (gGLManager.mIsATI)
00463         {
00464                 maskFeatures("ATI");
00465         }
00466         if (gGLManager.mIsGFFX)
00467         {
00468                 maskFeatures("GeForceFX");
00469         }
00470         if (gGLManager.mIsIntel)
00471         {
00472                 maskFeatures("Intel");
00473         }
00474         if (gGLManager.mGLVersion < 1.5f)
00475         {
00476                 maskFeatures("OpenGLPre15");
00477         }
00478         // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
00479         std::string gpustr = mGPUString;
00480         for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
00481         {
00482                 if (*iter == ' ')
00483                 {
00484                         *iter = '_';
00485                 }
00486         }
00487 //      llinfos << "Masking features from gpu table match: " << gpustr << llendl;
00488         maskFeatures(gpustr.c_str());
00489 
00490         if (isSafe())
00491         {
00492                 maskFeatures("safe");
00493         }
00494 }
00495 
00496 extern LLOSInfo gSysOS;
00497 
00498 void LLFeatureManager::applyRecommendedFeatures()
00499 {
00500         // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
00501 
00502         llinfos << "Applying Recommended Features" << llendl;
00503 #ifndef LL_RELEASE_FOR_DOWNLOAD
00504         dump();
00505 #endif
00506         
00507         // Enabling VBO
00508         if (getRecommendedLevel("RenderVBO"))
00509         {
00510                 gSavedSettings.setBOOL("RenderVBOEnable", TRUE);
00511         }
00512         else
00513         {
00514                 gSavedSettings.setBOOL("RenderVBOEnable", FALSE);
00515         }
00516 
00517         // Anisotropic rendering
00518         BOOL aniso = getRecommendedLevel("RenderAniso");
00519         LLImageGL::sGlobalUseAnisotropic        = aniso;
00520         gSavedSettings.setBOOL("RenderAnisotropic", LLImageGL::sGlobalUseAnisotropic);
00521 
00522         // Render Avatar Mode
00523         BOOL avatar_vp = getRecommendedLevel("RenderAvatarVP");
00524         S32 avatar_mode = getRecommendedLevel("RenderAvatarMode");
00525         if (avatar_vp == FALSE)
00526                 avatar_mode = 0;
00527         gSavedSettings.setBOOL("RenderAvatarVP", avatar_vp);
00528         gSavedSettings.setS32("RenderAvatarMode", avatar_mode);
00529         
00530         // Render Distance
00531         S32 far_clip = getRecommendedLevel("RenderDistance");
00532         gSavedSettings.setF32("RenderFarClip", (F32)far_clip);
00533 
00534         // Lighting
00535         S32 lighting = getRecommendedLevel("RenderLighting");
00536         gSavedSettings.setS32("RenderLightingDetail", lighting);
00537 
00538         // ObjectBump
00539         BOOL bump = getRecommendedLevel("RenderObjectBump");
00540         gSavedSettings.setBOOL("RenderObjectBump", bump);
00541         
00542         // Particle Count
00543         S32 max_parts = getRecommendedLevel("RenderParticleCount");
00544         gSavedSettings.setS32("RenderMaxPartCount", max_parts);
00545         LLViewerPartSim::setMaxPartCount(max_parts);
00546 
00547         // RippleWater
00548         BOOL ripple = getRecommendedLevel("RenderRippleWater");
00549         gSavedSettings.setBOOL("RenderRippleWater", ripple);
00550 
00551         // Occlusion Culling
00552         BOOL occlusion = getRecommendedLevel("UseOcclusion");
00553         gSavedSettings.setBOOL("UseOcclusion", occlusion);
00554         
00555         // Vertex Shaders
00556         S32 shaders = getRecommendedLevel("VertexShaderEnable");
00557         gSavedSettings.setBOOL("VertexShaderEnable", shaders);
00558 
00559         // Terrain
00560         S32 terrain = getRecommendedLevel("RenderTerrainDetail");
00561         gSavedSettings.setS32("RenderTerrainDetail", terrain);
00562         LLDrawPoolTerrain::sDetailMode = terrain;
00563 
00564         // Set the amount of VRAM we have available
00565         if (isSafe())
00566         {
00567                 gSavedSettings.setS32("GraphicsCardMemorySetting", 1);  // 32 MB in 'safe' mode
00568         }
00569         else
00570         {
00571                 S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
00572                 // -1 indicates use default (max), don't change
00573                 if (idx != -1)
00574                 {
00575                         idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
00576                         gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
00577                 }
00578         }
00579 }

Generated on Thu Jul 1 06:08:30 2010 for Second Life Viewer by  doxygen 1.4.7