llfeaturemanager.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056
  1. /**
  2. * @file llfeaturemanager.cpp
  3. * @brief LLFeatureManager class implementation
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewergpl$
  6. *
  7. * Copyright (c) 2003-2009, 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 <regex>
  34. #include "boost/tokenizer.hpp"
  35. #include "llfeaturemanager.h"
  36. #if LL_WINDOWS
  37. #include "lldxhardware.h"
  38. #endif
  39. #include "lldir.h"
  40. #include "llgl.h"
  41. #include "llnotifications.h"
  42. #include "llrender.h"
  43. #include "llsys.h"
  44. #include "llui.h"
  45. #include "llwindow.h"
  46. #include "llappviewer.h"
  47. #include "lldrawpoolterrain.h"
  48. #include "llgridmanager.h"
  49. #include "llpipeline.h"
  50. #include "llviewercontrol.h"
  51. #include "llviewershadermgr.h"
  52. #include "llviewertexturelist.h"
  53. #include "llweb.h"
  54. #include "llworld.h"
  55. LLFeatureManager gFeatureManager;
  56. ///////////////////////////////////////////////////////////////////////////////
  57. // LLFeatureInfo class
  58. ///////////////////////////////////////////////////////////////////////////////
  59. LLFeatureInfo::LLFeatureInfo(const std::string& name, bool avail, F32 level)
  60. : mValid(true),
  61. mName(name),
  62. mAvailable(avail),
  63. mRecommendedLevel(level)
  64. {
  65. }
  66. ///////////////////////////////////////////////////////////////////////////////
  67. // LLFeatureList class
  68. ///////////////////////////////////////////////////////////////////////////////
  69. LLFeatureList::LLFeatureList(const std::string& name)
  70. : mName(name)
  71. {
  72. }
  73. void LLFeatureList::addFeature(const std::string& name, bool avail, F32 level)
  74. {
  75. LLFeatureInfo fi(name, avail, level);
  76. feature_map_t::iterator it = mFeatures.find(name);
  77. if (it == mFeatures.end())
  78. {
  79. mFeatures[name] = fi;
  80. }
  81. else
  82. {
  83. llwarns << "Attempting to add preexisting feature " << name << llendl;
  84. it->second = fi;
  85. }
  86. }
  87. bool LLFeatureList::isFeatureAvailable(const std::string& name)
  88. {
  89. feature_map_t::iterator it = mFeatures.find(name);
  90. if (it != mFeatures.end())
  91. {
  92. return (it->second).mAvailable;
  93. }
  94. llwarns << "Feature " << name << " not in feature list !" << llendl;
  95. // true so that you have to explicitly disable something for it to be
  96. // disabled
  97. return true;
  98. }
  99. // Looks up the specified feature mask and overlay it on top of the current
  100. // feature mask.
  101. void LLFeatureList::maskList(LLFeatureList& mask)
  102. {
  103. LLFeatureInfo mask_fi;
  104. feature_map_t::iterator feature_it;
  105. for (feature_it = mask.mFeatures.begin();
  106. feature_it != mask.mFeatures.end(); ++feature_it)
  107. {
  108. mask_fi = feature_it->second;
  109. //
  110. // Look for the corresponding feature
  111. //
  112. feature_map_t::iterator iter = mFeatures.find(mask_fi.mName);
  113. if (iter == mFeatures.end())
  114. {
  115. llwarns << "Feature " << mask_fi.mName
  116. << " in mask not in top level !" << llendl;
  117. continue;
  118. }
  119. LLFeatureInfo& cur_fi = iter->second;
  120. if (mask_fi.mAvailable && !cur_fi.mAvailable)
  121. {
  122. llwarns << "Mask attempting to reenabling disabled feature, ignoring "
  123. << cur_fi.mName << llendl;
  124. continue;
  125. }
  126. cur_fi.mAvailable = mask_fi.mAvailable;
  127. cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel,
  128. mask_fi.mRecommendedLevel);
  129. LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
  130. << " Feature " << mask_fi.mName
  131. << " Mask: " << mask_fi.mRecommendedLevel
  132. << " Now: " << cur_fi.mRecommendedLevel
  133. << LL_ENDL;
  134. }
  135. LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName
  136. << std::endl;
  137. // Will conditionally call dump only if the above message will be
  138. // logged, thanks to it being wrapped by the LL_DEBUGS and LL_ENDL
  139. // macros.
  140. dump();
  141. LL_CONT << LL_ENDL;
  142. }
  143. void LLFeatureList::dump()
  144. {
  145. LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
  146. LL_DEBUGS("RenderInit") << "------------" << LL_ENDL;
  147. LLFeatureInfo fi;
  148. feature_map_t::iterator feature_it;
  149. for (feature_it = mFeatures.begin(); feature_it != mFeatures.end();
  150. ++feature_it)
  151. {
  152. fi = feature_it->second;
  153. LL_DEBUGS("RenderInit") << fi.mName << "\t\t" << fi.mAvailable
  154. << ":" << fi.mRecommendedLevel << LL_ENDL;
  155. }
  156. LL_DEBUGS("RenderInit") << LL_ENDL;
  157. }
  158. ///////////////////////////////////////////////////////////////////////////////
  159. // LLFeatureManager class proper
  160. ///////////////////////////////////////////////////////////////////////////////
  161. LLFeatureList* LLFeatureManager::findMask(const std::string& name)
  162. {
  163. if (mMaskList.count(name))
  164. {
  165. return mMaskList[name];
  166. }
  167. return NULL;
  168. }
  169. bool LLFeatureManager::maskFeatures(const std::string& name)
  170. {
  171. LLFeatureList* maskp = findMask(name);
  172. if (!maskp)
  173. {
  174. LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
  175. return false;
  176. }
  177. LL_DEBUGS("RenderInit") << "Applying Feature Mask: " << name << LL_ENDL;
  178. maskList(*maskp);
  179. return true;
  180. }
  181. bool LLFeatureManager::loadFeatureTables()
  182. {
  183. // *TODO: if to add something else to the skipped list, make this data
  184. // driven. Put it in the feature table and parse it correctly
  185. mSkippedFeatures.emplace("RenderAnisotropic");
  186. std::string filepath = gDirUtil.getFullPath(LL_PATH_APP_SETTINGS,
  187. "featuretable.txt");
  188. LL_DEBUGS("RenderInit") << "Looking for feature table in " << filepath
  189. << LL_ENDL;
  190. llifstream file(filepath.c_str());
  191. if (!file.is_open())
  192. {
  193. llwarns << "Unable to open feature table !" << llendl;
  194. return false;
  195. }
  196. std::string name;
  197. U32 version;
  198. // Check file version
  199. file >> name;
  200. file >> version;
  201. if (name != "version")
  202. {
  203. llwarns << filepath << " does not appear to be a valid feature table !"
  204. << llendl;
  205. file.close();
  206. return false;
  207. }
  208. mTableVersion = version;
  209. LLFeatureList* flp = NULL;
  210. while (file >> name)
  211. {
  212. char buffer[MAX_STRING];
  213. if (name.substr(0, 2) == "//")
  214. {
  215. // This is a comment.
  216. file.getline(buffer, MAX_STRING);
  217. continue;
  218. }
  219. if (name.empty())
  220. {
  221. // This is a blank line
  222. file.getline(buffer, MAX_STRING);
  223. continue;
  224. }
  225. if (name == "list")
  226. {
  227. // It is a new mask, create it.
  228. file >> name;
  229. if (mMaskList.count(name))
  230. {
  231. llerrs << "Overriding mask " << name << ", this is invalid !"
  232. << llendl;
  233. }
  234. flp = new LLFeatureList(name);
  235. mMaskList[name] = flp;
  236. }
  237. else if (!flp)
  238. {
  239. llerrs << "Specified parameter before <list> keyword !" << llendl;
  240. }
  241. else
  242. {
  243. S32 available;
  244. F32 recommended;
  245. file >> available >> recommended;
  246. flp->addFeature(name, available, recommended);
  247. }
  248. }
  249. file.close();
  250. return true;
  251. }
  252. // This helper class is used to ensure that each generateTextures() call is
  253. // matched by a corresponding deleteTextures() call. It also handles the
  254. // bindManual() calls using those textures.
  255. class LLTextureHolder
  256. {
  257. public:
  258. LLTextureHolder(U32 unit, U32 size)
  259. : mTexUnit(gGL.getTexUnit(unit)),
  260. mSource(size) // Pre-allocate vector
  261. {
  262. LLImageGL::generateTextures(mSource.size(), &mSource[0]);
  263. }
  264. ~LLTextureHolder()
  265. {
  266. // Ensure that we unbind and delete these textures regardless of how we
  267. // exit
  268. if (mTexUnit)
  269. {
  270. mTexUnit->unbind(LLTexUnit::TT_TEXTURE);
  271. }
  272. LLImageGL::deleteTextures(mSource.size(), &mSource[0]);
  273. }
  274. bool bind(U32 index)
  275. {
  276. return mTexUnit &&
  277. mTexUnit->bindManual(LLTexUnit::TT_TEXTURE, mSource[index]);
  278. }
  279. private:
  280. LLTexUnit* mTexUnit;
  281. std::vector<U32> mSource;
  282. };
  283. #if LL_LINUX
  284. // *HACK: to avoid a black (SDL1) or white (SDL2) screen after benchmark, when
  285. // not yet logged in... *TODO: find the root cause for that empty screen, and a
  286. // better way than this dirty trick to fix it... HB
  287. class LLScreenRestorerHelper
  288. {
  289. public:
  290. LLScreenRestorerHelper() = default;
  291. ~LLScreenRestorerHelper()
  292. {
  293. if (gWindowp)
  294. {
  295. // This triggers a proper screen refresh (that I sadly could not
  296. // achieve otherwise), by triggering a full redraw event at the SDL
  297. // level (redrawing at the viewer level is not enough; I also tried
  298. // swapping SDL GL buffers before and after benchmark, but to no
  299. // avail). HB
  300. gWindowp->refresh();
  301. }
  302. }
  303. };
  304. #endif
  305. //static
  306. F32 LLFeatureManager::benchmarkGPU()
  307. {
  308. if (!gGLManager.mHasTimerQuery)
  309. {
  310. // Do not bother benchmarking the fixed function or venerable drivers
  311. // which do not support accurate timing anyway and are likely to be
  312. // correctly identified by the GPU table already.
  313. return -1.f;
  314. }
  315. if (!gBenchmarkProgram.mProgramObject)
  316. {
  317. // Do not try and benchmark before the shaders get properly
  318. // initialized, which can only happen *after* the feature manager
  319. // (which calls this benchmarking routine) has been initialized !
  320. // The chicken or the egg dilemna... HB
  321. return -1.f;
  322. }
  323. #if LL_LINUX
  324. // *HACK (see above). HB
  325. LLScreenRestorerHelper restore_on_exit;
  326. #endif
  327. LLGLDisable blend(GL_BLEND);
  328. // Measure memory bandwidth by:
  329. // - allocating a batch of textures and render targets
  330. // - rendering those textures to those render targets
  331. // - recording time taken
  332. // - taking the median time for a given number of samples
  333. // Resolution of textures/render targets
  334. constexpr U32 res = 1024;
  335. // Number of textures
  336. constexpr U32 count = 32;
  337. // Number of samples to take
  338. constexpr S32 samples = 64;
  339. std::vector<LLRenderTarget> dest(count);
  340. LLTextureHolder tex_holder(0, count);
  341. std::vector<F32> results;
  342. // Build a random texture
  343. U32 bytes = res * res * 4;
  344. U8* pixels = new U8[bytes];
  345. for (U32 i = 0; i < bytes; ++i)
  346. {
  347. pixels[i] = (U8)ll_rand(255);
  348. }
  349. gGL.setColorMask(true, true);
  350. LLGLDepthTest depth(GL_FALSE);
  351. for (U32 i = 0; i < count; ++i)
  352. {
  353. // Allocate render targets and textures
  354. bool success;
  355. if (gUsePBRShaders)
  356. {
  357. success = dest[i].allocate(res, res, GL_RGBA);
  358. }
  359. else
  360. {
  361. success = dest[i].allocate(res, res, GL_RGBA, false, false);
  362. }
  363. if (!success)
  364. {
  365. LLMemory::allocationFailed();
  366. llwarns << "Failed to allocate render target " << i << llendl;
  367. delete[] pixels;
  368. return -1.f;
  369. }
  370. dest[i].bindTarget();
  371. dest[i].clear();
  372. dest[i].flush();
  373. if (!tex_holder.bind(i))
  374. {
  375. llwarns << "Failed to bind tex unit " << i << llendl;
  376. delete[] pixels;
  377. return -1.f;
  378. }
  379. LLImageGL::setManualImage(GL_TEXTURE_2D, 0, GL_RGBA, res, res, GL_RGBA,
  380. GL_UNSIGNED_BYTE, pixels);
  381. }
  382. delete[] pixels;
  383. // Make a dummy triangle to draw with
  384. LLPointer<LLVertexBuffer> buff =
  385. new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX);
  386. if (!buff->allocateBuffer(3, 0))
  387. {
  388. LLMemory::allocationFailed();
  389. llwarns << "Failed to allocate vertex buffer" << llendl;
  390. return -1.f;
  391. }
  392. LLStrider<LLVector3> v;
  393. if (!buff->getVertexStrider(v))
  394. {
  395. llwarns << "Could not allocate vertex buffer. Benchmark aborted."
  396. << llendl;
  397. return -1.f;
  398. }
  399. // Generate a dummy triangle
  400. v[0].set(-1.f, 1.f, 0.f);
  401. v[1].set(-1.f, -3.f, 0.f);
  402. v[2].set(3.f, 1.f, 0.f);
  403. LLGLSLShader::initProfile();
  404. buff->unmapBuffer();
  405. gBenchmarkProgram.bind();
  406. #if 0 // Do not do that: this causes a blank screen under core GL profile.
  407. // We *already* have a vertex array anyway, when under core GL profile
  408. // (see mDummyVAO in indra/llrender/llrender.cpp). HB
  409. # ifdef GL_ARB_vertex_array_object
  410. U32 glarray = 0;
  411. if (LLRender::sGLCoreProfile)
  412. {
  413. glGenVertexArrays(1, &glarray);
  414. glBindVertexArray(glarray);
  415. }
  416. # endif
  417. #endif
  418. buff->setBuffer(LLVertexBuffer::MAP_VERTEX);
  419. // Wait for any previous GL commands to finish
  420. glFinish();
  421. LLTimer timer;
  422. for (S32 c = -1; c < samples; ++c)
  423. {
  424. timer.start();
  425. for (U32 i = 0; i < count; ++i)
  426. {
  427. dest[i].bindTarget();
  428. tex_holder.bind(i);
  429. buff->drawArrays(LLRender::TRIANGLES, 0, 3);
  430. dest[i].flush();
  431. }
  432. // Wait for current batch of copies to finish
  433. glFinish();
  434. F32 time = timer.getElapsedTimeF32();
  435. if (c >= 0) // Ignore 1st sample as it tends to be artificially slow
  436. {
  437. // Store result in gigabytes per second
  438. F32 gb = (F32)((F64)(res * res * 8 * count)) / 1000000000.f;
  439. F32 gbps = gb / time;
  440. results.push_back(gbps);
  441. }
  442. }
  443. #if 0 // Do not do that: this causes a blank screen under core GL profile
  444. # ifdef GL_ARB_vertex_array_object
  445. if (LLRender::sGLCoreProfile)
  446. {
  447. glBindVertexArray(0);
  448. glDeleteVertexArrays(1, &glarray);
  449. }
  450. # endif
  451. #endif
  452. LLGLSLShader::finishProfile(false);
  453. std::sort(results.begin(), results.end());
  454. F32 gbps = results[results.size() / 2];
  455. llinfos << "Memory bandwidth is " << llformat("%.3f", gbps)
  456. << "GB/s according to CPU timers" << llendl;
  457. #if LL_DARWIN
  458. if (gbps > 512.f)
  459. {
  460. llinfos << "Memory bandwidth is improbably high and likely incorrect."
  461. << llendl;
  462. // OS-X is probably lying, discard result
  463. return -1.f;
  464. }
  465. #endif
  466. F32 ms = gBenchmarkProgram.mTimeElapsed / 1000000.f;
  467. F32 seconds = ms / 1000.f;
  468. F64 samples_drawn = res * res * count * samples;
  469. F32 samples_sec = (F32)(samples_drawn / 1000000000.0) / seconds;
  470. gbps = samples_sec * 8;
  471. llinfos << "Memory bandwidth is " << llformat("%.3f", gbps)
  472. << "GB/s according to ARB_timer_query" << llendl;
  473. gBenchmarkProgram.unbind();
  474. return gbps;
  475. }
  476. void LLFeatureManager::loadGPUClass(bool benchmark_gpu)
  477. {
  478. LL_DEBUGS("RenderInit") << "Loading GPU class..." << LL_ENDL;
  479. // Defaults
  480. mGPUMemoryBandwidth = 0.f; // 0 = not benchmarked
  481. mGPUSupported = false;
  482. mGPUClass = GPU_CLASS_UNKNOWN;
  483. const std::string raw_renderer = gGLManager.getRawGLString();
  484. mGPUString = raw_renderer;
  485. if (benchmark_gpu)
  486. {
  487. // Bias GPU performances with CPU speed
  488. F32 class0_gbps = llmax(gSavedSettings.getU32("GPUMemoryBWClassBase"),
  489. 1U);
  490. // This factor will be 1.f for a CPU core CPUBenchmarkPerfFactor times
  491. // weaker than the core of a 9700K @ 5GHz. HB
  492. F32 cpu_bias = llclamp(gSavedSettings.getF32("CPUBenchmarkPerfFactor"),
  493. 0.1f, 10.f);
  494. // Multiply our adjustment factor by actual CPU core benchmark factor
  495. cpu_bias *= LLCPUInfo::getInstance()->benchmarkFactor();
  496. // Get GPU memory bandwidth from benchmark
  497. mGPUMemoryBandwidth = benchmarkGPU();
  498. F32 gbps = mGPUMemoryBandwidth * cpu_bias;
  499. if (gbps < 0.f)
  500. {
  501. // Could not bench, use GLVersion
  502. #if LL_DARWIN
  503. // GLVersion is misleading on macOS, just default to class 2 if we
  504. // cannot bench
  505. mGPUClass = GPU_CLASS_2;
  506. #else
  507. if (gGLManager.mGLVersion <= 2.f)
  508. {
  509. mGPUClass = GPU_CLASS_0;
  510. }
  511. else if (gGLManager.mGLVersion <= 3.f)
  512. {
  513. mGPUClass = GPU_CLASS_1;
  514. }
  515. else if (gGLManager.mGLVersion < 3.3f)
  516. {
  517. mGPUClass = GPU_CLASS_2;
  518. }
  519. else if (gGLManager.mGLVersion < 4.f)
  520. {
  521. mGPUClass = GPU_CLASS_3;
  522. }
  523. else if (gGLManager.mGLVersion < 4.4f)
  524. {
  525. mGPUClass = GPU_CLASS_4;
  526. }
  527. else
  528. {
  529. mGPUClass = GPU_CLASS_5;
  530. }
  531. if (gGLManager.mIsIntel && mGPUClass > GPU_CLASS_1)
  532. {
  533. // Intel GPUs are generally weaker than other GPUs despite
  534. // having advanced OpenGL features
  535. mGPUClass = (EGPUClass)(mGPUClass - 1);
  536. }
  537. #endif
  538. }
  539. else if (gbps < class0_gbps || gGLManager.mGLVersion <= 2.f)
  540. {
  541. mGPUClass = GPU_CLASS_0;
  542. }
  543. else if (gbps < 2.f * class0_gbps || gGLManager.mGLVersion <= 3.f)
  544. {
  545. mGPUClass = GPU_CLASS_1;
  546. }
  547. else if (gbps < 4.f * class0_gbps || gGLManager.mGLVersion < 3.3f)
  548. {
  549. mGPUClass = GPU_CLASS_2;
  550. }
  551. else if (gbps < 8.f * class0_gbps || gGLManager.mGLVersion < 4.f)
  552. {
  553. mGPUClass = GPU_CLASS_3;
  554. }
  555. else if (gbps < 16.f * class0_gbps || gGLManager.mGLVersion < 4.4f)
  556. {
  557. mGPUClass = GPU_CLASS_4;
  558. }
  559. else
  560. {
  561. mGPUClass = GPU_CLASS_5;
  562. }
  563. mGPUSupported = mGPUClass > GPU_CLASS_0;
  564. if (mGPUSupported)
  565. {
  566. std::string msg =
  567. llformat("GPU is considered supported (class %d). Class deduced from ",
  568. (S32)mGPUClass);
  569. if (gbps > 0)
  570. {
  571. llinfos << msg
  572. << "CPU speed-pondered GPU memory benchmark: "
  573. << (S32)gbps << "GB/s" << llendl;
  574. }
  575. else
  576. {
  577. llinfos << msg << "advertized OpenGL version: "
  578. << gGLManager.mGLVersion << llendl;
  579. }
  580. gSavedSettings.setS32("LastGPUClass", mGPUClass);
  581. gSavedSettings.setString("LastGPUString", raw_renderer);
  582. }
  583. else
  584. {
  585. llwarns << "GPU is not supported !" << llendl;
  586. }
  587. return;
  588. }
  589. std::string filepath = gDirUtil.getFullPath(LL_PATH_APP_SETTINGS,
  590. "gpu_table.txt");
  591. llifstream file(filepath.c_str());
  592. if (!file.is_open())
  593. {
  594. llwarns << "Unable to open GPU table: " << filepath
  595. << ". Using the GPU benchmarking method instead..." << llendl;
  596. loadGPUClass(true); // Retry with benchmarking
  597. return;
  598. }
  599. std::string renderer = raw_renderer;
  600. for (std::string::iterator i = renderer.begin(); i != renderer.end();
  601. ++i)
  602. {
  603. *i = tolower(*i);
  604. }
  605. bool found = false;
  606. for (U32 line = 0; !found && !file.eof(); ++line)
  607. {
  608. char buffer[MAX_STRING];
  609. buffer[0] = 0;
  610. file.getline(buffer, MAX_STRING);
  611. if (strlen(buffer) == 0)
  612. {
  613. // This is a blank line
  614. continue;
  615. }
  616. if (strlen(buffer) >= 2 && buffer[0] == '/' && buffer[1] == '/')
  617. {
  618. // This is a comment.
  619. continue;
  620. }
  621. // Setup the tokenizer
  622. std::string buf(buffer);
  623. std::string cls, label, expr, supported;
  624. typedef boost::tokenizer<boost::char_separator<char> > tok;
  625. tok tokens(buf, boost::char_separator<char>("\t\n"));
  626. tok::iterator token_iter = tokens.begin();
  627. // Grab the label, pseudo regular expression, and class
  628. if (token_iter != tokens.end())
  629. {
  630. label = *token_iter++;
  631. }
  632. if (token_iter != tokens.end())
  633. {
  634. expr = *token_iter++;
  635. }
  636. if (token_iter != tokens.end())
  637. {
  638. cls = *token_iter++;
  639. }
  640. if (token_iter != tokens.end())
  641. {
  642. supported = *token_iter++;
  643. }
  644. if (label.empty() || expr.empty() || cls.empty() || supported.empty())
  645. {
  646. llwarns << "Invald gpu_table.txt at line " << line << ": '"
  647. << buffer << "'" << llendl;
  648. continue;
  649. }
  650. for (U32 i = 0; i < expr.length(); ++i)
  651. {
  652. expr[i] = tolower(expr[i]);
  653. }
  654. bool result = false;
  655. try
  656. {
  657. // Run the regular expression against the renderer
  658. std::regex re(expr.c_str());
  659. result = std::regex_search(renderer, re);
  660. }
  661. catch (std::regex_error& e)
  662. {
  663. llwarns << "Regex error: " << e.what() << " at line " << line
  664. << llendl;
  665. continue;
  666. }
  667. if (result)
  668. {
  669. // If we found it, stop !
  670. found = true;
  671. mGPUString = label;
  672. mGPUClass = (EGPUClass)strtol(cls.c_str(), NULL, 10);
  673. mGPUSupported = strtol(supported.c_str(), NULL, 10);
  674. break;
  675. }
  676. }
  677. file.close();
  678. std::string last_seen = gSavedSettings.getString("LastGPUString");
  679. if (raw_renderer != last_seen)
  680. {
  681. // Enable core GL profile for recognized NVIDIA cards when not yet
  682. // seen in former sessions (i.e. we preserve the user choice after
  683. // the first session). HB
  684. if (gGLManager.mIsNVIDIA && gGLManager.mGLVersion >= 3.f &&
  685. !gSavedSettings.getBool("RenderGLCoreProfile"))
  686. {
  687. gSavedSettings.setBool("RenderGLCoreProfile", true);
  688. }
  689. }
  690. if (!found)
  691. {
  692. // Do not re-benchmark at each sessions an already benchmarked
  693. // "unknown" GPU; simply reuse the stored GPU class as computed
  694. // on first launch with this GPU. HB
  695. if (last_seen == raw_renderer)
  696. {
  697. mGPUClass = (EGPUClass)gSavedSettings.getS32("LastGPUClass");
  698. mGPUClass = llclamp(mGPUClass, GPU_CLASS_UNKNOWN, GPU_CLASS_5);
  699. mGPUSupported = mGPUClass != GPU_CLASS_UNKNOWN;
  700. }
  701. if (mGPUSupported)
  702. {
  703. llinfos << "GPU '" << raw_renderer
  704. << "' already benchmarked and deemed compatible."
  705. << llendl;
  706. }
  707. else
  708. {
  709. llinfos << "GPU '" << raw_renderer
  710. << "' not recognized, using the GPU benchmarking method instead..."
  711. << llendl;
  712. loadGPUClass(true); // Retry with benchmarking
  713. return;
  714. }
  715. }
  716. if (mGPUSupported)
  717. {
  718. if (found)
  719. {
  720. llinfos << "GPU '" << raw_renderer << "' recognized as '"
  721. << mGPUString << "' and is supported." << llendl;
  722. }
  723. gSavedSettings.setS32("LastGPUClass", mGPUClass);
  724. gSavedSettings.setString("LastGPUString", raw_renderer);
  725. }
  726. else
  727. {
  728. llwarns << "GPU '" << raw_renderer << "' recognized as '"
  729. << mGPUString << "' and is not supported !" << llendl;
  730. }
  731. }
  732. void LLFeatureManager::cleanupFeatureTables()
  733. {
  734. std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
  735. mMaskList.clear();
  736. }
  737. void LLFeatureManager::init()
  738. {
  739. // Load the tables
  740. loadFeatureTables();
  741. // Get the GPU class from gpu_table.txt (or from the OpenGL version). We
  742. // cannot benchmark this early in the viewer initialization processs
  743. // because the shaders have not yet been loaded. HB
  744. loadGPUClass(false);
  745. // Apply the base masks, so we know if anything is disabled
  746. applyBaseMasks();
  747. }
  748. void LLFeatureManager::applyRecommendedSettings()
  749. {
  750. llinfos << "Applying recommended features." << llendl;
  751. loadGPUClass(gSavedSettings.getBool("BenchmarkGPU"));
  752. // Do not go too far on recommended level: level 4 max (no shadows)
  753. S32 level = llmin(mGPUClass, GPU_CLASS_4);
  754. setGraphicsLevel(level, false);
  755. gSavedSettings.setU32("RenderQualityPerformance", level);
  756. // Enable core GL profile for NVIDIA cards with OpenGL v3+. HB
  757. if (gGLManager.mIsNVIDIA && gGLManager.mGLVersion >= 3.f &&
  758. !gSavedSettings.getBool("RenderGLCoreProfile"))
  759. {
  760. gSavedSettings.setBool("RenderGLCoreProfile", true);
  761. gNotifications.add("CoreProfileAfterRestart");
  762. }
  763. }
  764. // See featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
  765. void LLFeatureManager::applyFeatures(bool skip_features)
  766. {
  767. #if LL_DEBUG
  768. dump();
  769. #endif
  770. // Scroll through all of these and set their corresponding control value
  771. for (feature_map_t::iterator mIt = mFeatures.begin();
  772. mIt != mFeatures.end(); ++mIt)
  773. {
  774. const std::string& name = mIt->first;
  775. // Skip features you want to skip; do this for when you do not want to
  776. // change certain settings
  777. if (skip_features && mSkippedFeatures.count(name))
  778. {
  779. continue;
  780. }
  781. // Get the control setting
  782. LLControlVariable* ctrl = gSavedSettings.getControl(name.c_str());
  783. if (!ctrl)
  784. {
  785. llwarns << "Control setting " << name << " does not exist !"
  786. << llendl;
  787. continue;
  788. }
  789. F32 recommended = 0.f;
  790. if (mIt->second.mAvailable)
  791. {
  792. recommended = mIt->second.mRecommendedLevel;
  793. }
  794. else
  795. {
  796. llwarns << "Feature " << name << " not available !" << llendl;
  797. }
  798. // Handle all the different types
  799. if (ctrl->isType(TYPE_BOOLEAN))
  800. {
  801. gSavedSettings.setBool(name.c_str(), recommended != 0.f);
  802. }
  803. else if (ctrl->isType(TYPE_S32))
  804. {
  805. gSavedSettings.setS32(name.c_str(), (S32)recommended);
  806. }
  807. else if (ctrl->isType(TYPE_U32))
  808. {
  809. gSavedSettings.setU32(name.c_str(), (U32)recommended);
  810. }
  811. else if (ctrl->isType(TYPE_F32))
  812. {
  813. gSavedSettings.setF32(name.c_str(), recommended);
  814. }
  815. else
  816. {
  817. llwarns << "Control variable is not a numeric type !" << llendl;
  818. }
  819. }
  820. }
  821. void LLFeatureManager::setGraphicsLevel(S32 level, bool skip_features)
  822. {
  823. LLViewerShaderMgr::sSkipReload = true;
  824. clear_glerror();
  825. applyBaseMasks();
  826. switch (level)
  827. {
  828. case 0:
  829. default:
  830. maskFeatures("Low");
  831. break;
  832. case 1:
  833. maskFeatures("Mid1");
  834. break;
  835. case 2:
  836. maskFeatures("Mid2");
  837. break;
  838. case 3:
  839. maskFeatures("High1");
  840. break;
  841. case 4:
  842. maskFeatures("High2");
  843. break;
  844. case 5:
  845. maskFeatures("Ultra");
  846. }
  847. applyFeatures(skip_features);
  848. LLViewerShaderMgr::sSkipReload = false;
  849. gViewerShaderMgrp->setShaders();
  850. }
  851. void LLFeatureManager::applyBaseMasks()
  852. {
  853. // Re-apply masks
  854. mFeatures.clear();
  855. LLFeatureList* maskp = findMask("all");
  856. if (!maskp)
  857. {
  858. llwarns << "Missing \"all\" list in feature table !" << llendl;
  859. return;
  860. }
  861. mFeatures = maskp->getFeatures();
  862. // Mask class
  863. if ((S32)mGPUClass >= 0 && (S32)mGPUClass < 6)
  864. {
  865. static const char* class_table[] =
  866. {
  867. "Class0",
  868. "Class1",
  869. "Class2",
  870. "Class3",
  871. "Class4",
  872. "Class5"
  873. };
  874. llinfos << "Setting GPU class to: " << class_table[mGPUClass] << llendl;
  875. maskFeatures(class_table[mGPUClass]);
  876. }
  877. else
  878. {
  879. llinfos << "Setting GPU class to: Unknown" << llendl;
  880. maskFeatures("Unknown");
  881. }
  882. // Now all those wacky ones
  883. if (gGLManager.mIsNVIDIA)
  884. {
  885. maskFeatures("NVIDIA");
  886. }
  887. if (gGLManager.mIsAMD)
  888. {
  889. maskFeatures("ATI");
  890. }
  891. if (gGLManager.mIsIntel)
  892. {
  893. maskFeatures("Intel");
  894. }
  895. if (gGLManager.mGLVersion < 3.f)
  896. {
  897. maskFeatures("OpenGLPre30");
  898. }
  899. if (gGLManager.mGLVersion < 4.f)
  900. {
  901. maskFeatures("OpenGLPre40");
  902. }
  903. if (gGLManager.mVRAM > 512)
  904. {
  905. maskFeatures("VRAMGT512");
  906. }
  907. // Now mask by gpu string. Replaces ' ' with '_' in mGPUString to deal with
  908. // inability for parser to handle spaces.
  909. std::string gpustr = mGPUString;
  910. for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end();
  911. ++iter)
  912. {
  913. if (*iter == ' ')
  914. {
  915. *iter = '_';
  916. }
  917. }
  918. LL_DEBUGS("RenderInit") << "Masking features from GPU table match: "
  919. << gpustr << LL_ENDL;
  920. maskFeatures(gpustr);
  921. if (isSafe())
  922. {
  923. maskFeatures("safe");
  924. }
  925. }