summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-07-06 20:53:53 +0300
committerAndrey Kleshchev <117672381+akleshchev@users.noreply.github.com>2026-07-07 19:54:57 +0300
commit1c8144b1708420b30c54472a5139d451034e89de (patch)
tree1115da96f7cbde43b8a77d4d5ea801fb9ef66c7a
parentacde733c6205f7355fcce436b8252c2e003b4a55 (diff)
#4604 Restore system memory factor, but in LLMemory
-rw-r--r--indra/llcommon/llmemory.cpp91
-rw-r--r--indra/llcommon/llmemory.h9
-rw-r--r--indra/llcommon/llsys.cpp2
-rw-r--r--indra/llcommon/llsys.h8
-rw-r--r--indra/newview/llappviewerwin32.cpp15
-rw-r--r--indra/newview/llviewerdisplay.cpp5
-rw-r--r--indra/newview/llviewermessage.cpp8
-rw-r--r--indra/newview/llvocache.cpp8
8 files changed, 133 insertions, 13 deletions
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 31424bfed3..4e44b9a56a 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2026, 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
@@ -73,6 +73,12 @@ U32Kilobytes LLMemory::sAllocatedMemInKB(0);
U32Kilobytes LLMemory::sAllocatedPageSizeInKB(0);
+LLFrameTimer LLMemory::sMemoryCheckTimer;
+F32 LLMemory::sSysMemoryFactor = 1.f;
+U32 LLMemory::sFactorLastFrameCount = 0;
+
+static const S32Megabytes MEM_LOW_THRESHOLD = S32Megabytes(256);
+
static LLTrace::SampleStatHandle<F64Megabytes> sAllocatedMem("allocated_mem", "active memory in use by application");
static LLTrace::SampleStatHandle<F64Megabytes> sVirtualMem("virtual_mem", "virtual memory assigned to application");
@@ -105,12 +111,13 @@ void LLMemory::updateMemoryInfo()
{
LL_PROFILE_ZONE_SCOPED;
+ sMemoryCheckTimer.reset();
sMaxPhysicalMemInKB = gSysMemory.getPhysicalMemoryKB();
LLMemoryInfo::updateAvailableMemory();
#if LL_WINDOWS
- // On windows getAvailableMemoryKB fills sAvailPhysicalMemInKB,
+ // On windows updateAvailableMemory fills sAvailPhysicalMemInKB,
//sAllocatedMemInKB and sAllocatedPageSizeInKB
sample(sVirtualMem, sAllocatedPageSizeInKB);
@@ -194,6 +201,86 @@ void LLMemory::logMemoryInfo(bool update)
LL_INFOS() << llformat("Current max usable memory: %.2f MB", sMaxPhysicalMemInKB / 1024.0) << LL_ENDL;
}
+void LLMemory::updateFreeSystemMemory()
+{
+ if (sMemoryCheckTimer.getElapsedTimeF32() >= 1.f) //once per second.
+ {
+ LLMemory::updateMemoryInfo(); // resets the timer
+ }
+}
+
+F32 LLMemory::getSystemMemoryBudgetFactor()
+{
+ // Only update once per frame
+ U32 current_frame = LLFrameTimer::getFrameCount();
+ if (sFactorLastFrameCount == current_frame)
+ {
+ return sSysMemoryFactor;
+ }
+ sFactorLastFrameCount = current_frame;
+
+ updateFreeSystemMemory();
+#if LL_WINDOWS
+ S32Megabytes free_sys_mem = getAvailableCommitMemMB();
+#else
+ S32Megabytes free_sys_mem = getAvailableMemKB();
+#endif
+ bool is_sys_low = free_sys_mem < MEM_LOW_THRESHOLD;
+ static bool was_low = false;
+
+ // 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_low)
+ {
+ // debt is a negative value since MIN_FREE_MAIN_MEMORY > free memory.
+ S32Megabytes sys_budget_debt = free_sys_mem - MEM_LOW_THRESHOLD;
+
+ // Leave some padding, otherwise we will crash out of memory before hitting factor 2.
+ const S32Megabytes PAD_BUFFER(32);
+ S32Megabytes budget_target = MEM_LOW_THRESHOLD - PAD_BUFFER;
+ if (!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.value() / (F32)budget_target.value();
+ 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.value() / (F32)budget_target.value(), 0.f);
+ sSysMemoryFactor += increment * LLFrameTimer::getFrameDeltaTimeF32();
+ }
+ sSysMemoryFactor = llclamp(sSysMemoryFactor, 1.f, 2.f);
+ }
+ else
+ {
+ // 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
+ // threshold.
+ const S32Megabytes MEM_THRESHOLD = MEM_LOW_THRESHOLD + S32Megabytes(150);
+ if (free_sys_mem > MEM_THRESHOLD && sSysMemoryFactor > 1.f)
+ {
+ // Ramp down factor over time.
+ constexpr F32 DECREMENT = 0.02f;
+ sSysMemoryFactor -= DECREMENT * LLFrameTimer::getFrameDeltaTimeF32();
+ sSysMemoryFactor = llclamp(sSysMemoryFactor, 1.f, 2.f);
+ }
+ }
+ was_low = is_sys_low;
+
+ return sSysMemoryFactor;
+}
+
#if LL_WINDOWS
//static
U32Megabytes LLMemory::getAvailableCommitMemMB()
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 290f6e03d1..efcd7aadc4 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2026, 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
@@ -27,6 +27,7 @@
#define LLMEMORY_H
#include "linden_common.h"
+#include "llframetimer.h"
#include "llunits.h"
#include "stdtypes.h"
#if !LL_WINDOWS
@@ -428,6 +429,7 @@ public:
static void initMaxHeapSizeGB(F32Gigabytes max_heap_size);
static void updateMemoryInfo() ;
static void logMemoryInfo(bool update = false);
+ static F32 getSystemMemoryBudgetFactor();
#if LL_WINDOWS
// Commit charge is a Windows-only concept, combines page file and ram
@@ -437,6 +439,7 @@ public:
static U32Kilobytes getMaxMemKB() ;
static U32Kilobytes getAllocatedMemKB() ;
private:
+ static void updateFreeSystemMemory();
// LLMemoryInfo directly updates memory stats
friend class LLMemoryInfo;
@@ -447,6 +450,10 @@ private:
static U32Kilobytes sAllocatedPageSizeInKB ;
static U32Kilobytes sMaxHeapSizeInKB;
+
+ static LLFrameTimer sMemoryCheckTimer;
+ static F32 sSysMemoryFactor;
+ static U32 sFactorLastFrameCount;
};
// LLRefCount moved to llrefcount.h
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 568a6b36dc..bc48fc0fd2 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2026, 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
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index 709fb29a82..0abbe047ad 100644
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -4,7 +4,7 @@
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
+ * Copyright (C) 2026, 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
@@ -134,12 +134,12 @@ public:
static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib.
#endif
- // Updates LLMemory's values (which ones is OS specific).
+ // Updates LLMemory's values.
static void updateAvailableMemory();
// Retrieve a map of memory statistics. The keys of the map are platform-
- // dependent. The values are in kilobytes to try to avoid integer overflow.
- // On windows updates LLMemory values.
+ // dependent.
+ // On Windows updates LLMemory values.
LLSD getStatsMap() const;
// Re-fetch memory data (as reported by stream() and getStatsMap()) from the
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 5c2594e85c..8a9ddbcde6 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -82,6 +82,7 @@
#include "BugSplat.h"
#include "boost/json.hpp" // Boost.Json
#include "llagent.h" // for agent location
+#include "llmemory.h"
#include "llstartup.h"
#include "llviewerregion.h"
#include "llvoavatarself.h" // for agent name
@@ -181,11 +182,15 @@ namespace
sBugSplatSender->setAttribute(WCSTR(L"VRAM"), WCSTR(STRINGIZE(gGLManager.mVRAM)));
sBugSplatSender->setAttribute(WCSTR(L"RAM"), WCSTR(STRINGIZE(gSysMemory.getPhysicalMemoryKB().value())));
- // Memory usage at crash time (can be 1s obsolete)
- sBugSplatSender->setAttribute(WCSTR(L"MemAllocatedKB"), WCSTR(std::to_string(LLMemory::getAllocatedMemKB().value())));
- sBugSplatSender->setAttribute(WCSTR(L"MemAvailableKB"), WCSTR(std::to_string(LLMemory::getAvailableMemKB().value())));
- sBugSplatSender->setAttribute(WCSTR(L"MemMaxPhysicalKB"), WCSTR(std::to_string(LLMemory::getMaxMemKB().value())));
- sBugSplatSender->setAttribute(WCSTR(L"MemAvailCommitMB"), WCSTR(std::to_string(LLMemory::getAvailableCommitMemMB().value())));
+ const U32 avail_kb = LLMemory::getAvailableMemKB().value();
+ if (avail_kb != U32_MAX) // filter out initial values, if one is not set, all are not set
+ {
+ // Memory usage at crash time (can be 1s obsolete)
+ sBugSplatSender->setAttribute(WCSTR(L"MemAllocatedKB"), WCSTR(std::to_string(LLMemory::getAllocatedMemKB().value())));
+ sBugSplatSender->setAttribute(WCSTR(L"MemAvailableKB"), WCSTR(std::to_string(LLMemory::getAvailableMemKB().value())));
+ sBugSplatSender->setAttribute(WCSTR(L"MemMaxPhysicalKB"), WCSTR(std::to_string(LLMemory::getMaxMemKB().value())));
+ sBugSplatSender->setAttribute(WCSTR(L"MemAvailCommitMB"), WCSTR(std::to_string(LLMemory::getAvailableCommitMemMB().value())));
+ }
if (gAgent.getRegion())
{
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index 9f1b0d75f3..0d50ba6fe2 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -217,6 +217,11 @@ void display_update_camera()
{
final_far *= 0.5f;
}
+ // When system memory is critically low or recovering, shrink draw distance.
+ else if (const F32 mem_factor = LLMemory::getSystemMemoryBudgetFactor(); mem_factor > 1.f)
+ {
+ final_far = llmax(32.f, final_far / mem_factor);
+ }
LLViewerCamera::getInstance()->setFar(final_far);
LLVOAvatar::sRenderDistance = llclamp(final_far, 16.f, 256.f);
gViewerWindow->setup3DRender();
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 09f17fec40..8863dfb501 100644
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -40,6 +40,7 @@
#include "llinventorydefines.h"
#include "lllslconstants.h"
#include "llmaterialtable.h"
+#include "llmemory.h"
#include "llregionhandle.h"
#include "llsd.h"
#include "llsdserialize.h"
@@ -3371,6 +3372,13 @@ void send_agent_update(bool force_send, bool send_reliable)
static F32 last_draw_disatance_step = 1024;
F32 memory_limited_draw_distance = gAgentCamera.mDrawDistance;
+ const F32 mem_factor = LLMemory::getSystemMemoryBudgetFactor();
+ if (mem_factor > 1.f)
+ {
+ // We are critically low on memory or recovering,
+ // limit requested draw distance
+ memory_limited_draw_distance = llmax(gAgentCamera.mDrawDistance / mem_factor, gAgentCamera.mDrawDistance / 2.f);
+ }
if (tp_state == LLAgent::TELEPORT_ARRIVING || LLStartUp::getStartupState() < STATE_MISC)
{
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
index f3efe3f3bb..e513a3813f 100644
--- a/indra/newview/llvocache.cpp
+++ b/indra/newview/llvocache.cpp
@@ -32,6 +32,7 @@
#include "lldrawable.h"
#include "llviewerregion.h"
#include "llagentcamera.h"
+#include "llmemory.h"
#include "llsdserialize.h"
#include "llworld.h" // For LLWorld::getInstance()
//static variables
@@ -488,6 +489,13 @@ void LLVOCacheEntry::updateDebugSettings()
static const F32 MIN_RADIUS = 1.0f;
F32 draw_radius = gAgentCamera.mDrawDistance;
+ const F32 mem_factor = LLMemory::getSystemMemoryBudgetFactor();
+ if (mem_factor > 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
+ draw_radius = llmax(draw_radius / mem_factor, draw_radius / 2.f);
+ }
const F32 clamped_min_radius = llclamp((F32) min_radius, MIN_RADIUS, draw_radius); // [1, mDrawDistance]
sNearRadius = MIN_RADIUS + ((clamped_min_radius - MIN_RADIUS) * adjust_factor);