diff options
Diffstat (limited to 'indra/newview/llfeaturemanager.cpp')
| -rw-r--r-- | indra/newview/llfeaturemanager.cpp | 1510 |
1 files changed, 755 insertions, 755 deletions
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp index c4a91e1d25..b27c84dce1 100644 --- a/indra/newview/llfeaturemanager.cpp +++ b/indra/newview/llfeaturemanager.cpp @@ -1,755 +1,755 @@ -/** - * @file llfeaturemanager.cpp - * @brief LLFeatureManager class implementation - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include <iostream> -#include <fstream> - -#include <boost/regex.hpp> -#include <boost/assign/list_of.hpp> - -#include "llfeaturemanager.h" -#include "lldir.h" - -#include "llsys.h" -#include "llgl.h" - -#include "llappviewer.h" -#include "llbufferstream.h" -#include "llnotificationsutil.h" -#include "llviewercontrol.h" -#include "llworld.h" -#include "lldrawpoolterrain.h" -#include "llviewertexturelist.h" -#include "llversioninfo.h" -#include "llwindow.h" -#include "llui.h" -#include "llcontrol.h" -#include "llboost.h" -#include "llweb.h" -#include "llviewershadermgr.h" -#include "llstring.h" -#include "stringize.h" -#include "llcorehttputil.h" - -#if LL_WINDOWS -#include "lldxhardware.h" -#endif - -#if LL_DARWIN -const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; -#elif LL_LINUX -const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt"; -#else -const char FEATURE_TABLE_FILENAME[] = "featuretable.txt"; -#endif - -#if 0 // consuming code in #if 0 below -#endif -LLFeatureInfo::LLFeatureInfo(const std::string& name, const bool available, const F32 level) - : mValid(true), mName(name), mAvailable(available), mRecommendedLevel(level) -{ -} - -LLFeatureList::LLFeatureList(const std::string& name) - : mName(name) -{ -} - -LLFeatureList::~LLFeatureList() -{ -} - -void LLFeatureList::addFeature(const std::string& name, const bool available, const F32 level) -{ - if (mFeatures.count(name)) - { - LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL; - } - - LLFeatureInfo fi(name, available, level); - LL_DEBUGS_ONCE("RenderInit") << "Feature '" << name << "' " - << (available ? "" : "not " ) << "available" - << " at " << level - << LL_ENDL; - mFeatures[name] = fi; -} - -bool LLFeatureList::isFeatureAvailable(const std::string& name) -{ - if (mFeatures.count(name)) - { - return mFeatures[name].mAvailable; - } - - LL_WARNS_ONCE("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL; - - // changing this to true so you have to explicitly disable - // something for it to be disabled - return true; -} - -F32 LLFeatureList::getRecommendedValue(const std::string& name) -{ - if (mFeatures.count(name) && isFeatureAvailable(name)) - { - LL_DEBUGS_ONCE("RenderInit") << "Setting '" << name << "' to recommended value " << mFeatures[name].mRecommendedLevel << LL_ENDL; - return mFeatures[name].mRecommendedLevel; - } - - LL_WARNS_ONCE("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL; - return 0; -} - -bool LLFeatureList::maskList(LLFeatureList &mask) -{ - LL_DEBUGS_ONCE() << "Masking with " << mask.mName << LL_ENDL; - // - // Lookup the specified feature mask, and overlay it on top of the - // current feature mask. - // - - LLFeatureInfo mask_fi; - - feature_map_t::iterator feature_it; - for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it) - { - mask_fi = feature_it->second; - // - // Look for the corresponding feature - // - if (!mFeatures.count(mask_fi.mName)) - { - LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL; - continue; - } - - LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName]; - if (mask_fi.mAvailable && !cur_fi.mAvailable) - { - LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL; - continue; - } - cur_fi.mAvailable = mask_fi.mAvailable; - cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel); - LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName - << " Feature " << mask_fi.mName - << " Mask: " << mask_fi.mRecommendedLevel - << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL; - } - - LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl; - // Will conditionally call dump only if the above message will be logged, thanks - // to it being wrapped by the LL_DEBUGS and LL_ENDL macros. - dump(); - LL_CONT << LL_ENDL; - - return true; -} - -void LLFeatureList::dump() -{ - LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL; - - LLFeatureInfo fi; - feature_map_t::iterator feature_it; - for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it) - { - fi = feature_it->second; - LL_DEBUGS("RenderInit") << "With " << mName << " feature " << fi.mName << " " << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL; - } -} - -static const std::vector<std::string> sGraphicsLevelNames = boost::assign::list_of - ("Low") - ("LowMid") - ("Mid") - ("MidHigh") - ("High") - ("HighUltra") - ("Ultra") -; - -U32 LLFeatureManager::getMaxGraphicsLevel() const -{ - return sGraphicsLevelNames.size() - 1; -} - -bool LLFeatureManager::isValidGraphicsLevel(U32 level) const -{ - return (level <= getMaxGraphicsLevel()); -} - -std::string LLFeatureManager::getNameForGraphicsLevel(U32 level) const -{ - if (isValidGraphicsLevel(level)) - { - return sGraphicsLevelNames[level]; - } - return STRINGIZE("Invalid graphics level " << level << ", valid are 0 .. " - << getMaxGraphicsLevel()); -} - -S32 LLFeatureManager::getGraphicsLevelForName(const std::string& name) const -{ - const std::string FixedFunction("FixedFunction"); - std::string rname(name); - if (LLStringUtil::endsWith(rname, FixedFunction)) - { - // chop off any "FixedFunction" suffix - rname = rname.substr(0, rname.length() - FixedFunction.length()); - } - for (S32 i(0), iend(getMaxGraphicsLevel()); i <= iend; ++i) - { - if (sGraphicsLevelNames[i] == rname) - { - return i; - } - } - return -1; -} - -LLFeatureList *LLFeatureManager::findMask(const std::string& name) -{ - if (mMaskList.count(name)) - { - return mMaskList[name]; - } - - return NULL; -} - -bool LLFeatureManager::maskFeatures(const std::string& name) -{ - LLFeatureList *maskp = findMask(name); - if (!maskp) - { - LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL; - return false; - } - LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL; - return maskList(*maskp); -} - -bool LLFeatureManager::loadFeatureTables() -{ - // *TODO - if I or anyone else adds something else to the skipped list - // make this data driven. Put it in the feature table and parse it - // correctly - mSkippedFeatures.insert("RenderAnisotropic"); - mSkippedFeatures.insert("RenderGamma"); - mSkippedFeatures.insert("RenderVBOEnable"); - mSkippedFeatures.insert("RenderFogRatio"); - - // first table is install with app - std::string app_path = gDirUtilp->getAppRODataDir(); - app_path += gDirUtilp->getDirDelimiter(); - - std::string filename; - filename = FEATURE_TABLE_FILENAME; - - app_path += filename; - - bool parse_ok = parseFeatureTable(app_path); - - return parse_ok; -} - - -bool LLFeatureManager::parseFeatureTable(std::string filename) -{ - LL_INFOS("RenderInit") << "Attempting to parse feature table from " << filename << LL_ENDL; - - llifstream file; - std::string name; - U32 version; - - cleanupFeatureTables(); // in case an earlier attempt left partial results - file.open(filename.c_str()); /*Flawfinder: ignore*/ - - if (!file) - { - LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL; - return false; - } - - // Check file version - file >> name; - if (name != "version") - { - LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL; - return false; - } - file >> version; - - mTableVersion = version; - LL_INFOS("RenderInit") << "Found feature table version " << version << LL_ENDL; - - LLFeatureList *flp = NULL; - bool parse_ok = true; - while (parse_ok && file >> name ) - { - char buffer[MAX_STRING]; /*Flawfinder: ignore*/ - - if (name.substr(0,2) == "//") - { - // This is a comment. - file.getline(buffer, MAX_STRING); - continue; - } - - if (name == "list") - { - LL_DEBUGS("RenderInit") << "Before new list" << std::endl; - if (flp) - { - flp->dump(); - } - else - { - LL_CONT << "No current list"; - } - LL_CONT << LL_ENDL; - - // It's a new mask, create it. - file >> name; - if (!mMaskList.count(name)) - { - flp = new LLFeatureList(name); - mMaskList[name] = flp; - } - else - { - LL_WARNS("RenderInit") << "Overriding mask '" << name << "'; this is invalid!" << LL_ENDL; - parse_ok = false; - } - } - else - { - if (flp) - { - S32 available; - F32 recommended; - file >> available >> recommended; - flp->addFeature(name, available, recommended); - } - else - { - LL_WARNS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL; - parse_ok = false; - } - } - } - file.close(); - - if (!parse_ok) - { - LL_WARNS("RenderInit") << "Discarding feature table data from " << filename << LL_ENDL; - cleanupFeatureTables(); - } - - return parse_ok; -} - -F32 gpu_benchmark(); - -#if LL_WINDOWS - -F32 logExceptionBenchmark() -{ - // FIXME: gpu_benchmark uses many C++ classes on the stack to control state. - // SEH exceptions with our current exception handling options do not call - // destructors for these classes, resulting in an undefined state should - // this handler be invoked. - F32 gbps = -1; - __try - { - gbps = gpu_benchmark(); - } - __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation())) - { - // HACK - ensure that profiling is disabled - LLGLSLShader::finishProfile(false); - - // convert to C++ styled exception - char integer_string[32]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } - return gbps; -} -#endif - -bool LLFeatureManager::loadGPUClass() -{ - if (!gSavedSettings.getBOOL("SkipBenchmark")) - { - F32 class1_gbps = gSavedSettings.getF32("RenderClass1MemoryBandwidth"); - //get memory bandwidth from benchmark - F32 gbps; - try - { -#if LL_WINDOWS - gbps = logExceptionBenchmark(); -#else - gbps = gpu_benchmark(); -#endif - } - catch (const std::exception& e) - { - gbps = -1.f; - LL_WARNS("RenderInit") << "GPU benchmark failed: " << e.what() << LL_ENDL; - } - - mGPUMemoryBandwidth = gbps; - - // bias by CPU speed - F32 cpu_basis_mhz = gSavedSettings.getF32("RenderCPUBasis"); - F32 cpu_mhz = (F32) gSysCPU.getMHz(); - F32 cpu_bias = llclamp(cpu_mhz / cpu_basis_mhz, 0.5f, 1.f); - gbps *= cpu_bias; - - if (gbps < 0.f) - { //couldn't bench, default to Low - #if LL_DARWIN - //GLVersion is misleading on OSX, just default to class 3 if we can't bench - LL_WARNS("RenderInit") << "Unable to get an accurate benchmark; defaulting to class 3" << LL_ENDL; - mGPUClass = GPU_CLASS_3; - #else - mGPUClass = GPU_CLASS_0; - #endif - } - else if (gbps <= class1_gbps) - { - mGPUClass = GPU_CLASS_1; - } - else if (gbps <= class1_gbps *2.f) - { - mGPUClass = GPU_CLASS_2; - } - else if (gbps <= class1_gbps*4.f) - { - mGPUClass = GPU_CLASS_3; - } - else if (gbps <= class1_gbps*8.f) - { - mGPUClass = GPU_CLASS_4; - } - else - { - mGPUClass = GPU_CLASS_5; - } - - #if LL_WINDOWS - const F32Gigabytes MIN_PHYSICAL_MEMORY(2); - - LLMemory::updateMemoryInfo(); - F32Gigabytes physical_mem = LLMemory::getMaxMemKB(); - if (MIN_PHYSICAL_MEMORY > physical_mem && mGPUClass > GPU_CLASS_1) - { - // reduce quality on systems that don't have enough memory - mGPUClass = (EGPUClass)(mGPUClass - 1); - } - #endif //LL_WINDOWS - } //end if benchmark - else - { - //setting says don't benchmark MAINT-7558 - LL_WARNS("RenderInit") << "Setting 'SkipBenchmark' is true; defaulting to class 1 (may be required for some GPUs)" << LL_ENDL; - - mGPUClass = GPU_CLASS_1; - } - - // defaults - mGPUString = gGLManager.getRawGLString(); - mGPUSupported = true; - - return true; // indicates that a gpu value was established -} - -void LLFeatureManager::cleanupFeatureTables() -{ - std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer()); - mMaskList.clear(); -} - -void LLFeatureManager::initSingleton() -{ - // load the tables - loadFeatureTables(); - - // get the gpu class - loadGPUClass(); - - // apply the base masks, so we know if anything is disabled - applyBaseMasks(); -} - -void LLFeatureManager::applyRecommendedSettings() -{ - // apply saved settings - // cap the level at 2 (high) - U32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5)); - - LL_INFOS("RenderInit") << "Applying Recommended Features for level " << level << LL_ENDL; - - setGraphicsLevel(level, false); - gSavedSettings.setU32("RenderQualityPerformance", level); - - // now apply the tweaks to draw distance - // these are double negatives, because feature masks only work by - // downgrading values, so i needed to make a true value go to false - // for certain cards, thus the awkward name, "Disregard..." - if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance")) - { - gSavedSettings.setF32("RenderFarClip", 96.0f); - } - else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance")) - { - gSavedSettings.setF32("RenderFarClip", 128.0f); - } -} - -void LLFeatureManager::applyFeatures(bool skipFeatures) -{ - // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt - -#ifndef LL_RELEASE_FOR_DOWNLOAD - dump(); -#endif - - // scroll through all of these and set their corresponding control value - for(feature_map_t::iterator mIt = mFeatures.begin(); - mIt != mFeatures.end(); - ++mIt) - { - // skip features you want to skip - // do this for when you don't want to change certain settings - if(skipFeatures) - { - if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end()) - { - continue; - } - } - - // get the control setting - LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first); - if(ctrl == NULL) - { - LL_WARNS("RenderInit") << "AHHH! Control setting " << mIt->first << " does not exist!" << LL_ENDL; - continue; - } - - // handle all the different types - if(ctrl->isType(TYPE_BOOLEAN)) - { - gSavedSettings.setBOOL(mIt->first, (bool)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_S32)) - { - gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_U32)) - { - gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first)); - } - else if (ctrl->isType(TYPE_F32)) - { - gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first)); - } - else - { - LL_WARNS("RenderInit") << "AHHH! Control variable is not a numeric type!" << LL_ENDL; - } - } -} - -void LLFeatureManager::setGraphicsLevel(U32 level, bool skipFeatures) -{ - LLViewerShaderMgr::sSkipReload = true; - flush_glerror(); // Whatever may have already happened (e.g., to cause us to change), don't let confuse it with new initializations. - applyBaseMasks(); - - // if we're passed an invalid level, default to "Low" - std::string features(isValidGraphicsLevel(level)? getNameForGraphicsLevel(level) : "Low"); - - maskFeatures(features); - - applyFeatures(skipFeatures); - - LLViewerShaderMgr::sSkipReload = false; - LLViewerShaderMgr::instance()->setShaders(); - gPipeline.refreshCachedSettings(); -} - -void LLFeatureManager::applyBaseMasks() -{ - // reapply masks - mFeatures.clear(); - - LLFeatureList* maskp = findMask("all"); - if(maskp == NULL) - { - LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL; - return; - } - - mFeatures = maskp->getFeatures(); - - // mask class - if (mGPUClass >= 0 && mGPUClass < 6) - { - const char* class_table[] = - { - "Class0", - "Class1", - "Class2", - "Class3", - "Class4", - "Class5", - }; - - LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL; - maskFeatures(class_table[mGPUClass]); - } - else - { - LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL; - maskFeatures("Unknown"); - } - - // now all those wacky ones - if (gGLManager.mIsNVIDIA) - { - maskFeatures("NVIDIA"); - } - if (gGLManager.mIsAMD) - { - maskFeatures("AMD"); - } - if (gGLManager.mIsIntel) - { - maskFeatures("Intel"); - } - if (gGLManager.mGLVersion < 3.f) - { - maskFeatures("OpenGLPre30"); - } - if (gGLManager.mNumTextureImageUnits <= 8) - { - maskFeatures("TexUnit8orLess"); - } - if (gGLManager.mVRAM > 512) - { - maskFeatures("VRAMGT512"); - } - if (gGLManager.mVRAM < 2048) - { - maskFeatures("VRAMLT2GB"); - } - if (gGLManager.mGLVersion < 3.99f) - { - maskFeatures("GL3"); - } - - // now mask by gpu string - // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces - std::string gpustr = mGPUString; - for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter) - { - if (*iter == ' ') - { - *iter = '_'; - } - } - - //LL_INFOS() << "Masking features from gpu table match: " << gpustr << LL_ENDL; - maskFeatures(gpustr); - - if (isSafe()) - { - maskFeatures("safe"); - } -} - -LLSD LLFeatureManager::getRecommendedSettingsMap() -{ - // Create the map and fill it with the hardware recommended settings. - // It's needed to create an initial Default graphics preset (MAINT-6435). - // The process is similar to the one LLFeatureManager::applyRecommendedSettings() does. - - LLSD map(LLSD::emptyMap()); - - U32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5)); - LL_INFOS("RenderInit") << "Getting the map of recommended settings for level " << level << LL_ENDL; - - std::string features(isValidGraphicsLevel(level) ? getNameForGraphicsLevel(level) : "Low"); - - maskFeatures(features); - - LLControlVariable* ctrl = gSavedSettings.getControl("RenderQualityPerformance"); // include the quality value for correct preset loading - map["RenderQualityPerformance"]["Value"] = (LLSD::Integer)level; - map["RenderQualityPerformance"]["Comment"] = ctrl->getComment();; - map["RenderQualityPerformance"]["Persist"] = 1; - map["RenderQualityPerformance"]["Type"] = LLControlGroup::typeEnumToString(ctrl->type()); - - - - for (feature_map_t::iterator mIt = mFeatures.begin(); mIt != mFeatures.end(); ++mIt) - { - LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first); - if (ctrl == NULL) - { - LL_WARNS("RenderInit") << "AHHH! Control setting " << mIt->first << " does not exist!" << LL_ENDL; - continue; - } - - if (ctrl->isType(TYPE_BOOLEAN)) - { - map[mIt->first]["Value"] = (LLSD::Boolean)getRecommendedValue(mIt->first); - } - else if (ctrl->isType(TYPE_S32) || ctrl->isType(TYPE_U32)) - { - map[mIt->first]["Value"] = (LLSD::Integer)getRecommendedValue(mIt->first); - } - else if (ctrl->isType(TYPE_F32)) - { - map[mIt->first]["Value"] = (LLSD::Real)getRecommendedValue(mIt->first); - } - else - { - LL_WARNS("RenderInit") << "AHHH! Control variable is not a numeric type!" << LL_ENDL; - continue; - } - map[mIt->first]["Comment"] = ctrl->getComment();; - map[mIt->first]["Persist"] = 1; - map[mIt->first]["Type"] = LLControlGroup::typeEnumToString(ctrl->type()); - } - - return map; -} +/**
+ * @file llfeaturemanager.cpp
+ * @brief LLFeatureManager class implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <fstream>
+
+#include <boost/regex.hpp>
+#include <boost/assign/list_of.hpp>
+
+#include "llfeaturemanager.h"
+#include "lldir.h"
+
+#include "llsys.h"
+#include "llgl.h"
+
+#include "llappviewer.h"
+#include "llbufferstream.h"
+#include "llnotificationsutil.h"
+#include "llviewercontrol.h"
+#include "llworld.h"
+#include "lldrawpoolterrain.h"
+#include "llviewertexturelist.h"
+#include "llversioninfo.h"
+#include "llwindow.h"
+#include "llui.h"
+#include "llcontrol.h"
+#include "llboost.h"
+#include "llweb.h"
+#include "llviewershadermgr.h"
+#include "llstring.h"
+#include "stringize.h"
+#include "llcorehttputil.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+#if LL_DARWIN
+const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
+#elif LL_LINUX
+const char FEATURE_TABLE_FILENAME[] = "featuretable_linux.txt";
+#else
+const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
+#endif
+
+#if 0 // consuming code in #if 0 below
+#endif
+LLFeatureInfo::LLFeatureInfo(const std::string& name, const bool available, const F32 level)
+ : mValid(true), mName(name), mAvailable(available), mRecommendedLevel(level)
+{
+}
+
+LLFeatureList::LLFeatureList(const std::string& name)
+ : mName(name)
+{
+}
+
+LLFeatureList::~LLFeatureList()
+{
+}
+
+void LLFeatureList::addFeature(const std::string& name, const bool available, const F32 level)
+{
+ if (mFeatures.count(name))
+ {
+ LL_WARNS("RenderInit") << "LLFeatureList::Attempting to add preexisting feature " << name << LL_ENDL;
+ }
+
+ LLFeatureInfo fi(name, available, level);
+ LL_DEBUGS_ONCE("RenderInit") << "Feature '" << name << "' "
+ << (available ? "" : "not " ) << "available"
+ << " at " << level
+ << LL_ENDL;
+ mFeatures[name] = fi;
+}
+
+bool LLFeatureList::isFeatureAvailable(const std::string& name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mAvailable;
+ }
+
+ LL_WARNS_ONCE("RenderInit") << "Feature " << name << " not on feature list!" << LL_ENDL;
+
+ // changing this to true so you have to explicitly disable
+ // something for it to be disabled
+ return true;
+}
+
+F32 LLFeatureList::getRecommendedValue(const std::string& name)
+{
+ if (mFeatures.count(name) && isFeatureAvailable(name))
+ {
+ LL_DEBUGS_ONCE("RenderInit") << "Setting '" << name << "' to recommended value " << mFeatures[name].mRecommendedLevel << LL_ENDL;
+ return mFeatures[name].mRecommendedLevel;
+ }
+
+ LL_WARNS_ONCE("RenderInit") << "Feature " << name << " not on feature list or not available!" << LL_ENDL;
+ return 0;
+}
+
+bool LLFeatureList::maskList(LLFeatureList &mask)
+{
+ LL_DEBUGS_ONCE() << "Masking with " << mask.mName << LL_ENDL;
+ //
+ // Lookup the specified feature mask, and overlay it on top of the
+ // current feature mask.
+ //
+
+ LLFeatureInfo mask_fi;
+
+ feature_map_t::iterator feature_it;
+ for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
+ {
+ mask_fi = feature_it->second;
+ //
+ // Look for the corresponding feature
+ //
+ if (!mFeatures.count(mask_fi.mName))
+ {
+ LL_WARNS("RenderInit") << "Feature " << mask_fi.mName << " in mask not in top level!" << LL_ENDL;
+ continue;
+ }
+
+ LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
+ if (mask_fi.mAvailable && !cur_fi.mAvailable)
+ {
+ LL_WARNS("RenderInit") << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << LL_ENDL;
+ continue;
+ }
+ cur_fi.mAvailable = mask_fi.mAvailable;
+ cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
+ LL_DEBUGS("RenderInit") << "Feature mask " << mask.mName
+ << " Feature " << mask_fi.mName
+ << " Mask: " << mask_fi.mRecommendedLevel
+ << " Now: " << cur_fi.mRecommendedLevel << LL_ENDL;
+ }
+
+ LL_DEBUGS("RenderInit") << "After applying mask " << mask.mName << std::endl;
+ // Will conditionally call dump only if the above message will be logged, thanks
+ // to it being wrapped by the LL_DEBUGS and LL_ENDL macros.
+ dump();
+ LL_CONT << LL_ENDL;
+
+ return true;
+}
+
+void LLFeatureList::dump()
+{
+ LL_DEBUGS("RenderInit") << "Feature list: " << mName << LL_ENDL;
+
+ LLFeatureInfo fi;
+ feature_map_t::iterator feature_it;
+ for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
+ {
+ fi = feature_it->second;
+ LL_DEBUGS("RenderInit") << "With " << mName << " feature " << fi.mName << " " << fi.mAvailable << ":" << fi.mRecommendedLevel << LL_ENDL;
+ }
+}
+
+static const std::vector<std::string> sGraphicsLevelNames = boost::assign::list_of
+ ("Low")
+ ("LowMid")
+ ("Mid")
+ ("MidHigh")
+ ("High")
+ ("HighUltra")
+ ("Ultra")
+;
+
+U32 LLFeatureManager::getMaxGraphicsLevel() const
+{
+ return sGraphicsLevelNames.size() - 1;
+}
+
+bool LLFeatureManager::isValidGraphicsLevel(U32 level) const
+{
+ return (level <= getMaxGraphicsLevel());
+}
+
+std::string LLFeatureManager::getNameForGraphicsLevel(U32 level) const
+{
+ if (isValidGraphicsLevel(level))
+ {
+ return sGraphicsLevelNames[level];
+ }
+ return STRINGIZE("Invalid graphics level " << level << ", valid are 0 .. "
+ << getMaxGraphicsLevel());
+}
+
+S32 LLFeatureManager::getGraphicsLevelForName(const std::string& name) const
+{
+ const std::string FixedFunction("FixedFunction");
+ std::string rname(name);
+ if (LLStringUtil::endsWith(rname, FixedFunction))
+ {
+ // chop off any "FixedFunction" suffix
+ rname = rname.substr(0, rname.length() - FixedFunction.length());
+ }
+ for (S32 i(0), iend(getMaxGraphicsLevel()); i <= iend; ++i)
+ {
+ if (sGraphicsLevelNames[i] == rname)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+LLFeatureList *LLFeatureManager::findMask(const std::string& name)
+{
+ if (mMaskList.count(name))
+ {
+ return mMaskList[name];
+ }
+
+ return NULL;
+}
+
+bool LLFeatureManager::maskFeatures(const std::string& name)
+{
+ LLFeatureList *maskp = findMask(name);
+ if (!maskp)
+ {
+ LL_DEBUGS("RenderInit") << "Unknown feature mask " << name << LL_ENDL;
+ return false;
+ }
+ LL_INFOS("RenderInit") << "Applying GPU Feature list: " << name << LL_ENDL;
+ return maskList(*maskp);
+}
+
+bool LLFeatureManager::loadFeatureTables()
+{
+ // *TODO - if I or anyone else adds something else to the skipped list
+ // make this data driven. Put it in the feature table and parse it
+ // correctly
+ mSkippedFeatures.insert("RenderAnisotropic");
+ mSkippedFeatures.insert("RenderGamma");
+ mSkippedFeatures.insert("RenderVBOEnable");
+ mSkippedFeatures.insert("RenderFogRatio");
+
+ // first table is install with app
+ std::string app_path = gDirUtilp->getAppRODataDir();
+ app_path += gDirUtilp->getDirDelimiter();
+
+ std::string filename;
+ filename = FEATURE_TABLE_FILENAME;
+
+ app_path += filename;
+
+ bool parse_ok = parseFeatureTable(app_path);
+
+ return parse_ok;
+}
+
+
+bool LLFeatureManager::parseFeatureTable(std::string filename)
+{
+ LL_INFOS("RenderInit") << "Attempting to parse feature table from " << filename << LL_ENDL;
+
+ llifstream file;
+ std::string name;
+ U32 version;
+
+ cleanupFeatureTables(); // in case an earlier attempt left partial results
+ file.open(filename.c_str()); /*Flawfinder: ignore*/
+
+ if (!file)
+ {
+ LL_WARNS("RenderInit") << "Unable to open feature table " << filename << LL_ENDL;
+ return false;
+ }
+
+ // Check file version
+ file >> name;
+ if (name != "version")
+ {
+ LL_WARNS("RenderInit") << filename << " does not appear to be a valid feature table!" << LL_ENDL;
+ return false;
+ }
+ file >> version;
+
+ mTableVersion = version;
+ LL_INFOS("RenderInit") << "Found feature table version " << version << LL_ENDL;
+
+ LLFeatureList *flp = NULL;
+ bool parse_ok = true;
+ while (parse_ok && file >> name )
+ {
+ char buffer[MAX_STRING]; /*Flawfinder: ignore*/
+
+ if (name.substr(0,2) == "//")
+ {
+ // This is a comment.
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (name == "list")
+ {
+ LL_DEBUGS("RenderInit") << "Before new list" << std::endl;
+ if (flp)
+ {
+ flp->dump();
+ }
+ else
+ {
+ LL_CONT << "No current list";
+ }
+ LL_CONT << LL_ENDL;
+
+ // It's a new mask, create it.
+ file >> name;
+ if (!mMaskList.count(name))
+ {
+ flp = new LLFeatureList(name);
+ mMaskList[name] = flp;
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "Overriding mask '" << name << "'; this is invalid!" << LL_ENDL;
+ parse_ok = false;
+ }
+ }
+ else
+ {
+ if (flp)
+ {
+ S32 available;
+ F32 recommended;
+ file >> available >> recommended;
+ flp->addFeature(name, available, recommended);
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "Specified parameter before <list> keyword!" << LL_ENDL;
+ parse_ok = false;
+ }
+ }
+ }
+ file.close();
+
+ if (!parse_ok)
+ {
+ LL_WARNS("RenderInit") << "Discarding feature table data from " << filename << LL_ENDL;
+ cleanupFeatureTables();
+ }
+
+ return parse_ok;
+}
+
+F32 gpu_benchmark();
+
+#if LL_WINDOWS
+
+F32 logExceptionBenchmark()
+{
+ // FIXME: gpu_benchmark uses many C++ classes on the stack to control state.
+ // SEH exceptions with our current exception handling options do not call
+ // destructors for these classes, resulting in an undefined state should
+ // this handler be invoked.
+ F32 gbps = -1;
+ __try
+ {
+ gbps = gpu_benchmark();
+ }
+ __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ // HACK - ensure that profiling is disabled
+ LLGLSLShader::finishProfile(false);
+
+ // convert to C++ styled exception
+ char integer_string[32];
+ sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
+ throw std::exception(integer_string);
+ }
+ return gbps;
+}
+#endif
+
+bool LLFeatureManager::loadGPUClass()
+{
+ if (!gSavedSettings.getBOOL("SkipBenchmark"))
+ {
+ F32 class1_gbps = gSavedSettings.getF32("RenderClass1MemoryBandwidth");
+ //get memory bandwidth from benchmark
+ F32 gbps;
+ try
+ {
+#if LL_WINDOWS
+ gbps = logExceptionBenchmark();
+#else
+ gbps = gpu_benchmark();
+#endif
+ }
+ catch (const std::exception& e)
+ {
+ gbps = -1.f;
+ LL_WARNS("RenderInit") << "GPU benchmark failed: " << e.what() << LL_ENDL;
+ }
+
+ mGPUMemoryBandwidth = gbps;
+
+ // bias by CPU speed
+ F32 cpu_basis_mhz = gSavedSettings.getF32("RenderCPUBasis");
+ F32 cpu_mhz = (F32) gSysCPU.getMHz();
+ F32 cpu_bias = llclamp(cpu_mhz / cpu_basis_mhz, 0.5f, 1.f);
+ gbps *= cpu_bias;
+
+ if (gbps < 0.f)
+ { //couldn't bench, default to Low
+ #if LL_DARWIN
+ //GLVersion is misleading on OSX, just default to class 3 if we can't bench
+ LL_WARNS("RenderInit") << "Unable to get an accurate benchmark; defaulting to class 3" << LL_ENDL;
+ mGPUClass = GPU_CLASS_3;
+ #else
+ mGPUClass = GPU_CLASS_0;
+ #endif
+ }
+ else if (gbps <= class1_gbps)
+ {
+ mGPUClass = GPU_CLASS_1;
+ }
+ else if (gbps <= class1_gbps *2.f)
+ {
+ mGPUClass = GPU_CLASS_2;
+ }
+ else if (gbps <= class1_gbps*4.f)
+ {
+ mGPUClass = GPU_CLASS_3;
+ }
+ else if (gbps <= class1_gbps*8.f)
+ {
+ mGPUClass = GPU_CLASS_4;
+ }
+ else
+ {
+ mGPUClass = GPU_CLASS_5;
+ }
+
+ #if LL_WINDOWS
+ const F32Gigabytes MIN_PHYSICAL_MEMORY(2);
+
+ LLMemory::updateMemoryInfo();
+ F32Gigabytes physical_mem = LLMemory::getMaxMemKB();
+ if (MIN_PHYSICAL_MEMORY > physical_mem && mGPUClass > GPU_CLASS_1)
+ {
+ // reduce quality on systems that don't have enough memory
+ mGPUClass = (EGPUClass)(mGPUClass - 1);
+ }
+ #endif //LL_WINDOWS
+ } //end if benchmark
+ else
+ {
+ //setting says don't benchmark MAINT-7558
+ LL_WARNS("RenderInit") << "Setting 'SkipBenchmark' is true; defaulting to class 1 (may be required for some GPUs)" << LL_ENDL;
+
+ mGPUClass = GPU_CLASS_1;
+ }
+
+ // defaults
+ mGPUString = gGLManager.getRawGLString();
+ mGPUSupported = true;
+
+ return true; // indicates that a gpu value was established
+}
+
+void LLFeatureManager::cleanupFeatureTables()
+{
+ std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
+ mMaskList.clear();
+}
+
+void LLFeatureManager::initSingleton()
+{
+ // load the tables
+ loadFeatureTables();
+
+ // get the gpu class
+ loadGPUClass();
+
+ // apply the base masks, so we know if anything is disabled
+ applyBaseMasks();
+}
+
+void LLFeatureManager::applyRecommendedSettings()
+{
+ // apply saved settings
+ // cap the level at 2 (high)
+ U32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5));
+
+ LL_INFOS("RenderInit") << "Applying Recommended Features for level " << level << LL_ENDL;
+
+ setGraphicsLevel(level, false);
+ gSavedSettings.setU32("RenderQualityPerformance", level);
+
+ // now apply the tweaks to draw distance
+ // these are double negatives, because feature masks only work by
+ // downgrading values, so i needed to make a true value go to false
+ // for certain cards, thus the awkward name, "Disregard..."
+ if(!gSavedSettings.getBOOL("Disregard96DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 96.0f);
+ }
+ else if(!gSavedSettings.getBOOL("Disregard128DefaultDrawDistance"))
+ {
+ gSavedSettings.setF32("RenderFarClip", 128.0f);
+ }
+}
+
+void LLFeatureManager::applyFeatures(bool skipFeatures)
+{
+ // see featuretable.txt / featuretable_linux.txt / featuretable_mac.txt
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ dump();
+#endif
+
+ // scroll through all of these and set their corresponding control value
+ for(feature_map_t::iterator mIt = mFeatures.begin();
+ mIt != mFeatures.end();
+ ++mIt)
+ {
+ // skip features you want to skip
+ // do this for when you don't want to change certain settings
+ if(skipFeatures)
+ {
+ if(mSkippedFeatures.find(mIt->first) != mSkippedFeatures.end())
+ {
+ continue;
+ }
+ }
+
+ // get the control setting
+ LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
+ if(ctrl == NULL)
+ {
+ LL_WARNS("RenderInit") << "AHHH! Control setting " << mIt->first << " does not exist!" << LL_ENDL;
+ continue;
+ }
+
+ // handle all the different types
+ if(ctrl->isType(TYPE_BOOLEAN))
+ {
+ gSavedSettings.setBOOL(mIt->first, (bool)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_S32))
+ {
+ gSavedSettings.setS32(mIt->first, (S32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_U32))
+ {
+ gSavedSettings.setU32(mIt->first, (U32)getRecommendedValue(mIt->first));
+ }
+ else if (ctrl->isType(TYPE_F32))
+ {
+ gSavedSettings.setF32(mIt->first, (F32)getRecommendedValue(mIt->first));
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "AHHH! Control variable is not a numeric type!" << LL_ENDL;
+ }
+ }
+}
+
+void LLFeatureManager::setGraphicsLevel(U32 level, bool skipFeatures)
+{
+ LLViewerShaderMgr::sSkipReload = true;
+ flush_glerror(); // Whatever may have already happened (e.g., to cause us to change), don't let confuse it with new initializations.
+ applyBaseMasks();
+
+ // if we're passed an invalid level, default to "Low"
+ std::string features(isValidGraphicsLevel(level)? getNameForGraphicsLevel(level) : "Low");
+
+ maskFeatures(features);
+
+ applyFeatures(skipFeatures);
+
+ LLViewerShaderMgr::sSkipReload = false;
+ LLViewerShaderMgr::instance()->setShaders();
+ gPipeline.refreshCachedSettings();
+}
+
+void LLFeatureManager::applyBaseMasks()
+{
+ // reapply masks
+ mFeatures.clear();
+
+ LLFeatureList* maskp = findMask("all");
+ if(maskp == NULL)
+ {
+ LL_WARNS("RenderInit") << "AHH! No \"all\" in feature table!" << LL_ENDL;
+ return;
+ }
+
+ mFeatures = maskp->getFeatures();
+
+ // mask class
+ if (mGPUClass >= 0 && mGPUClass < 6)
+ {
+ const char* class_table[] =
+ {
+ "Class0",
+ "Class1",
+ "Class2",
+ "Class3",
+ "Class4",
+ "Class5",
+ };
+
+ LL_INFOS("RenderInit") << "Setting GPU Class to " << class_table[mGPUClass] << LL_ENDL;
+ maskFeatures(class_table[mGPUClass]);
+ }
+ else
+ {
+ LL_INFOS("RenderInit") << "Setting GPU Class to Unknown" << LL_ENDL;
+ maskFeatures("Unknown");
+ }
+
+ // now all those wacky ones
+ if (gGLManager.mIsNVIDIA)
+ {
+ maskFeatures("NVIDIA");
+ }
+ if (gGLManager.mIsAMD)
+ {
+ maskFeatures("AMD");
+ }
+ if (gGLManager.mIsIntel)
+ {
+ maskFeatures("Intel");
+ }
+ if (gGLManager.mGLVersion < 3.f)
+ {
+ maskFeatures("OpenGLPre30");
+ }
+ if (gGLManager.mNumTextureImageUnits <= 8)
+ {
+ maskFeatures("TexUnit8orLess");
+ }
+ if (gGLManager.mVRAM > 512)
+ {
+ maskFeatures("VRAMGT512");
+ }
+ if (gGLManager.mVRAM < 2048)
+ {
+ maskFeatures("VRAMLT2GB");
+ }
+ if (gGLManager.mGLVersion < 3.99f)
+ {
+ maskFeatures("GL3");
+ }
+
+ // now mask by gpu string
+ // Replaces ' ' with '_' in mGPUString to deal with inability for parser to handle spaces
+ std::string gpustr = mGPUString;
+ for (std::string::iterator iter = gpustr.begin(); iter != gpustr.end(); ++iter)
+ {
+ if (*iter == ' ')
+ {
+ *iter = '_';
+ }
+ }
+
+ //LL_INFOS() << "Masking features from gpu table match: " << gpustr << LL_ENDL;
+ maskFeatures(gpustr);
+
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
+
+LLSD LLFeatureManager::getRecommendedSettingsMap()
+{
+ // Create the map and fill it with the hardware recommended settings.
+ // It's needed to create an initial Default graphics preset (MAINT-6435).
+ // The process is similar to the one LLFeatureManager::applyRecommendedSettings() does.
+
+ LLSD map(LLSD::emptyMap());
+
+ U32 level = llmax(GPU_CLASS_0, llmin(mGPUClass, GPU_CLASS_5));
+ LL_INFOS("RenderInit") << "Getting the map of recommended settings for level " << level << LL_ENDL;
+
+ std::string features(isValidGraphicsLevel(level) ? getNameForGraphicsLevel(level) : "Low");
+
+ maskFeatures(features);
+
+ LLControlVariable* ctrl = gSavedSettings.getControl("RenderQualityPerformance"); // include the quality value for correct preset loading
+ map["RenderQualityPerformance"]["Value"] = (LLSD::Integer)level;
+ map["RenderQualityPerformance"]["Comment"] = ctrl->getComment();;
+ map["RenderQualityPerformance"]["Persist"] = 1;
+ map["RenderQualityPerformance"]["Type"] = LLControlGroup::typeEnumToString(ctrl->type());
+
+
+
+ for (feature_map_t::iterator mIt = mFeatures.begin(); mIt != mFeatures.end(); ++mIt)
+ {
+ LLControlVariable* ctrl = gSavedSettings.getControl(mIt->first);
+ if (ctrl == NULL)
+ {
+ LL_WARNS("RenderInit") << "AHHH! Control setting " << mIt->first << " does not exist!" << LL_ENDL;
+ continue;
+ }
+
+ if (ctrl->isType(TYPE_BOOLEAN))
+ {
+ map[mIt->first]["Value"] = (LLSD::Boolean)getRecommendedValue(mIt->first);
+ }
+ else if (ctrl->isType(TYPE_S32) || ctrl->isType(TYPE_U32))
+ {
+ map[mIt->first]["Value"] = (LLSD::Integer)getRecommendedValue(mIt->first);
+ }
+ else if (ctrl->isType(TYPE_F32))
+ {
+ map[mIt->first]["Value"] = (LLSD::Real)getRecommendedValue(mIt->first);
+ }
+ else
+ {
+ LL_WARNS("RenderInit") << "AHHH! Control variable is not a numeric type!" << LL_ENDL;
+ continue;
+ }
+ map[mIt->first]["Comment"] = ctrl->getComment();;
+ map[mIt->first]["Persist"] = 1;
+ map[mIt->first]["Type"] = LLControlGroup::typeEnumToString(ctrl->type());
+ }
+
+ return map;
+}
|
