From 140d5166e7b77060efdad6b0e011c444930966bc Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Sat, 4 Jul 2026 00:01:07 +0300 Subject: Fix vertual memory overflow in logs --- indra/llcommon/llsys.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'indra/llcommon/llsys.cpp') diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 803bab393c..dd46766cc2 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -956,15 +956,17 @@ LLSD LLMemoryInfo::loadStatsMap() state.dwLength = sizeof(state); GlobalMemoryStatusEx(&state); - DWORDLONG div = 1024; + static constexpr DWORDLONG div = 1024; stats.add("Percent Memory use", state.dwMemoryLoad/div); stats.add("Total Physical KB", state.ullTotalPhys/div); stats.add("Avail Physical KB", state.ullAvailPhys/div); stats.add("Total page KB", state.ullTotalPageFile/div); stats.add("Avail page KB", state.ullAvailPageFile/div); - stats.add("Total Virtual KB", state.ullTotalVirtual/div); - stats.add("Avail Virtual KB", state.ullAvailVirtual/div); + + static constexpr DWORDLONG mb_div = 1024 * 1024; + stats.add("Total Virtual MB", state.ullTotalVirtual/mb_div); // ~134 million MB + stats.add("Avail Virtual MB", state.ullAvailVirtual/mb_div); // SL-12122 - Call to GetPerformanceInfo() was removed here. Took // on order of 10 ms, causing unacceptable frame time spike every -- cgit v1.3 From acde733c6205f7355fcce436b8252c2e003b4a55 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:32:46 +0300 Subject: #4298 Record memory in bugsplat's attributes # Conflicts: # indra/newview/llappviewerwin32.cpp --- indra/llcommon/llmemory.cpp | 30 +++++++++------- indra/llcommon/llmemory.h | 8 +++++ indra/llcommon/llsys.cpp | 72 ++++++++++++++++++++++++++------------ indra/llcommon/llsys.h | 5 +-- indra/newview/llappviewerwin32.cpp | 7 ++++ 5 files changed, 84 insertions(+), 38 deletions(-) (limited to 'indra/llcommon/llsys.cpp') diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index ba48319a16..31424bfed3 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -52,6 +52,9 @@ //static +// On windows commit charge information is vital for OOM diagnosis. +U32Megabytes LLMemory::sAvailCommitMemInMB(U32_MAX); + // most important memory metric for texture streaming // On Windows, this should agree with resource monitor -> performance -> memory -> available // On OS X, this should be activity monitor -> memory -> (physical memory - memory used) @@ -104,21 +107,11 @@ void LLMemory::updateMemoryInfo() sMaxPhysicalMemInKB = gSysMemory.getPhysicalMemoryKB(); - U32Kilobytes avail_mem; - LLMemoryInfo::getAvailableMemoryKB(avail_mem); - sAvailPhysicalMemInKB = avail_mem; + LLMemoryInfo::updateAvailableMemory(); #if LL_WINDOWS - PROCESS_MEMORY_COUNTERS counters; - - if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) - { - LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL; - return ; - } - - sAllocatedMemInKB = U32Kilobytes::convert(U64Bytes(counters.WorkingSetSize)); - sAllocatedPageSizeInKB = U32Kilobytes::convert(U64Bytes(counters.PagefileUsage)); + // On windows getAvailableMemoryKB fills sAvailPhysicalMemInKB, + //sAllocatedMemInKB and sAllocatedPageSizeInKB sample(sVirtualMem, sAllocatedPageSizeInKB); #elif defined(LL_DARWIN) @@ -201,6 +194,17 @@ void LLMemory::logMemoryInfo(bool update) LL_INFOS() << llformat("Current max usable memory: %.2f MB", sMaxPhysicalMemInKB / 1024.0) << LL_ENDL; } +#if LL_WINDOWS +//static +U32Megabytes LLMemory::getAvailableCommitMemMB() +{ + // Commit charge combines page file and ram, + // theoretical limit is 128TB on 64bit windows. + // Store as MB instead of KB to prevent overflow. + return sAvailCommitMemInMB; +} +#endif + //static U32Kilobytes LLMemory::getAvailableMemKB() { diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index adc556d180..290f6e03d1 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -429,10 +429,18 @@ public: static void updateMemoryInfo() ; static void logMemoryInfo(bool update = false); +#if LL_WINDOWS + // Commit charge is a Windows-only concept, combines page file and ram + static U32Megabytes getAvailableCommitMemMB(); +#endif static U32Kilobytes getAvailableMemKB() ; static U32Kilobytes getMaxMemKB() ; static U32Kilobytes getAllocatedMemKB() ; private: + // LLMemoryInfo directly updates memory stats + friend class LLMemoryInfo; + + static U32Megabytes sAvailCommitMemInMB; static U32Kilobytes sAvailPhysicalMemInKB ; static U32Kilobytes sMaxPhysicalMemInKB ; static U32Kilobytes sAllocatedMemInKB; diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index dd46766cc2..568a6b36dc 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -43,6 +43,7 @@ #include "llerrorcontrol.h" #include "llevents.h" #include "llformat.h" +#include "llmemory.h" #include "llregex.h" #include "lltimer.h" #include "llsdserialize.h" @@ -802,15 +803,13 @@ U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const } //static -void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_mem_kb) +void LLMemoryInfo::updateAvailableMemory() { LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; #if LL_WINDOWS - // Sigh, this shouldn't be a static method, then we wouldn't have to - // reload this data separately from refresh() - LLSD statsMap(loadStatsMap()); - - avail_mem_kb = (U32Kilobytes)statsMap["Avail Physical KB"].asInteger(); + // On windows loadStatsMap will fill sAvailPhysicalMemInKB, + // sAvailCommitMemInMB, sAllocatedMemInKB and sAllocatedPageSizeInKB + loadStatsMap(); #elif LL_DARWIN // use host_statistics64 to get memory info @@ -822,11 +821,11 @@ void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_mem_kb) kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast(&vmstat), &count); if (result == KERN_SUCCESS) { - avail_mem_kb = U64Bytes((vmstat.free_count + vmstat.inactive_count) * page_size); + LLMemory::sAvailPhysicalMemInKB = U64Bytes((vmstat.free_count + vmstat.inactive_count) * page_size); } else { - avail_mem_kb = (U32Kilobytes)-1; + LLMemory::sAvailPhysicalMemInKB = (U32Kilobytes)-1; } #elif LL_LINUX @@ -880,12 +879,12 @@ void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_mem_kb) // (could also run 'free', but easier to read a file than run a program) LLSD statsMap(loadStatsMap()); - avail_mem_kb = (U32Kilobytes)statsMap["MemFree"].asInteger(); + LLMemory::sAvailPhysicalMemInKB = (U32Kilobytes)statsMap["MemFree"].asInteger(); #else //do not know how to collect available memory info for other systems. //leave it blank here for now. - avail_mem_kb = (U32Kilobytes)-1 ; + LLMemory::sAvailPhysicalMemInKB = (U32Kilobytes)-1 ; #endif } @@ -958,9 +957,13 @@ LLSD LLMemoryInfo::loadStatsMap() static constexpr DWORDLONG div = 1024; - stats.add("Percent Memory use", state.dwMemoryLoad/div); + stats.add("Percent Memory use", state.dwMemoryLoad); stats.add("Total Physical KB", state.ullTotalPhys/div); stats.add("Avail Physical KB", state.ullAvailPhys/div); + + // Despite the confusing naming "PageFile" , these values + // actually represent the committed memory limit for + // the system or the current process, whichever is smaller. stats.add("Total page KB", state.ullTotalPageFile/div); stats.add("Avail page KB", state.ullAvailPageFile/div); @@ -968,6 +971,9 @@ LLSD LLMemoryInfo::loadStatsMap() stats.add("Total Virtual MB", state.ullTotalVirtual/mb_div); // ~134 million MB stats.add("Avail Virtual MB", state.ullAvailVirtual/mb_div); + LLMemory::sAvailPhysicalMemInKB = U32Kilobytes::convert(U64Bytes(state.ullAvailPhys)); + LLMemory::sAvailCommitMemInMB = U32Megabytes::convert(U64Bytes(state.ullAvailPageFile)); + // SL-12122 - Call to GetPerformanceInfo() was removed here. Took // on order of 10 ms, causing unacceptable frame time spike every // second, and results were never used. If this is needed in the @@ -982,18 +988,38 @@ LLSD LLMemoryInfo::loadStatsMap() // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the // pointer. - GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmem, sizeof(pmem)); - - stats.add("Page Fault Count", pmem.PageFaultCount); - stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div); - stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div); - stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div); - stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div); - stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div); - stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div); - stats.add("PagefileUsage KB", pmem.PagefileUsage/div); - stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div); - stats.add("PrivateUsage KB", pmem.PrivateUsage/div); + if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmem, sizeof(pmem))) + { + LLMemory::sAllocatedMemInKB = U32Kilobytes::convert(U64Bytes(pmem.WorkingSetSize)); + LLMemory::sAllocatedPageSizeInKB = U32Kilobytes::convert(U64Bytes(pmem.PagefileUsage)); + + stats.add("Page Fault Count", pmem.PageFaultCount); + stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize / div); + stats.add("WorkingSetSize KB", pmem.WorkingSetSize / div); + stats.add("QuotaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage / div); + stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage / div); + stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage / div); + stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage / div); + stats.add("PagefileUsage KB", pmem.PagefileUsage / div); + stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage / div); + stats.add("PrivateUsage KB", pmem.PrivateUsage / div); + } + else + { + LLMemory::sAllocatedMemInKB = U32Kilobytes(0); + LLMemory::sAllocatedPageSizeInKB = U32Kilobytes(0); + + stats.add("Page Fault Count", 0); + stats.add("PeakWorkingSetSize KB", 0); + stats.add("WorkingSetSize KB", 0); + stats.add("QuotaPeakPagedPoolUsage KB", 0); + stats.add("QuotaPagedPoolUsage KB", 0); + stats.add("QuotaPeakNonPagedPoolUsage KB", 0); + stats.add("QuotaNonPagedPoolUsage KB", 0); + stats.add("PagefileUsage KB", 0); + stats.add("PeakPagefileUsage KB", 0); + stats.add("PrivateUsage KB", 0); + } #elif LL_DARWIN diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 827b0dc048..709fb29a82 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -134,11 +134,12 @@ public: static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib. #endif - //get the available memory in KiloBytes. - static void getAvailableMemoryKB(U32Kilobytes& avail_mem_kb); + // Updates LLMemory's values (which ones is OS specific). + 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. 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 2e4e9e29d5..5c2594e85c 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -181,6 +181,12 @@ 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()))); + if (gAgent.getRegion()) { // region location, when we have it @@ -193,6 +199,7 @@ namespace } LLAppViewer* app = LLAppViewer::instance(); + if (!app->isSecondInstance() && !app->errorMarkerExists()) { // If marker doesn't exist, create a marker with 'other' or 'logout' code for next launch -- cgit v1.3 From 1c8144b1708420b30c54472a5139d451034e89de Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev <117672381+akleshchev@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:53:53 +0300 Subject: #4604 Restore system memory factor, but in LLMemory --- indra/llcommon/llmemory.cpp | 91 +++++++++++++++++++++++++++++++++++++- indra/llcommon/llmemory.h | 9 +++- indra/llcommon/llsys.cpp | 2 +- indra/llcommon/llsys.h | 8 ++-- indra/newview/llappviewerwin32.cpp | 15 ++++--- indra/newview/llviewerdisplay.cpp | 5 +++ indra/newview/llviewermessage.cpp | 8 ++++ indra/newview/llvocache.cpp | 8 ++++ 8 files changed, 133 insertions(+), 13 deletions(-) (limited to 'indra/llcommon/llsys.cpp') 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 sAllocatedMem("allocated_mem", "active memory in use by application"); static LLTrace::SampleStatHandle 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); -- cgit v1.3