From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:44:46 -0500 Subject: Second Life viewer sources 1.13.2.12 --- linden/indra/newview/llfeaturemanager.cpp | 861 ++++++++++++++++++++++++++++++ 1 file changed, 861 insertions(+) create mode 100644 linden/indra/newview/llfeaturemanager.cpp (limited to 'linden/indra/newview/llfeaturemanager.cpp') diff --git a/linden/indra/newview/llfeaturemanager.cpp b/linden/indra/newview/llfeaturemanager.cpp new file mode 100644 index 0000000..d3b0dbb --- /dev/null +++ b/linden/indra/newview/llfeaturemanager.cpp @@ -0,0 +1,861 @@ +/** + * @file llfeaturemanager.cpp + * @brief LLFeatureManager class implementation + * + * Copyright (c) 2003-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include + +#include "llviewerprecompiledheaders.h" + +#include "llfeaturemanager.h" +#include "lldir.h" + +#include "llsys.h" +#include "llgl.h" +#include "llsecondlifeurls.h" + +#include "llviewercontrol.h" +#include "llworld.h" +#include "pipeline.h" +#include "lldrawpoolterrain.h" +#include "llviewerimagelist.h" +#include "llwindow.h" +#include "llui.h" + +#if LL_WINDOWS +#include "lldxhardware.h" +#endif + +// +// externs +// +extern LLMemoryInfo gSysMemory; +extern LLCPUInfo gSysCPU; +extern void write_debug(const char *str); +extern void write_debug(const std::string& str); + +#if LL_DARWIN +const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt"; +#else +const char FEATURE_TABLE_FILENAME[] = "featuretable.txt"; +#endif + +const char GPU_TABLE_FILENAME[] = "gpu_table.txt"; + +LLFeatureManager *gFeatureManagerp = NULL; + +LLFeatureInfo::LLFeatureInfo(const char *name, const BOOL available, const S32 level) : mValid(TRUE) +{ + mName = name; + mAvailable = available; + mRecommendedLevel = level; +} + +LLFeatureList::LLFeatureList(const char *name) +{ + mName = name; +} + +LLFeatureList::~LLFeatureList() +{ +} + +void LLFeatureList::addFeature(const char *name, const BOOL available, const S32 level) +{ + if (mFeatures.count(name)) + { + llwarns << "LLFeatureList::Attempting to add preexisting feature " << name << llendl; + } + + LLFeatureInfo fi(name, available, level); + mFeatures[name] = fi; +} + +BOOL LLFeatureList::isFeatureAvailable(const char *name) +{ + if (mFeatures.count(name)) + { + return mFeatures[name].mAvailable; + } + + llwarns << "Feature " << name << " not on feature list!" << llendl; + return FALSE; +} + +S32 LLFeatureList::getRecommendedLevel(const char *name) +{ + if (mFeatures.count(name)) + { + return mFeatures[name].mRecommendedLevel; + } + + llwarns << "Feature " << name << " not on feature list!" << llendl; + return -1; +} + +BOOL LLFeatureList::maskList(LLFeatureList &mask) +{ + //llinfos << "Masking with " << mask.mName << llendl; + // + // 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)) + { + llwarns << "Feature " << mask_fi.mName << " in mask not in top level!" << llendl; + continue; + } + + LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName]; + if (mask_fi.mAvailable && !cur_fi.mAvailable) + { + llwarns << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << llendl; + continue; + } + cur_fi.mAvailable = mask_fi.mAvailable; + cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel); +#ifndef LL_RELEASE_FOR_DOWNLOAD + llinfos << "Feature mask " << mask.mName + << " Feature " << mask_fi.mName + << " Mask: " << mask_fi.mRecommendedLevel + << " Now: " << cur_fi.mRecommendedLevel << llendl; +#endif + } + +#if 0 && !LL_RELEASE_FOR_DOWNLOAD + llinfos << "After appling mask " << mask.mName << llendl; + dump(); +#endif + return TRUE; +} + +void LLFeatureList::dump() +{ + llinfos << "Feature list: " << mName << llendl; + llinfos << "--------------" << llendl; + + LLFeatureInfo fi; + feature_map_t::iterator feature_it; + for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it) + { + fi = feature_it->second; + llinfos << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << llendl; + } + llinfos << llendl; +} + +LLFeatureList *LLFeatureManager::findMask(const char *name) +{ + if (mMaskList.count(name)) + { + return mMaskList[name]; + } + + return NULL; +} + +BOOL LLFeatureManager::maskFeatures(const char *name) +{ + LLFeatureList *maskp = findMask(name); + if (!maskp) + { + llwarns << "Unknown feature mask " << name << llendl; + return FALSE; + } + llinfos << "Applying Feature Mask: " << name << llendl; + return maskList(*maskp); +} + +BOOL LLFeatureManager::loadFeatureTables() +{ + std::string data_path = gDirUtilp->getAppRODataDir(); + + data_path += gDirUtilp->getDirDelimiter(); + + data_path += FEATURE_TABLE_FILENAME; + + + char name[MAX_STRING+1]; + + llifstream file; + U32 version; + + file.open(data_path.c_str()); + + if (!file) + { + llwarns << "Unable to open feature table!" << llendl; + return FALSE; + } + + // Check file version + file >> name; + file >> version; + if (strcmp(name, "version")) + { + llwarns << data_path << " does not appear to be a valid feature table!" << llendl; + return FALSE; + } + + mTableVersion = version; + + LLFeatureList *flp = NULL; + while (!file.eof()) + { + char buffer[MAX_STRING]; + name[0] = 0; + + file >> name; + + if (strlen(name) >= 2 && + name[0] == '/' && + name[1] == '/') + { + // This is a comment. + file.getline(buffer, MAX_STRING); + continue; + } + + if (strlen(name) == 0) + { + // This is a blank line + file.getline(buffer, MAX_STRING); + continue; + } + + if (!strcmp(name, "list")) + { + if (flp) + { + //flp->dump(); + } + // It's a new mask, create it. + file >> name; + if (mMaskList.count(name)) + { + llerrs << "Overriding mask " << name << ", this is invalid!" << llendl; + } + + if (!flp) + { + // + // The first one is always the default + // + flp = this; + } + else + { + flp = new LLFeatureList(name); + mMaskList[name] = flp; + } + + } + else + { + if (!flp) + { + llerrs << "Specified parameter before keyword!" << llendl; + } + S32 available, recommended; + file >> available >> recommended; + flp->addFeature(name, available, recommended); + } + } + file.close(); + //flp->dump(); + + return TRUE; +} + +S32 LLFeatureManager::getGPUClass() +{ + return mGPUClass; +} + +S32 LLFeatureManager::loadGPUClass() +{ + std::string data_path = gDirUtilp->getAppRODataDir(); + + data_path += gDirUtilp->getDirDelimiter(); + + data_path += GPU_TABLE_FILENAME; + + llifstream file; + + file.open(data_path.c_str()); + + if (!file) + { + llwarns << "Unable to open GPU table: " << data_path << "!" << llendl; + return 0; + } + + std::string renderer = gGLManager.getRawGLString(); + for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i) + { + *i = tolower(*i); + } + + while (!file.eof()) + { + char buffer[MAX_STRING]; + buffer[0] = 0; + + file.getline(buffer, MAX_STRING); + + if (strlen(buffer) >= 2 && + buffer[0] == '/' && + buffer[1] == '/') + { + // This is a comment. + continue; + } + + if (strlen(buffer) == 0) + { + // This is a blank line + continue; + } + + char* cls, *label, *expr; + + label = strtok(buffer, "\t"); + expr = strtok(NULL, "\t"); + cls = strtok(NULL, "\t"); + + if (label == NULL || expr == NULL || cls == NULL) + { + continue; + } + + for (U32 i = 0; i < strlen(expr); i++) + { + expr[i] = tolower(expr[i]); + } + + char* ex = strtok(expr, ".*"); + char* rnd = (char*) renderer.c_str(); + + while (ex != NULL && rnd != NULL) + { + rnd = strstr(rnd, ex); + ex = strtok(NULL, ".*"); + } + + if (rnd != NULL) + { + file.close(); + llinfos << "GPU is " << label << llendl; + return (S32) strtol(cls, NULL, 10); + } + } + file.close(); + //flp->dump(); + + llwarns << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << llendl; + return 0; +} + +void LLFeatureManager::cleanupFeatureTables() +{ + std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer()); + mMaskList.clear(); +} + + +void LLFeatureManager::initCPUFeatureMasks() +{ + if (gSysMemory.getPhysicalMemory() <= 256*1024*1024) + { + maskFeatures("RAM256MB"); + } + else if (gSysMemory.getPhysicalMemory() <= 512*1024*1024) + { + //maskFeatures("RAM512MB"); + } + + if (gSysCPU.getMhz() < 1100) + { + maskFeatures("CPUSlow"); + } + if (isSafe()) + { + maskFeatures("safe"); + } +} + +void LLFeatureManager::initGraphicsFeatureMasks() +{ + mGPUClass = loadGPUClass(); + + if (mGPUClass >= 0 && mGPUClass < 4) + { + const char* class_table[] = + { + "Class0", + "Class1", + "Class2", + "Class3" + }; + + llinfos << "Setting GPU Class to " << class_table[mGPUClass] << llendl; + maskFeatures(class_table[mGPUClass]); + } + + if (!gGLManager.mHasFragmentShader) + { + maskFeatures("NoPixelShaders"); + } + if (!gGLManager.mHasVertexShader) + { + maskFeatures("NoVertexShaders"); + } + if (gGLManager.mIsNVIDIA) + { + maskFeatures("NVIDIA"); + } + if (gGLManager.mIsGF2or4MX) + { + maskFeatures("GeForce2"); + } + if (gGLManager.mIsATI) + { + maskFeatures("ATI"); + } + if (gGLManager.mIsRadeon8500) + { + maskFeatures("Radeon8500"); + } + if (gGLManager.mIsRadeon9700) + { + maskFeatures("Radeon9700"); + } + if (gGLManager.mIsGFFX) + { + maskFeatures("GeForceFX"); + } + if (gGLManager.mIsIntel) + { + maskFeatures("Brookdale"); + } + + if (gGLManager.mIsMobilityRadeon9000) + { + maskFeatures("MobilityRadeon9000"); + } + if (isSafe()) + { + maskFeatures("safe"); + } +} + +extern LLOSInfo gSysOS; + + +BOOL bad_hardware_dialog(const LLString &info_str, const LLString &url) +{ + if (!gSavedSettings.getWarning("AboutBadPCI")) + { + return FALSE; + } + + // XUI:translate + std::string msg = llformat( + "[SECOND_LIFE] has detected that there may be a problem with.\n" + "hardware or drivers on your computer. Often resolving these\n" + "issues can result in enhanced stability and performance.\n" + " \n" + "%s\n" + " \n" + "Would you like to view a web page with more detailed\n" + "information on this problem?\n", info_str.c_str()); + + // Warn them that runnin without DirectX 9 will + // not allow us to tell them about driver issues + S32 button = OSMessageBox(msg.c_str(), + "Warning", + OSMB_YESNO); + if (OSBTN_YES== button) + { + llinfos << "User quitting after detecting bad drivers" << llendl; + spawn_web_browser(url.c_str()); + return TRUE; + } + else + { + // Don't warn about bad PCI stuff again, they've clicked past it. + gSavedSettings.setWarning("AboutBadPCI", FALSE); + } + return FALSE; +} + +BOOL LLFeatureManager::initPCIFeatureMasks() +{ +#if LL_WINDOWS + BOOL exit_after_bad = FALSE; + + BOOL is_2000 = FALSE; + BOOL is_xp = FALSE; + + if (gSysOS.mMajorVer != 5) + { + // Unknown windows version number, exit!" + llwarns << "Unknown Windows major version " << gSysOS.mMajorVer << ", aborting detection!" << llendl; + return FALSE; + } + if (gSysOS.mMinorVer == 0) + { + is_2000 = TRUE; + } + else if (gSysOS.mMinorVer == 1) + { + is_xp = TRUE; + } + else + { + llwarns << "Unknown Windows minor version " << gSysOS.mMinorVer << ", aborting detection!" << llendl; + return FALSE; + } + + // This only works on Win32, as it relies on DX9 hardware detection + // The PCI masks are actually the inverse of the normal masks + // We actually look through the masks,and see if any hardware matches it. + // This is because the masks encode logic about + + // Check for the broken AMD AGP controllers (751, 761, 762) + + // Horrible cruddy fixed lookup table. + // Figure out what OS we're on, the version numbers are different. Sigh... + + LLDXDriverFile *dfilep = NULL; + LLDXDevice *devp = NULL; + + // AMD(1022) AGP controllers + // 7007 AMD-751 AGP Controller + // 700F AMD-761 AGP Controller + // 700D AMD-762 AGP Controller + devp = gDXHardware.findDevice("VEN_1022", "DEV_7007|DEV_700F|DEV_700D"); + if (devp) + { + // We're just pretty much screwed here, there are big problems with this hardware + // We've got trouble with the most recent nVidia drivers. Check for this and warn. + + // Note: Need to detect that we're running with older nVidia hardware, probably + exit_after_bad |= bad_hardware_dialog("AMD AGP Controller", + AMD_AGP_URL); + } + + // VIA(1106) AGP Controllers + // These need upgrading on both Win2K and WinXP + // + // 8305 VT8363/8365 CPU to AGP - Apollo KT133/KM133 + // 8598 VT82C598MVP/694X CPU to AGP - Apollo MVP3/Pro133A + // 8605 VT8605 CPU to AGP - Apollo PM133 + // B091 VT8633 CPU to AGP - Apollo Pro 266 + // B099 VT8366/A/T CPU to AGP - Apollo KT266/A/333 + // B168 VT8235 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X333 chipset + // B188 VT8237 CPU to AGP (AGP 2.0/3.0) - K8T800 + // B198 VT8237 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X600 chipset + + devp = gDXHardware.findDevice("VEN_1106", + "DEV_8305|DEV_8598|DEV_8605|DEV_B091|" + "DEV_B099|DEV_B168|DEV_B188|DEV_B198"); + if (devp) + { + BOOL out_of_date = FALSE; + // Wanted driver: VIAAGP1.SYS + // Version Format: M.mm.0000.vvvv + // M.mm - Major/minor OS version (5.0 for Win2000, 5.1 for WinXP) + // vvvv - driver version number + // + // Notes: + // 3442 is most recent as of 2/25/04, probably want at least 3430 (seems to be a common version) + + // These are DELIBERATE assignments inside if statements, blech. + if (dfilep = devp->findDriver("pci.sys")) + { + // Old driver: pci.sys + // Version: 5.01.2600.xxxx + // + // Notes: + // Default WinXP driver for B168, B198? + + // Old driver: pci.sys + // Version: 5.01.2195.xxxx + // + // Notes: + // Default Win2K driver for 8305? + + llwarns << "Detected pci.sys" << llendl; + write_debug("Old driver (pci.sys) for VIA detected!"); + out_of_date = TRUE; + } + else if (dfilep = devp->findDriver("VIAAGP.SYS")) + { + // Old driver: VIAAGP.SYS + // Version: 5.01.2600.xxxx + // + // Notes: + // Default WinXP driver for B09x? + + llwarns << "Detected VIAAGP.SYS" << llendl; + write_debug("Old driver (VIAAGP.SYS) for VIA detected!"); + out_of_date = TRUE; + } + else if (dfilep = devp->findDriver("VIAAGP1.SYS")) + { + if (dfilep->mVersion.getField(3) < 3430) + { + // They're using a pretty old version of the VIA AGP drivers + // Maybe they want to upgrade? + llwarns << "Detected VIAAGP1.SYS" << llendl; + write_debug("Old driver (VIAAGP1.SYS) for VIA detected!"); + out_of_date = TRUE; + } + } + if (out_of_date) + { + exit_after_bad |= bad_hardware_dialog("Out of date VIA AGP chipset driver", + VIA_URL); + } + } + + // Intel(8086) AGP controllers (Win2K) + // These particular controllers only may need drivers on Win2K + // + // 1A31 82845[MP|MZ] Processor to AGP Controller + // 2532 82850/860 Processor to AGP Controller + if (is_2000) + { + devp = gDXHardware.findDevice("VEN_8086", + "DEV_1A31"); + if (devp) + { + if (dfilep = devp->findDriver("pci.sys")) + { + // Old driver: pci.sys + // Version 5.01.21[9|6]5.xxxx + // + // Notes: + // Default driver for Win2K? Not sure what the "correct" driver is - + // maybe some variant of AGP440.SYS? + llwarns << "Detected pci.sys" << llendl; + write_debug("Old driver (pci.sys) for Intel 82845/850 on Win2K detected!"); + exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver", + INTEL_CHIPSET_URL); + } + } + } + + /* Removed 4/3/2006 by JC + After talking with Doug, we don't know what the proper driver + and/or version number should be for Intel 865. Regardless, this + code would _always_ complain if you had that chipset. + + // Intel(8086) AGP controllers (All) + // These particular controllers may need drivers on both Win2K and WinXP + // + // 2561 82845G/GL/GE/PE/GV Processor to AGP Controller + // 2571 82865G/PE/P/GV/28248P Processor to AGP Controller + devp = gDXHardware.findDevice("VEN_8086", + "DEV_2571"); + if (devp) + { + // Wanted driver: AGP440.SYS(?) + // + // Notes: + // Not sure, need to verify with an actual 82865/75 (Dell 8300?) + + // Old driver: pci.sys + // Version 5.01.21[9|6]5.xxxx + // + // Notes: + // Default driver for Win2K? Not sure what the "correct" driver is - + // maybe some variant of AGP440.SYS? + exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver", + INTEL_CHIPSET_URL); + } + */ + + + // SiS(1039) AGP controllers (All) + // These particular controllers may need drivers on both Win2K and WinXP + // + // 0001 SiS 530 + // 0002 SiS SG86C202(???) + // 0003 SiS 648FX + devp = gDXHardware.findDevice("VEN_1039", + "DEV_0001|DEV_0002|DEV_0003"); + if (devp) + { + BOOL out_of_date = FALSE; + // Wanted driver: SISAGPX.SYS + // + // Notes: + // Not sure, need to verify with an actual 82865/75 (Dell 8300?) + + // Old driver: pci.sys + // Version 5.01.21[9|6]5.xxxx + // + // Notes: + // Default driver for Win2K? Not sure what the "correct" driver is - + // maybe some variant of AGP440.SYS? + if (dfilep = devp->findDriver("pci.sys")) + { + // Old driver: pci.sys + // Version 5.01.21[9|6]5.xxxx + // + llwarns << "Detected pci.sys" << llendl; + write_debug("Old driver (pci.sys) for SiS detected!"); + out_of_date = TRUE; + } + + if (dfilep = devp->findDriver("sisagp.sys")) + { + // Old driver: pci.sys + // Version 5.01.21[9|6]5.xxxx + // + llwarns << "Detected sisagp.sys" << llendl; + write_debug("Old driver (sisagp.sys) for SiS detected!"); + out_of_date = TRUE; + } + + if (dfilep = devp->findDriver("sisagpx.sys")) + { + // Old driver: pci.sys + // Version 7.02.0000.xxxx + // + // Notes: + // Default driver for Win2K? Not sure what the "correct" driver is - + // maybe some variant of AGP440.SYS? + if (dfilep->mVersion.getField(3) < 1160) + { + out_of_date = TRUE; + llwarns << "Detected sisagpx.sys" << llendl; + write_debug("Old driver (sisagpx.sys) for SiS detected!"); + } + } + if (out_of_date) + { + exit_after_bad |= bad_hardware_dialog("Out of date SiS chipset driver", + SIS_CHIPSET_URL); + } + } + + return exit_after_bad; +#else + return TRUE; +#endif +} + +void LLFeatureManager::applyRecommendedFeatures() +{ + // see featuretable.txt + + llinfos << "Applying Recommended Features" << llendl; +#ifndef LL_RELEASE_FOR_DOWNLOAD + dump(); +#endif + + // Enabling AGP + if (getRecommendedLevel("RenderAGP")) + { + gSavedSettings.setBOOL("RenderUseAGP", TRUE); + } + else + { + gSavedSettings.setBOOL("RenderUseAGP", FALSE); + } + + // Anisotropic rendering + BOOL aniso = getRecommendedLevel("RenderAniso"); + LLImageGL::sGlobalUseAnisotropic = aniso; + gSavedSettings.setBOOL("RenderAnisotropic", LLImageGL::sGlobalUseAnisotropic); + + // Render Avatar Mode + BOOL avatar_vp = getRecommendedLevel("RenderAvatarVP"); + S32 avatar_mode = getRecommendedLevel("RenderAvatarMode"); + if (avatar_vp == FALSE) + avatar_mode = 0; + gSavedSettings.setBOOL("RenderAvatarVP", avatar_vp); + gSavedSettings.setS32("RenderAvatarMode", avatar_mode); + + // Render Distance + S32 far_clip = getRecommendedLevel("RenderDistance"); + gSavedSettings.setF32("RenderFarClip", (F32)far_clip); + + // Lighting + S32 lighting = getRecommendedLevel("RenderLighting"); + gSavedSettings.setS32("RenderLightingDetail", lighting); + + // ObjectBump + BOOL bump = getRecommendedLevel("RenderObjectBump"); + gSavedSettings.setBOOL("RenderObjectBump", bump); + + // Particle Count + S32 max_parts = getRecommendedLevel("RenderParticleCount"); + gSavedSettings.setS32("RenderMaxPartCount", max_parts); + LLViewerPartSim::setMaxPartCount(max_parts); + + // RippleWater + BOOL ripple = getRecommendedLevel("RenderRippleWater"); + gSavedSettings.setBOOL("RenderRippleWater", ripple); + + // Vertex Shaders + S32 shaders = getRecommendedLevel("VertexShaderEnable"); + gSavedSettings.setBOOL("VertexShaderEnable", shaders); + + // Terrain + S32 terrain = getRecommendedLevel("RenderTerrainDetail"); + gSavedSettings.setS32("RenderTerrainDetail", terrain); + LLDrawPoolTerrain::sDetailMode = terrain; + + // Set the amount of VRAM we have available + if (isSafe()) + { + gSavedSettings.setS32("GraphicsCardMemorySetting", 1); // 32 MB in 'safe' mode + } + else + { + S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting"); + // -1 indicates use default (max), don't change + if (idx != -1) + { + idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting + gSavedSettings.setS32("GraphicsCardMemorySetting", idx); + } + } +} -- cgit v1.1