llfeaturemanager.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llfeaturemanager.cpp
  3.  * @brief LLFeatureManager class implementation
  4.  *
  5.  * $LicenseInfo:firstyear=2003&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2003-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include <iostream>
  34. #include <fstream>
  35. #include <boost/regex.hpp>
  36. #include "llfeaturemanager.h"
  37. #include "lldir.h"
  38. #include "llsys.h"
  39. #include "llgl.h"
  40. #include "llsecondlifeurls.h"
  41. #include "llappviewer.h"
  42. #include "llviewercontrol.h"
  43. #include "llworld.h"
  44. #include "lldrawpoolterrain.h"
  45. #include "llviewertexturelist.h"
  46. #include "llwindow.h"
  47. #include "llui.h"
  48. #include "llcontrol.h"
  49. #include "llboost.h"
  50. #include "llweb.h"
  51. #if LL_WINDOWS
  52. #include "lldxhardware.h"
  53. #endif
  54. #if LL_DARWIN
  55. const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
  56. #elif LL_LINUX
  57. const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
  58. #elif LL_SOLARIS
  59. const char FEATURE_TABLE_FILENAME[] = "featuretable_solaris.txt";
  60. #else
  61. const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
  62. #endif
  63. const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
  64. LLFeatureInfo::LLFeatureInfo(const std::string& name, const BOOL available, const F32 level)
  65. : mValid(TRUE), mName(name), mAvailable(available), mRecommendedLevel(level)
  66. {
  67. }
  68. LLFeatureList::LLFeatureList(const std::string& name)
  69. : mName(name)
  70. {
  71. }
  72. LLFeatureList::~LLFeatureList()
  73. {
  74. }
  75. void LLFeatureList::addFeature(const std::string& name, const BOOL available, const F32 level)
  76. {
  77. if (mFeatures.count(name))
  78. {
  79. LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
  80. }
  81. LLFeatureInfo fi(name, available, level);
  82. mFeatures[name] = fi;
  83. }
  84. BOOL LLFeatureList::isFeatureAvailable(const std::string& name)
  85. {
  86. if (mFeatures.count(name))
  87. {
  88. return mFeatures[name].mAvailable;
  89. }
  90. LL_WARNS("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
  91. // changing this to TRUE so you have to explicitly disable 
  92. // something for it to be disabled
  93. return TRUE;
  94. }
  95. F32 LLFeatureList::getRecommendedValue(const std::string& name)
  96. {
  97. if (mFeatures.count(name) && isFeatureAvailable(name))
  98. {
  99. return mFeatures[name].mRecommendedLevel;
  100. }
  101. LL_WARNS("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
  102. return 0;
  103. }
  104. BOOL LLFeatureList::maskList(LLFeatureList &mask)
  105. {
  106. //llinfos << "Masking with " << mask.mName << llendl;
  107. //
  108. // Lookup the specified feature mask, and overlay it on top of the
  109. // current feature mask.
  110. //
  111. LLFeatureInfo mask_fi;
  112. feature_map_t::iterator feature_it;
  113. for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
  114. {
  115. mask_fi = feature_it->second;
  116. //
  117. // Look for the corresponding feature
  118. //
  119. if (!mFeatures.count(mask_fi.mName))
  120. {
  121. LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
  122. continue;
  123. }
  124. LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
  125. if (mask_fi.mAvailable && !cur_fi.mAvailable)
  126. {
  127. LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
  128. continue;
  129. }
  130. cur_fi.mAvailable = mask_fi.mAvailable;
  131. cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
  132. LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
  133. << " Feature " << mask_fi.mName
  134. << " Mask: " << mask_fi.mRecommendedLevel
  135. << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
  136. }
  137. LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
  138. // Will conditionally call dump only if the above message will be logged, thanks 
  139. // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
  140. dump();
  141. LL_CONT << LL_ENDL;
  142. return TRUE;
  143. }
  144. void LLFeatureList::dump()
  145. {
  146. LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
  147. LL_DEBUGS("RenderInit") << "--------------" << LL_ENDL;
  148. LLFeatureInfo fi;
  149. feature_map_t::iterator feature_it;
  150. for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
  151. {
  152. fi = feature_it->second;
  153. LL_DEBUGS("RenderInit") << fi.mName << "tt" << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
  154. }
  155. LL_DEBUGS("RenderInit") << LL_ENDL;
  156. }
  157. LLFeatureList *LLFeatureManager::findMask(const std::string& name)
  158. {
  159. if (mMaskList.count(name))
  160. {
  161. return mMaskList[name];
  162. }
  163. return NULL;
  164. }
  165. BOOL LLFeatureManager::maskFeatures(const std::string& name)
  166. {
  167. LLFeatureList *maskp = findMask(name);
  168. if (!maskp)
  169. {
  170.   LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
  171. return FALSE;
  172. }
  173. LL_DEBUGS("RenderInit") << "Applying Feature Mask: " << name << LL_ENDL;
  174. return maskList(*maskp);
  175. }
  176. BOOL LLFeatureManager::loadFeatureTables()
  177. {
  178. // *TODO - if I or anyone else adds something else to the skipped list
  179. // make this data driven.  Put it in the feature table and parse it
  180. // correctly
  181. mSkippedFeatures.insert("RenderAnisotropic");
  182. mSkippedFeatures.insert("RenderGamma");
  183. mSkippedFeatures.insert("RenderVBOEnable");
  184. mSkippedFeatures.insert("RenderFogRatio");
  185. std::string data_path = gDirUtilp->getAppRODataDir();
  186. data_path += gDirUtilp->getDirDelimiter();
  187. data_path += FEATURE_TABLE_FILENAME;
  188. lldebugs << "Looking for feature table in " << data_path << llendl;
  189. llifstream file;
  190. std::string name;
  191. U32 version;
  192. file.open(data_path);   /*Flawfinder: ignore*/
  193. if (!file)
  194. {
  195. LL_WARNS("RenderInit") << "Unable to open feature table!" << LL_ENDL;
  196. return FALSE;
  197. }
  198. // Check file version
  199. file >> name;
  200. file >> version;
  201. if (name != "version")
  202. {
  203. LL_WARNS("RenderInit") << data_path << " does not appear to be a valid feature table!" << LL_ENDL;
  204. return FALSE;
  205. }
  206. mTableVersion = version;
  207. LLFeatureList *flp = NULL;
  208. while (!file.eof() && file.good())
  209. {
  210. char buffer[MAX_STRING];  /*Flawfinder: ignore*/
  211. file >> name;
  212. if (name.substr(0,2) == "//")
  213. {
  214. // This is a comment.
  215. file.getline(buffer, MAX_STRING);
  216. continue;
  217. }
  218. if (name.empty())
  219. {
  220. // This is a blank line
  221. file.getline(buffer, MAX_STRING);
  222. continue;
  223. }
  224. if (name == "list")
  225. {
  226. if (flp)
  227. {
  228. //flp->dump();
  229. }
  230. // It's a new mask, create it.
  231. file >> name;
  232. if (mMaskList.count(name))
  233. {
  234. LL_ERRS("RenderInit") << "Overriding mask " << name << ", this is invalid!" << LL_ENDL;
  235. }
  236. flp = new LLFeatureList(name);
  237. mMaskList[name] = flp;
  238. }
  239. else
  240. {
  241. if (!flp)
  242. {
  243. LL_ERRS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
  244. return FALSE;
  245. }
  246. S32 available;
  247. F32 recommended;
  248. file >> available >> recommended;
  249. flp->addFeature(name, available, recommended);
  250. }
  251. }
  252. file.close();
  253. return TRUE;
  254. }
  255. void LLFeatureManager::loadGPUClass()
  256. {
  257. std::string data_path = gDirUtilp->getAppRODataDir();
  258. data_path += gDirUtilp->getDirDelimiter();
  259. data_path += GPU_TABLE_FILENAME;
  260. // defaults
  261. mGPUClass = GPU_CLASS_UNKNOWN;
  262. mGPUString = gGLManager.getRawGLString();
  263. mGPUSupported = FALSE;
  264. llifstream file;
  265. file.open(data_path);   /*Flawfinder: ignore*/
  266. if (!file)
  267. {
  268. LL_WARNS("RenderInit") << "Unable to open GPU table: " << data_path << "!" << LL_ENDL;
  269. return;
  270. }
  271. std::string renderer = gGLManager.getRawGLString();
  272. for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
  273. {
  274. *i = tolower(*i);
  275. }
  276. while (!file.eof())
  277. {
  278. char buffer[MAX_STRING];  /*Flawfinder: ignore*/
  279. buffer[0] = 0;
  280. file.getline(buffer, MAX_STRING);
  281. if (strlen(buffer) >= 2 &&   /*Flawfinder: ignore*/
  282. buffer[0] == '/' && 
  283. buffer[1] == '/')
  284. {
  285. // This is a comment.
  286. continue;
  287. }
  288. if (strlen(buffer) == 0)  /*Flawfinder: ignore*/
  289. {
  290. // This is a blank line
  291. continue;
  292. }
  293. // setup the tokenizer
  294. std::string buf(buffer);
  295. std::string cls, label, expr, supported;
  296. boost_tokenizer tokens(buf, boost::char_separator<char>("tn"));
  297. boost_tokenizer::iterator token_iter = tokens.begin();
  298. // grab the label, pseudo regular expression, and class
  299. if(token_iter != tokens.end())
  300. {
  301. label = *token_iter++;
  302. }
  303. if(token_iter != tokens.end())
  304. {
  305. expr = *token_iter++;
  306. }
  307. if(token_iter != tokens.end())
  308. {
  309. cls = *token_iter++;
  310. }
  311. if(token_iter != tokens.end())
  312. {
  313. supported = *token_iter++;
  314. }
  315. if (label.empty() || expr.empty() || cls.empty() || supported.empty())
  316. {
  317. continue;
  318. }
  319. for (U32 i = 0; i < expr.length(); i++)  /*Flawfinder: ignore*/
  320. {
  321. expr[i] = tolower(expr[i]);
  322. }
  323. // run the regular expression against the renderer
  324. boost::regex re(expr.c_str());
  325. if(boost::regex_search(renderer, re))
  326. {
  327. // if we found it, stop!
  328. file.close();
  329. LL_INFOS("RenderInit") << "GPU is " << label << llendl;
  330. mGPUString = label;
  331. mGPUClass = (EGPUClass) strtol(cls.c_str(), NULL, 10);
  332. mGPUSupported = (BOOL) strtol(supported.c_str(), NULL, 10);
  333. file.close();
  334. return;
  335. }
  336. }
  337. file.close();
  338. LL_WARNS("RenderInit") << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << LL_ENDL;
  339. }
  340. void LLFeatureManager::cleanupFeatureTables()
  341. {
  342. std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
  343. mMaskList.clear();
  344. }
  345. void LLFeatureManager::init()
  346. {
  347. // load the tables
  348. loadFeatureTables();
  349. // get the gpu class
  350. loadGPUClass();
  351. // apply the base masks, so we know if anything is disabled
  352. applyBaseMasks();
  353. }
  354. void LLFeatureManager::applyRecommendedSettings()
  355. {
  356. // apply saved settings
  357. // cap the level at 2 (high)
  358. S32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_2));
  359. llinfos << "Applying Recommended Features" << llendl;
  360. setGraphicsLevel(level, false);
  361. gSavedSettings.setU32("RenderQualityPerformance", level);
  362. // now apply the tweaks to draw distance
  363. // these are double negatives, because feature masks only work by
  364. // downgrading values, so i needed to make a true value go to false
  365. // for certain cards, thus the awkward name, "Disregard..."
  366. if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
  367. {
  368. gSavedSettings.setF32("RenderFarClip", 96.0f);
  369. }
  370. else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
  371. {
  372. gSavedSettings.setF32("RenderFarClip", 128.0f);
  373. }
  374. }
  375. void LLFeatureManager::applyFeatures(bool skipFeatures)
  376. {
  377. // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
  378. #ifndef LL_RELEASE_FOR_DOWNLOAD
  379. dump();
  380. #endif
  381. // scroll through all of these and set their corresponding control value
  382. for(feature_map_t::iterator mIt = mFeatures.begin(); 
  383. mIt != mFeatures.end(); 
  384. ++mIt)
  385. {
  386. // skip features you want to skip
  387. // do this for when you don't want to change certain settings
  388. if(skipFeatures)
  389. {
  390. if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
  391. {
  392. continue;
  393. }
  394. }
  395. // get the control setting
  396. LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
  397. if(ctrl == NULL)
  398. {
  399. llwarns << "AHHH! Control setting " << mIt->first << " does not exist!" << llendl;
  400. continue;
  401. }
  402. // handle all the different types
  403. if(ctrl->isType(TYPE_BOOLEAN))
  404. {
  405. gSavedSettings.setBOOL(mIt->first, (BOOL)getRecommendedValue(mIt->first));
  406. }
  407. else if (ctrl->isType(TYPE_S32))
  408. {
  409. gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first));
  410. }
  411. else if (ctrl->isType(TYPE_U32))
  412. {
  413. gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first));
  414. }
  415. else if (ctrl->isType(TYPE_F32))
  416. {
  417. gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first));
  418. }
  419. else
  420. {
  421. llwarns << "AHHH! Control variable is not a numeric type!" << llendl;
  422. }
  423. }
  424. }
  425. void LLFeatureManager::setGraphicsLevel(S32 level, bool skipFeatures)
  426. {
  427. applyBaseMasks();
  428. switch (level)
  429. {
  430. case 0:
  431. maskFeatures("Low");
  432. break;
  433. case 1:
  434. maskFeatures("Mid");
  435. break;
  436. case 2:
  437. maskFeatures("High");
  438. break;
  439. case 3:
  440. maskFeatures("Ultra");
  441. break;
  442. default:
  443. maskFeatures("Low");
  444. break;
  445. }
  446. applyFeatures(skipFeatures);
  447. }
  448. void LLFeatureManager::applyBaseMasks()
  449. {
  450. // reapply masks
  451. mFeatures.clear();
  452. LLFeatureList* maskp = findMask("all");
  453. if(maskp == NULL)
  454. {
  455. LL_WARNS("RenderInit") << "AHH! No "all" in feature table!" << LL_ENDL;
  456. return;
  457. }
  458. mFeatures = maskp->getFeatures();
  459. // mask class
  460. if (mGPUClass >= 0 && mGPUClass < 4)
  461. {
  462. const char* class_table[] =
  463. {
  464. "Class0",
  465. "Class1",
  466. "Class2",
  467. "Class3"
  468. };
  469. LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
  470. maskFeatures(class_table[mGPUClass]);
  471. }
  472. else
  473. {
  474. LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
  475. maskFeatures("Unknown");
  476. }
  477. // now all those wacky ones
  478. if (!gGLManager.mHasFragmentShader)
  479. {
  480. maskFeatures("NoPixelShaders");
  481. }
  482. if (!gGLManager.mHasVertexShader)
  483. {
  484. maskFeatures("NoVertexShaders");
  485. }
  486. if (gGLManager.mIsNVIDIA)
  487. {
  488. maskFeatures("NVIDIA");
  489. }
  490. if (gGLManager.mIsGF2or4MX)
  491. {
  492. maskFeatures("GeForce2");
  493. }
  494. if (gGLManager.mIsATI)
  495. {
  496. maskFeatures("ATI");
  497. }
  498. if (gGLManager.mATIOldDriver)
  499. {
  500. maskFeatures("ATIOldDriver");
  501. }
  502. if (gGLManager.mIsGFFX)
  503. {
  504. maskFeatures("GeForceFX");
  505. }
  506. if (gGLManager.mIsIntel)
  507. {
  508. maskFeatures("Intel");
  509. }
  510. if (gGLManager.mGLVersion < 1.5f)
  511. {
  512. maskFeatures("OpenGLPre15");
  513. }
  514. // now mask by gpu string
  515. // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
  516. std::string gpustr = mGPUString;
  517. for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
  518. {
  519. if (*iter == ' ')
  520. {
  521. *iter = '_';
  522. }
  523. }
  524. //llinfos << "Masking features from gpu table match: " << gpustr << llendl;
  525. maskFeatures(gpustr);
  526. // now mask cpu type ones
  527. if (gSysMemory.getPhysicalMemoryClamped() <= 256*1024*1024)
  528. {
  529. maskFeatures("RAM256MB");
  530. }
  531. #if LL_SOLARIS && defined(__sparc)  //  even low MHz SPARCs are fast
  532. #error The 800 is hinky. Would something like a LL_MIN_MHZ make more sense here?
  533. if (gSysCPU.getMhz() < 800)
  534. #else
  535. if (gSysCPU.getMhz() < 1100)
  536. #endif
  537. {
  538. maskFeatures("CPUSlow");
  539. }
  540. if (isSafe())
  541. {
  542. maskFeatures("safe");
  543. }
  544. }