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
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
00115
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
00133
00134
00135
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
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
00169
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
00216
00217
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];
00231
00232 llifstream file;
00233 U32 version;
00234
00235 file.open(data_path.c_str());
00236
00237 if (!file)
00238 {
00239 LL_WARNS("RenderInit") << "Unable to open feature table!" << LL_ENDL;
00240 return FALSE;
00241 }
00242
00243
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];
00258 name[0] = 0;
00259
00260 file >> name;
00261
00262 if (strlen(name) >= 2 &&
00263 name[0] == '/' &&
00264 name[1] == '/')
00265 {
00266
00267 file.getline(buffer, MAX_STRING);
00268 continue;
00269 }
00270
00271 if (strlen(name) == 0)
00272 {
00273
00274 file.getline(buffer, MAX_STRING);
00275 continue;
00276 }
00277
00278 if (!strcmp(name, "list"))
00279 {
00280 if (flp)
00281 {
00282
00283 }
00284
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
00320 mGPUClass = GPU_CLASS_UNKNOWN;
00321 mGPUString = gGLManager.getRawGLString();
00322 mGPUSupported = FALSE;
00323
00324 llifstream file;
00325
00326 file.open(data_path.c_str());
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];
00343 buffer[0] = 0;
00344
00345 file.getline(buffer, MAX_STRING);
00346
00347 if (strlen(buffer) >= 2 &&
00348 buffer[0] == '/' &&
00349 buffer[1] == '/')
00350 {
00351
00352 continue;
00353 }
00354
00355 if (strlen(buffer) == 0)
00356 {
00357
00358 continue;
00359 }
00360
00361
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
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++)
00391 {
00392 expr[i] = tolower(expr[i]);
00393 }
00394
00395
00396 boost::regex re(expr.c_str());
00397 if(boost::regex_search(renderer, re))
00398 {
00399
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
00423 loadFeatureTables();
00424
00425
00426 loadGPUClass();
00427
00428
00429 applyBaseMasks();
00430 }
00431
00432 void LLFeatureManager::applyRecommendedSettings()
00433 {
00434
00435
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
00445
00446
00447
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
00463
00464 #ifndef LL_RELEASE_FOR_DOWNLOAD
00465 dump();
00466 #endif
00467
00468
00469 for(feature_map_t::iterator mIt = mFeatures.begin();
00470 mIt != mFeatures.end();
00471 ++mIt)
00472 {
00473
00474
00475 if(skipFeatures)
00476 {
00477 if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
00478 {
00479 continue;
00480 }
00481 }
00482
00483
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
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
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
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
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
00610
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
00621 maskFeatures(gpustr.c_str());
00622
00623
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 }