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
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
00130
00131
00132
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
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
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];
00220
00221 llifstream file;
00222 U32 version;
00223
00224 file.open(data_path.c_str());
00225
00226 if (!file)
00227 {
00228 llwarns << "Unable to open feature table!" << llendl;
00229 return FALSE;
00230 }
00231
00232
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];
00247 name[0] = 0;
00248
00249 file >> name;
00250
00251 if (strlen(name) >= 2 &&
00252 name[0] == '/' &&
00253 name[1] == '/')
00254 {
00255
00256 file.getline(buffer, MAX_STRING);
00257 continue;
00258 }
00259
00260 if (strlen(name) == 0)
00261 {
00262
00263 file.getline(buffer, MAX_STRING);
00264 continue;
00265 }
00266
00267 if (!strcmp(name, "list"))
00268 {
00269 if (flp)
00270 {
00271
00272 }
00273
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
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
00319 mGPUClass = 0;
00320 mGPUString = gGLManager.getRawGLString();
00321
00322 llifstream file;
00323
00324 file.open(data_path.c_str());
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];
00341 buffer[0] = 0;
00342
00343 file.getline(buffer, MAX_STRING);
00344
00345 if (strlen(buffer) >= 2 &&
00346 buffer[0] == '/' &&
00347 buffer[1] == '/')
00348 {
00349
00350 continue;
00351 }
00352
00353 if (strlen(buffer) == 0)
00354 {
00355
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++)
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
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
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
00501
00502 llinfos << "Applying Recommended Features" << llendl;
00503 #ifndef LL_RELEASE_FOR_DOWNLOAD
00504 dump();
00505 #endif
00506
00507
00508 if (getRecommendedLevel("RenderVBO"))
00509 {
00510 gSavedSettings.setBOOL("RenderVBOEnable", TRUE);
00511 }
00512 else
00513 {
00514 gSavedSettings.setBOOL("RenderVBOEnable", FALSE);
00515 }
00516
00517
00518 BOOL aniso = getRecommendedLevel("RenderAniso");
00519 LLImageGL::sGlobalUseAnisotropic = aniso;
00520 gSavedSettings.setBOOL("RenderAnisotropic", LLImageGL::sGlobalUseAnisotropic);
00521
00522
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
00531 S32 far_clip = getRecommendedLevel("RenderDistance");
00532 gSavedSettings.setF32("RenderFarClip", (F32)far_clip);
00533
00534
00535 S32 lighting = getRecommendedLevel("RenderLighting");
00536 gSavedSettings.setS32("RenderLightingDetail", lighting);
00537
00538
00539 BOOL bump = getRecommendedLevel("RenderObjectBump");
00540 gSavedSettings.setBOOL("RenderObjectBump", bump);
00541
00542
00543 S32 max_parts = getRecommendedLevel("RenderParticleCount");
00544 gSavedSettings.setS32("RenderMaxPartCount", max_parts);
00545 LLViewerPartSim::setMaxPartCount(max_parts);
00546
00547
00548 BOOL ripple = getRecommendedLevel("RenderRippleWater");
00549 gSavedSettings.setBOOL("RenderRippleWater", ripple);
00550
00551
00552 BOOL occlusion = getRecommendedLevel("UseOcclusion");
00553 gSavedSettings.setBOOL("UseOcclusion", occlusion);
00554
00555
00556 S32 shaders = getRecommendedLevel("VertexShaderEnable");
00557 gSavedSettings.setBOOL("VertexShaderEnable", shaders);
00558
00559
00560 S32 terrain = getRecommendedLevel("RenderTerrainDetail");
00561 gSavedSettings.setS32("RenderTerrainDetail", terrain);
00562 LLDrawPoolTerrain::sDetailMode = terrain;
00563
00564
00565 if (isSafe())
00566 {
00567 gSavedSettings.setS32("GraphicsCardMemorySetting", 1);
00568 }
00569 else
00570 {
00571 S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
00572
00573 if (idx != -1)
00574 {
00575 idx = LLViewerImageList::getMaxVideoRamSetting(-2);
00576 gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
00577 }
00578 }
00579 }