summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan "Geenz" Goodman <geenz@geenzo.com>2026-06-30 17:15:12 -0400
committerJonathan "Geenz" Goodman <geenz@geenzo.com>2026-06-30 17:15:12 -0400
commitcd62a49879374c84b66e1ee963e161c3e6109977 (patch)
tree8b023e916f13cd76daa6479ad62e4aac6734a9ff
parentc299c8038e73e541cafe3c8df135752cb48b903c (diff)
parentc99cd251383ed17b4dd1210ee0701d2a3b4dd0e5 (diff)
Merge branch 'release/26.3' into geenz/texture-streaming-tweaks
-rw-r--r--autobuild.xml2
-rw-r--r--indra/llinventory/llsettingssky.cpp31
-rw-r--r--indra/llwindow/llwindowwin32.cpp10
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings.xml2
-rw-r--r--indra/newview/app_settings/shaders/class1/deferred/CASF.glsl8
-rw-r--r--indra/newview/lldebugview.cpp2
-rw-r--r--indra/newview/lllogininstance.cpp1
-rw-r--r--indra/newview/llsidepanelinventory.cpp2
-rw-r--r--indra/newview/llstartup.cpp4
-rw-r--r--indra/newview/lltextureview.cpp2
-rw-r--r--indra/newview/lltoastnotifypanel.cpp2
-rw-r--r--indra/newview/llviewerdisplay.cpp5
-rw-r--r--indra/newview/llviewermessage.cpp5
-rw-r--r--indra/newview/llvieweroctree.cpp2
-rw-r--r--indra/newview/llviewertexture.cpp161
-rw-r--r--indra/newview/llviewertexture.h3
-rw-r--r--indra/newview/llvoavatar.cpp10
-rw-r--r--indra/newview/llvocache.cpp2
-rwxr-xr-xindra/newview/res/viewerRes.rc2
-rw-r--r--indra/newview/skins/default/xui/en/floater_script.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_navigation_bar.xml4
22 files changed, 173 insertions, 93 deletions
diff --git a/autobuild.xml b/autobuild.xml
index 3a2cfa0b46..1456dca104 100644
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -3390,7 +3390,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
<key>license_file</key>
<string>docs/LICENSE-source.txt</string>
<key>copyright</key>
- <string>Copyright (c) 2020, Linden Research, Inc.</string>
+ <string>Copyright (c) 2026, Linden Research, Inc.</string>
<key>version_file</key>
<string>newview/viewer_version.txt</string>
<key>name</key>
diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp
index 6f7510fef2..4957cf3c02 100644
--- a/indra/llinventory/llsettingssky.cpp
+++ b/indra/llinventory/llsettingssky.cpp
@@ -653,39 +653,14 @@ void LLSettingsSky::blend(LLSettingsBase::ptr_t &end, F64 blendf)
}
}
+ mHasLegacyHaze |= lerp_legacy_float(mHazeHorizon, mLegacyHazeHorizon, other->mHazeHorizon, other->mLegacyHazeHorizon, 0.19f, (F32)blendf);
mHasLegacyHaze |= lerp_legacy_float(mHazeDensity, mLegacyHazeDensity, other->mHazeDensity, other->mLegacyHazeDensity, 0.7f, (F32)blendf);
mHasLegacyHaze |= lerp_legacy_float(mDistanceMultiplier, mLegacyDistanceMultiplier, other->mDistanceMultiplier, other->mLegacyDistanceMultiplier, 0.8f, (F32)blendf);
+ mHasLegacyHaze |= lerp_legacy_float(mDensityMultiplier, mLegacyDensityMultiplier, other->mDensityMultiplier, other->mLegacyDensityMultiplier, 0.0001f, (F32)blendf);
mHasLegacyHaze |= lerp_legacy_color(mAmbientColor, mLegacyAmbientColor, other->mAmbientColor, other->mLegacyAmbientColor, LLColor3(0.25f, 0.25f, 0.25f), (F32)blendf);
+ mHasLegacyHaze |= lerp_legacy_color(mBlueHorizon, mLegacyBlueHorizon, other->mBlueHorizon, other->mLegacyBlueHorizon, LLColor3(0.4954f, 0.4954f, 0.6399f), (F32)blendf);
mHasLegacyHaze |= lerp_legacy_color(mBlueDensity, mLegacyBlueDensity, other->mBlueDensity, other->mLegacyBlueDensity, LLColor3(0.2447f, 0.4487f, 0.7599f), (F32)blendf);
- if (mLegacyHazeHorizon == mLegacyDensityMultiplier == mLegacyBlueHorizon)
- {
- // mHazeHorizon coupled with mDensityMultiplier, mDistanceMultiplier and
- // drastic blue horizon changes can result in a very bright sky during
- // the transition. Purpose of this code is to calculate a 'fake level'
- // and use it to even out brightness change.
- //
- // Example values that make lerp-based individual transition painfully bright:
- // Start: 3 Haze Horiz, 0.1 Density, 6.54 Distance, white ambient, white blue horizon
- // End: 0.03 Haze Horiz, 0.775 Density, 90.95 Distance, black ambient, black blue horizon
- F32 strt_level = mHazeHorizon * mDensityMultiplier * mBlueHorizon.length();
- F32 end_level = other->mHazeHorizon * other->mDensityMultiplier * other->mBlueHorizon.length();
- mHasLegacyHaze |= lerp_legacy_float(mHazeHorizon, mLegacyHazeHorizon, other->mHazeHorizon, other->mLegacyHazeHorizon, 0.19f, (F32)blendf);
- mHasLegacyHaze |= lerp_legacy_float(mDensityMultiplier, mLegacyDensityMultiplier, other->mDensityMultiplier, other->mLegacyDensityMultiplier, 0.0001f, (F32)blendf);
- mHasLegacyHaze |= lerp_legacy_color(mBlueHorizon, mLegacyBlueHorizon, other->mBlueHorizon, other->mLegacyBlueHorizon, LLColor3(0.4954f, 0.4954f, 0.6399f), (F32)blendf);
-
- // lerp the fake level instead of density multiplier to avoid brightening the sky too much.
- // This makes density multiplier non linear.
- F32 new_level = lerp(strt_level, end_level, (F32)blendf);
- mDensityMultiplier = new_level / (mHazeHorizon * mBlueHorizon.length());
- }
- else
- {
- // default values are used, so we should lerp settings independently
- mHasLegacyHaze |= lerp_legacy_float(mHazeHorizon, mLegacyHazeHorizon, other->mHazeHorizon, other->mLegacyHazeHorizon, 0.19f, (F32)blendf);
- mHasLegacyHaze |= lerp_legacy_float(mDensityMultiplier, mLegacyDensityMultiplier, other->mDensityMultiplier, other->mLegacyDensityMultiplier, 0.0001f, (F32)blendf);
- mHasLegacyHaze |= lerp_legacy_color(mBlueHorizon, mLegacyBlueHorizon, other->mBlueHorizon, other->mLegacyBlueHorizon, LLColor3(0.4954f, 0.4954f, 0.6399f), (F32)blendf);
- }
parammapping_t defaults = other->getParameterMap();
stringset_t skip = getSkipInterpolateKeys();
stringset_t slerps = getSlerpKeys();
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index c185fc6c4a..6230cc3026 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -4704,6 +4704,16 @@ void LLWindowWin32::requestHighPerformanceGPU() const
<< ", Vendor: 0x" << std::hex << desc.VendorId << std::dec
<< ", Flags: " << desc.Flags << LL_ENDL;
}
+ // Skip Microsoft Basic Render Driver, it's a placeholder for missing drivers
+ else if (description.find("Microsoft Basic Render Driver") != std::string::npos)
+ {
+ // User is likely missing drivers, so log a warning.
+ // Don't consider this adapter as a valid selection.
+ LL_WARNS("Window") << "Adapter " << adapterIndex << ": " << description
+ << ", Dedicated VRAM: " << (desc.DedicatedVideoMemory / 1024 / 1024) << " MB"
+ << ", Vendor: 0x" << std::hex << desc.VendorId << std::dec
+ << ", Flags: " << desc.Flags << LL_ENDL;
+ }
else
{
LL_INFOS("Window") << "Adapter " << adapterIndex << ": " << description
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 88367540e9..b85bf003fc 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -2184,7 +2184,7 @@ if (DARWIN)
set(MACOSX_BUNDLE_BUNDLE_NAME "SecondLife")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${VIEWER_SHORT_VERSION}.${VIEWER_VERSION_REVISION}")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${VIEWER_SHORT_VERSION}${VIEWER_MACOSX_PHASE}${VIEWER_REVISION}")
- set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2020")
+ set(MACOSX_BUNDLE_COPYRIGHT "Copyright © Linden Research, Inc. 2026")
set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "SecondLife.nib")
set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "LLApplication")
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 6e25d2e835..519041bbeb 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5903,7 +5903,7 @@
<key>Type</key>
<string>S32</string>
<key>Value</key>
- <integer>110</integer>
+ <integer>127</integer>
</map>
<key>NotificationChannelRightMargin</key>
<map>
diff --git a/indra/newview/app_settings/shaders/class1/deferred/CASF.glsl b/indra/newview/app_settings/shaders/class1/deferred/CASF.glsl
index 8e12d09443..df58f68556 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/CASF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/CASF.glsl
@@ -1171,12 +1171,12 @@ vec3 linear_to_srgb(vec3 cl);
// - Float zero is mapped to center of integers (so clear to integer zero is a nice default for atomic max usage).
// Burns 3 ops for conversion {shift,or,xor}.
//==============================================================================================================================
- AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000));}
- AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000));}
+ AU1 AFisToU1(AU1 x){return x^(( AShrSU1(x,AU1_(31)))|AU1_(0x80000000u));}
+ AU1 AFisFromU1(AU1 x){return x^((~AShrSU1(x,AU1_(31)))|AU1_(0x80000000u));}
//------------------------------------------------------------------------------------------------------------------------------
// Just adjust high 16-bit value (useful when upper part of 32-bit word is a 16-bit float value).
- AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000));}
- AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000));}
+ AU1 AFisToHiU1(AU1 x){return x^(( AShrSU1(x,AU1_(15)))|AU1_(0x80000000u));}
+ AU1 AFisFromHiU1(AU1 x){return x^((~AShrSU1(x,AU1_(15)))|AU1_(0x80000000u));}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//_____________________________________________________________/\_______________________________________________________________
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
index 3941b82e75..246a9d28e4 100644
--- a/indra/newview/lldebugview.cpp
+++ b/indra/newview/lldebugview.cpp
@@ -101,7 +101,7 @@ void LLDebugView::init()
addChild(gSceneMonitorView);
gSceneMonitorView->setRect(rect);
- r.set(150, rect.getHeight() - 60, 820, 110);
+ r.set(150, rect.getHeight() - 60, 835, 110);
LLTextureView::Params tvp;
tvp.name("gTextureView");
tvp.rect(r);
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 0358233637..02f013ad3c 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -134,6 +134,7 @@ void LLLoginInstance::reconnect()
{
// Sort of like connect, only using the pre-existing
// request params.
+ mAttemptComplete = false;
std::vector<std::string> uris;
LLGridManager::getInstance()->getLoginURIs(uris);
mLoginModule->connect(uris.front(), mRequestData);
diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp
index b48417bd71..623db82939 100644
--- a/indra/newview/llsidepanelinventory.cpp
+++ b/indra/newview/llsidepanelinventory.cpp
@@ -402,8 +402,6 @@ void LLSidepanelInventory::onOpen(const LLSD& key)
gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", (U32)time_corrected());
}
#endif
-
- gAgent.showLatestFeatureNotification("inventory");
}
void LLSidepanelInventory::performActionOnSelection(const std::string &action)
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 55f23eb021..60c6a5c194 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -1353,6 +1353,10 @@ bool idle_startup()
{
set_startup_status(0.30f, LLTrans::getString("LoginInitializingWorld"), gAgent.mMOTD);
do_startup_frame();
+
+ // close login UI before world UI is initialized, if it is still visible
+ LLPanelLogin::closePanel();
+
// We should have an agent id by this point.
llassert(!(gAgentID == LLUUID::null));
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
index 1cc26896b1..1c341a06cd 100644
--- a/indra/newview/lltextureview.cpp
+++ b/indra/newview/lltextureview.cpp
@@ -250,7 +250,7 @@ void LLTextureBar::draw()
gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
// Draw the progress bar.
- S32 bar_width = 100;
+ S32 bar_width = 110;
S32 bar_left = 260;
left = bar_left;
right = left + bar_width;
diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp
index 3de61dffc7..e905a879cb 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -301,7 +301,7 @@ void LLToastNotifyPanel::init( LLRect rect, bool show_images )
mIsScriptDialog = (notif_name == "ScriptDialog" || notif_name == "ScriptDialogGroup");
static LLCachedControl<S32> btn_width(gSavedSettings, "ToastButtonWidth", 90);
- static LLCachedControl<S32> script_button_width(gSavedSettings, "ScriptToastButtonWidth", 110);
+ static LLCachedControl<S32> script_button_width(gSavedSettings, "ScriptToastButtonWidth", 127);
mButtonWidth = mIsScriptDialog ? script_button_width : btn_width;
bool is_content_trusted = (notif_name != "LoadWebPage");
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 9dfa9a0efd..4773a8a555 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -217,9 +217,10 @@ void display_update_camera()
{
final_far *= 0.5f;
}
- else if (LLViewerTexture::sDesiredDiscardBias > 2.f)
+ // When system memory is critically low or recovering, shrink draw distance.
+ else if (LLViewerTexture::getSystemMemoryBudgetFactor() > 1.f)
{
- final_far = llmax(32.f, final_far / (LLViewerTexture::sDesiredDiscardBias - 1.f));
+ final_far = llmax(32.f, final_far / LLViewerTexture::getSystemMemoryBudgetFactor());
}
LLViewerCamera::getInstance()->setFar(final_far);
LLVOAvatar::sRenderDistance = llclamp(final_far, 16.f, 256.f);
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 812ba76551..612af029b9 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -3372,9 +3372,10 @@ void send_agent_update(bool force_send, bool send_reliable)
static F32 last_draw_disatance_step = 1024;
F32 memory_limited_draw_distance = gAgentCamera.mDrawDistance;
- if (LLViewerTexture::isSystemMemoryCritical())
+ if (LLViewerTexture::getSystemMemoryBudgetFactor() > 1.f)
{
- // If we are low on memory, reduce requested draw distance
+ // We are critcally low on memory or recovering,
+ // limit requested draw distance
memory_limited_draw_distance = llmax(gAgentCamera.mDrawDistance / LLViewerTexture::getSystemMemoryBudgetFactor(), gAgentCamera.mDrawDistance / 2.f);
}
diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp
index b1673d2232..3b79666f7c 100644
--- a/indra/newview/llvieweroctree.cpp
+++ b/indra/newview/llvieweroctree.cpp
@@ -1143,6 +1143,8 @@ void LLOcclusionCullingGroup::checkOcclusion()
GLuint query_result; // Will be # samples drawn, or a boolean depending on mHasOcclusionQuery2 (both are type GLuint)
{
LL_PROFILE_ZONE_NAMED_CATEGORY_OCTREE("co - query result");
+ // Ping watchdog before a blocking GL_QUERY_RESULT GPU call
+ LLAppViewer::instance()->pingMainloopTimeout("Display:CullQueryResult");
glGetQueryObjectuiv(mOcclusionQuery[LLViewerCamera::sCurCameraID], GL_QUERY_RESULT, &query_result);
}
#if LL_TRACK_PENDING_OCCLUSION_QUERIES
diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp
index c2cdd54910..545264b980 100644
--- a/indra/newview/llviewertexture.cpp
+++ b/indra/newview/llviewertexture.cpp
@@ -128,6 +128,7 @@ constexpr F32 MEMORY_CHECK_WAIT_TIME = 1.0f;
constexpr F32 MIN_VRAM_BUDGET = 768.f;
F32 LLViewerTexture::sFreeVRAMMegabytes = MIN_VRAM_BUDGET;
F32 LLViewerTexture::sWindowPixelArea = 1.f;
+F32 LLViewerTexture::sSysMemoryFactor = 1.f;
LLViewerTexture::EDebugTexels LLViewerTexture::sDebugTexelsMode = LLViewerTexture::DEBUG_TEXELS_OFF;
@@ -504,6 +505,13 @@ void LLViewerTexture::initClass()
LLImageGL::sDefaultGLTexture = LLViewerFetchedTexture::sDefaultImagep->getGLTexture();
}
+S32Megabytes get_render_free_main_memory_treshold()
+{
+ static LLCachedControl<U32> min_free_main_memory(gSavedSettings, "RenderMinFreeMainMemoryThreshold", 512);
+ const U32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory);
+ return MIN_FREE_MAIN_MEMORY;
+}
+
//static
void LLViewerTexture::updateClass()
{
@@ -537,23 +545,24 @@ void LLViewerTexture::updateClass()
// get an estimate of how much video memory we're using
// NOTE: our metrics miss about half the vram we use, so this biases high but turns out to typically be within 5% of the real number
- F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
+ F32 vram_used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc);
// For debugging purposes, it's useful to be able to set the VRAM budget manually.
// But when manual control is not enabled, use the VRAM divisor.
// While we're at it, assume we have 1024 to play with at minimum when the divisor is in use. Works more elegantly with the logic below this.
// -Geenz 2025-03-21
- F32 budget = max_vram_budget == 0 ? llmax(1024, (F32)gGLManager.mVRAM / tex_vram_divisor) : (F32)max_vram_budget;
+ F32 vram_budget = max_vram_budget == 0 ? llmax(1024, (F32)gGLManager.mVRAM / tex_vram_divisor) : (F32)max_vram_budget;
// Try to leave at least half a GB for everyone else and for bias,
// but keep at least 768MB for ourselves
// Viewer can 'overshoot' target when scene changes, if viewer goes over budget it
// can negatively impact performance, so leave 20% of a breathing room for
// 'bias' calculation to kick in.
- F32 target = llmax(llmin(budget - 512.f, budget * 0.8f), MIN_VRAM_BUDGET);
- sFreeVRAMMegabytes = target - used;
+ F32 vram_target = llmax(llmin(vram_budget - 512.f, vram_budget * 0.8f), MIN_VRAM_BUDGET);
+ sFreeVRAMMegabytes = vram_target - vram_used;
+ const S32Megabytes free_sys_mem = getFreeSystemMemory();
- F32 over_pct = (used - target) / target;
+ F32 over_pct = (vram_used - vram_target) / vram_target;
// Watermark-driven discard-bias controller. One clean signal: the bias
// climbs while used VRAM is above the high watermark, relaxes below the
@@ -573,18 +582,41 @@ void LLViewerTexture::updateClass()
static LLCachedControl<F32> bias_ramp(gSavedSettings, "TextureDiscardBiasRampRate", 2.0f);
static LLCachedControl<F32> bias_decay(gSavedSettings, "TextureDiscardBiasDecayRate", 1.0f);
- F32 high_frac = llclamp((F32)wm_high, 0.1f, 1.f);
- F32 low_frac = llclamp((F32)wm_low, 0.05f, high_frac);
- F32 high = budget * high_frac;
- F32 low = budget * low_frac;
- F32 cap = llmax((F32)bias_max, 0.f);
- F32 dt = (F32)gFrameIntervalSeconds;
+ F32 backoff_target = vram_target * llclamp((F32)backoff_start, 0.05f, 1.f);
+ F32 cap = llmax((F32)max_mult, 1.0001f);
+ F32 dt = (F32)gFrameIntervalSeconds;
+
+ // Skip the full-list iteration when there is no pressure to react to:
+ // mult already at baseline, last-ditch at zero, and used well clear of
+ // the backoff target. Worst case the controller picks up the spike one
+ // frame later, from `used` alone.
+ bool need_predict = sMemoryPressureMultiplier > 1.001f
+ || sLastDitchMinDiscard > 0.f
+ || vram_used > backoff_target * 0.5f;
- if (used > high)
+ S64 pending_bytes_increase = 0;
+ S64 pending_bytes_decrease = 0;
+ if (need_predict)
{
sDiscardBias += llmax((F32)bias_ramp, 0.f) * dt;
}
- else if (used < low)
+
+ // 1024 * 512 = 524288: matches the unit reduction at line 513.
+ constexpr F32 BYTES_TO_USED_UNITS = 1.f / 524288.f;
+ F32 predicted_used = vram_used
+ + (F32)pending_bytes_increase * BYTES_TO_USED_UNITS
+ - (F32)pending_bytes_decrease * BYTES_TO_USED_UNITS;
+ F32 predicted_over = predicted_used / llmax(backoff_target, 1.f);
+
+ // High water mark: when used crosses budget * high_water, skip the
+ // smoothed convergence and slam the controller into hard-cap state.
+ // Recovers the historical 90% behavior - immediate aggressive
+ // response instead of waiting for the lerp to chase the target.
+ static LLCachedControl<F32> high_water(gSavedSettings, "TextureMemoryHighWaterMark", 0.8f);
+ bool above_high_water = vram_used >= vram_budget * llclamp((F32)high_water, 0.5f, 1.f);
+
+ F32 target_mult = llclamp(powf(llmax(predicted_over, 1.f), llmax((F32)prediction_gain, 0.0001f)), 1.f, cap);
+ if (above_high_water)
{
sDiscardBias -= llmax((F32)bias_decay, 0.f) * dt;
}
@@ -596,25 +628,86 @@ void LLViewerTexture::updateClass()
if (s_pressure_log_timer.getElapsedTimeF32() > 1.f)
{
s_pressure_log_timer.reset();
+ F32 over = vram_used / llmax(backoff_target, 1.f);
LL_INFOS("TextureStream") << "pressure"
- << " discard_bias=" << sDiscardBias
- << " progress=" << getMemoryPressureProgress()
- << " used=" << used
- << " budget=" << budget
- << " high=" << high
- << " low=" << low
- << " over_pct=" << over_pct
- << " legacy_bias=" << sDesiredDiscardBias
+ << " mult=" << sMemoryPressureMultiplier
+ << " target_mult=" << target_mult
+ << " progress=" << progress
+ << " used=" << vram_used
+ << " predicted=" << predicted_used
+ << " target=" << vram_target
+ << " over=" << over
+ << " pred_over=" << predicted_over
+ << " in+=" << (S32)(pending_bytes_increase / 1024 / 1024)
+ << "MB in-=" << (S32)(pending_bytes_decrease / 1024 / 1024)
+ << "MB bias=" << sDesiredDiscardBias
+ << " ldmin=" << sLastDitchMinDiscard
<< " dsq=" << (S32)gTextureList.mDownScaleQueue.size()
<< LL_ENDL;
}
}
bool is_sys_low = isSystemMemoryLow();
+ bool is_sys_critically_low = isSystemMemoryCritical();
bool is_low = is_sys_low || over_pct > 0.f;
static bool was_low = false;
+ static bool sys_was_low = false;
+
+ // System memory factor
+ // sSysMemoryFactor affects draw distance
+ //
+ // We only decrement when more than 406MB is free, but increment
+ // when below 256MB free. This should provide a stable value
+ // in the 256-406MB range to avoid draw range fluctuations.
+ //
+ // Draw range reduction is a last resort, texture bias is supposed
+ // to free at least some memory before we get here.
+ // Note: textures were mostly moved to vram, we might want to
+ // detach texture bias from system memory.
+ if (is_sys_critically_low)
+ {
+ const S32Megabytes MIN_FREE_MAIN_MEMORY(get_render_free_main_memory_treshold() / 2);
+ // debt is a negative value since MIN_FREE_MAIN_MEMORY > free memory.
+ S32 sys_budget_debt = free_sys_mem - MIN_FREE_MAIN_MEMORY;
+
+ // Leave some padding, otherwise we will crash out of memory before hitting factor 2.
+ const S32Megabytes PAD_BUFFER(32);
+ S32Megabytes budget_target = MIN_FREE_MAIN_MEMORY - PAD_BUFFER;
+ if (!sys_was_low)
+ {
+ // Result should range from 1 at 0 debt to 2 at -224 debt, 2.14 at -256MB
+ F32 new_factor = 1.f - (F32)sys_budget_debt / (F32)budget_target;
+ sSysMemoryFactor = llmax(sSysMemoryFactor, new_factor);
+ }
+ else
+ {
+ // Slowly ramp up factor to free memory (increasing factor decreases draw range)
+ constexpr F32 MAX_INCREMENT = 0.05f;
+ F32 increment = MAX_INCREMENT * llmax(-(F32)sys_budget_debt / (F32)budget_target, 0.f);
+ sSysMemoryFactor += increment * gFrameIntervalSeconds;
+ }
+ sSysMemoryFactor = llclamp(sSysMemoryFactor, 1.f, 2.f);
+ }
+ else
+ {
+ const S32Megabytes MIN_FREE_MAIN_MEMORY(get_render_free_main_memory_treshold() / 2);
+ // Only start ramping down when we have breathing room.
+ // This should be under the value of isSystemMemoryLow to not throw texture
+ // bias into 1.5+ territory each time we fluctuate around isSystemMemoryLow's
+ // treshold.
+ const S32Megabytes MEM_THRESHOLD = MIN_FREE_MAIN_MEMORY + S32Megabytes(150);
+ if (free_sys_mem > MEM_THRESHOLD && sSysMemoryFactor > 1.f)
+ {
+ // Ramp down factor over time.
+ constexpr F32 DECREMENT = 0.02f;
+ sSysMemoryFactor -= DECREMENT * gFrameIntervalSeconds;
+ sSysMemoryFactor = llclamp(sSysMemoryFactor, 1.f, 2.f);
+ }
+ }
+ sys_was_low = is_sys_critically_low;
+ // VRAM memory bias
if (is_low && !was_low)
{
if (is_sys_low)
@@ -662,12 +755,11 @@ void LLViewerTexture::updateClass()
// lower discard bias over time when at least 10% of budget is free
constexpr F32 FREE_PERCENTAGE_TRESHOLD = -0.1f;
- constexpr U32 FREE_SYS_MEM_TRESHOLD = 100;
- static LLCachedControl<U32> min_free_main_memory(gSavedSettings, "RenderMinFreeMainMemoryThreshold", 512);
- const S32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory() + FREE_SYS_MEM_TRESHOLD);
+ constexpr U32 FREE_SYS_MEM_THRESHOLD = 100; // 100MB more than isSystemMemoryLow to avoid fluctuations.
+ const S32Megabytes MIN_FREE_MAIN_MEMORY(get_render_free_main_memory_treshold() + S32Megabytes(FREE_SYS_MEM_THRESHOLD));
if (sDesiredDiscardBias > 1.f
&& over_pct < FREE_PERCENTAGE_TRESHOLD
- && getFreeSystemMemory() > MIN_FREE_MAIN_MEMORY
+ && free_sys_mem > MIN_FREE_MAIN_MEMORY
&& !eviction_in_flight)
{
static LLCachedControl<F32> high_mem_discard_decrement(gSavedSettings, "RenderHighMemMinDiscardDecrement", .1f);
@@ -797,13 +889,6 @@ U32Megabytes LLViewerTexture::getFreeSystemMemory()
return physical_res;
}
-S32Megabytes get_render_free_main_memory_treshold()
-{
- static LLCachedControl<U32> min_free_main_memory(gSavedSettings, "RenderMinFreeMainMemoryThreshold", 512);
- const U32Megabytes MIN_FREE_MAIN_MEMORY(min_free_main_memory);
- return MIN_FREE_MAIN_MEMORY;
-}
-
//static
bool LLViewerTexture::isSystemMemoryLow()
{
@@ -816,18 +901,10 @@ bool LLViewerTexture::isSystemMemoryCritical()
return getFreeSystemMemory() < get_render_free_main_memory_treshold() / 2;
}
+// static
F32 LLViewerTexture::getSystemMemoryBudgetFactor()
{
- const S32Megabytes MIN_FREE_MAIN_MEMORY(get_render_free_main_memory_treshold() / 2);
- S32 free_budget = (S32Megabytes)getFreeSystemMemory() - MIN_FREE_MAIN_MEMORY;
- if (free_budget < 0)
- {
- // Leave some padding, otherwise we will crash out of memory before hitting factor 2.
- const S32Megabytes PAD_BUFFER(32);
- // Result should range from 1 at 0 free budget to 2 at -224 free budget, 2.14 at -256MB
- return 1.f - free_budget / (MIN_FREE_MAIN_MEMORY - PAD_BUFFER);
- }
- return 1.f;
+ return sSysMemoryFactor;
}
//end of static functions
diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h
index 390a6caa58..51ef9f8206 100644
--- a/indra/newview/llviewertexture.h
+++ b/indra/newview/llviewertexture.h
@@ -116,6 +116,8 @@ public:
static void updateClass();
static bool isSystemMemoryLow();
static bool isSystemMemoryCritical();
+
+ // Ranges from 1 (no RAM deficit) to 2 (RAM deficit)
static F32 getSystemMemoryBudgetFactor();
LLViewerTexture(bool usemipmaps = true);
@@ -272,6 +274,7 @@ public:
// estimated free memory for textures, by bias calculation
static F32 sFreeVRAMMegabytes;
+ static F32 sSysMemoryFactor;
// Viewport pixel area, refreshed once per frame. Hoisted to keep the
// per-texture hot path out of gViewerWindow.
static F32 sWindowPixelArea;
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index 2f39a76156..460570d0e5 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -8575,6 +8575,10 @@ bool LLVOAvatar::processFullyLoadedChange(bool loading)
if (changed && isSelf())
{
+ // Agent's own avatar doesn't track bakes the same way as other avatars.
+ // So just update here, on cloud removal.
+ markBodyPartsComplexityDirty();
+
// to know about outfit switching
LLAvatarRenderNotifier::getInstance()->updateNotificationState();
}
@@ -10170,6 +10174,10 @@ void LLVOAvatar::onInitialBakedTextureLoaded( bool success, LLViewerFetchedTextu
}
if (final || !success )
{
+ if (selfp)
+ {
+ selfp->markBodyPartsComplexityDirty();
+ }
delete avatar_idp;
}
}
@@ -11458,7 +11466,7 @@ void LLVOAvatar::calculateUpdateRenderComplexity()
// Store results
mVisualComplexity = total_cost;
- // Call the existing reporting function with the aggregated lists
+ // Call the reporting function with the aggregated lists
processComplexityCostChange(hud_complexity_list, object_complexity_list);
// Stop processing until something changes
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index 5d456b1a19..7618739b3c 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -488,7 +488,7 @@ void LLVOCacheEntry::updateDebugSettings()
static const F32 MIN_RADIUS = 1.0f;
F32 draw_radius = gAgentCamera.mDrawDistance;
- if (LLViewerTexture::isSystemMemoryCritical())
+ if (LLViewerTexture::getSystemMemoryBudgetFactor() > 1.f)
{
// Factor is intended to go from 1.0 to 2.0
// For safety cap reduction at 50%, we don't want to go below half of draw distance
diff --git a/indra/newview/res/viewerRes.rc b/indra/newview/res/viewerRes.rc
index dc2ba5f171..c9ca758555 100755
--- a/indra/newview/res/viewerRes.rc
+++ b/indra/newview/res/viewerRes.rc
@@ -157,7 +157,7 @@ BEGIN
VALUE "FileDescription", "Second Life"
VALUE "FileVersion", "${VIEWER_VERSION_MAJOR}.${VIEWER_VERSION_MINOR}.${VIEWER_VERSION_PATCH}.${VIEWER_VERSION_REVISION}"
VALUE "InternalName", "Second Life"
- VALUE "LegalCopyright", "Copyright (c) 2020, Linden Research, Inc."
+ VALUE "LegalCopyright", "Copyright (c) 2026, Linden Research, Inc."
VALUE "OriginalFilename", "SecondLife.exe"
VALUE "ProductName", "Second Life"
VALUE "ProductVersion", "${VIEWER_VERSION_MAJOR}.${VIEWER_VERSION_MINOR}.${VIEWER_VERSION_PATCH}.${VIEWER_VERSION_REVISION}"
diff --git a/indra/newview/skins/default/xui/en/floater_script.xml b/indra/newview/skins/default/xui/en/floater_script.xml
index ae6e68de17..20feec615c 100644
--- a/indra/newview/skins/default/xui/en/floater_script.xml
+++ b/indra/newview/skins/default/xui/en/floater_script.xml
@@ -8,8 +8,8 @@
can_dock="true"
can_minimize="true"
visible="false"
- width="350"
+ width="402"
can_resize="false"
- min_width="350"
+ min_width="402"
min_height="200">
</floater>
diff --git a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml
index 4d924a6c24..fcfd2357b5 100644
--- a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml
@@ -196,8 +196,8 @@
text_color="LtGray"
tool_tip="Drag Landmarks here for quick access to your favorite places in Second Life!"
top="13"
- valign="bottom"
- width="290">
+ valign="bottom"
+ width="310">
Places you save to your favorites bar will appear here.
</label>
<!-- More button actually is a text box. -->