Skip to content

Reduce LLFolderView per-item memory + fix three folderview defects#297

Merged
RyeMutt merged 3 commits into
developfrom
rye/folderview-memory
Jun 10, 2026
Merged

Reduce LLFolderView per-item memory + fix three folderview defects#297
RyeMutt merged 3 commits into
developfrom
rye/folderview-memory

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Jun 9, 2026

Copy link
Copy Markdown
Member

Reduces per-item memory in LLFolderView (cost paid per item, per open inventory window) and fixes several latent defects found along the way.

Memory reductions

View items:

  • Intern per-item-constant layout + colors into a shared LLFolderViewItemStyle const*, deduplicated across items (~100 B/item). Drop two dead fields (mLeftPad, mTextPadRight).
  • Lazily build the label/suffix LLFontVertexBuffers and release them when an item goes off-screen (setVisible override), so only on-screen items hold text geometry instead of every built item.

Inventory bridge:

  • Remove dead shadowed base LLInvFVBridge::mTimeSinceRequestStart (~16 B per bridge, per window); only LLFolderBridge's own copy is used.

Defects

  • draw(): the icon-overlay branch dereferenced the nullable mIcon for its height; fall back to the overlay's own height.
  • onIdleUpdateFavorites(): the "nothing changed" path left mFavoritesDirtyFlags set, leaking a per-idle callback that rescanned children every frame; clear it and deregister.
  • arrange(): drop two redundant sFonts map lookups (use getLabelFont()/sSuffixFont).

🤖 Generated with Claude Code

RyeMutt and others added 2 commits June 8, 2026 20:10
LLUICtrl never disconnected its control-variable signal connections on destruction, leaking a dead slot onto the session-lived LLControlVariable signal for every control bound via control_name/enabled/visibility that was later destroyed. Fold the five control-variable pointers and their connections into a lazily-allocated ControlVariables struct whose connections are scoped_connection, so destruction auto-disconnects; the struct is allocated only when a control actually binds a variable (~120 bytes saved per unbound control). Route llmenugl/llcombobox through getControlVariable()/getEnabledControlVariable() accessors.

Also make LLView::mToolTipMsg a lazily-allocated LLUIString*, null until a tooltip is set, so tooltip-less widgets no longer carry ~100 bytes of empty string storage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
View items (cost paid per item, per open inventory window):
- Intern per-item-constant layout + colors into a shared LLFolderViewItemStyle
  const*, deduplicated across items (~100 B/item); also drop two dead fields
  (mLeftPad, mTextPadRight).
- Lazily build the label/suffix LLFontVertexBuffers and release them when an item
  goes off-screen (setVisible override), so only on-screen items hold text
  geometry instead of every built item.

Defects:
- draw(): the icon-overlay branch dereferenced the nullable mIcon for its height;
  fall back to the overlay's own height.
- onIdleUpdateFavorites(): the 'nothing changed' path left mFavoritesDirtyFlags
  set, leaking a per-idle callback that rescanned children every frame; clear it
  and deregister.
- arrange(): drop two redundant sFonts map lookups (use getLabelFont()/sSuffixFont).

Inventory bridge:
- Remove dead shadowed base LLInvFVBridge::mTimeSinceRequestStart (~16 B per
  bridge, per window); only LLFolderBridge's own copy is used.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 039a9d74-4306-4958-8fdc-7d0a66e0d204

📥 Commits

Reviewing files that changed from the base of the PR and between de88742 and 61ccaf9.

📒 Files selected for processing (1)
  • indra/llui/llfolderviewitem.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • indra/llui/llfolderviewitem.cpp

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Prevented an idle callback from repeatedly rescanning folders.
    • Fixed combobox/menu check state and enabled-state synchronization so selections and toggles reflect control values reliably.
  • Performance & Optimization

    • Reduced memory and redraw overhead via shared, style-driven layout and lazy allocation of text/tooltip resources.
  • UI/Consistency

    • Unified styling and padding for folder, conversation, and menu items for more consistent layout and hit-testing.

Walkthrough

Refactors LLUI to consolidate control variables into a lazy container, lazily allocate LLView tooltips, intern shared LLFolderViewItemStyle instances, and update callers to use the new getters and style fields.

Changes

LLUI Infrastructure and Memory Optimization

Layer / File(s) Summary
LLUICtrl Control Variable Consolidation
indra/llui/lluictrl.h, indra/llui/lluictrl.cpp
Control variables and their signal connections are now stored in a lazily-allocated ControlVariables struct; accessors and setters updated to use the grouped storage.
LLView Lazy Tooltip Allocation
indra/llui/llview.h, indra/llui/llview.cpp
Tooltip storage converted from an inline LLUIString to a lazily-allocated LLUIString* with getOrCreateToolTipMsg() and updated setter/getter logic.
FolderViewItem Style Definition and Interning
indra/llui/llfolderviewitem.h, indra/llui/llfolderviewitem.cpp
Adds LLFolderViewItemStyle and internStyle() to deduplicate style instances; replaces per-instance geometry/color fields with a shared mStyle pointer.
FolderViewItem Layout, Drawing, and Buffer Management
indra/llui/llfolderviewitem.cpp
Layout, rendering, hit-testing, and font-buffer management refactored to use mStyle values; buffers lazily allocated and released on visibility changes; favorite/arrow/icon geometry updated.
Control Variable Getter Adoption
indra/llui/llcombobox.cpp, indra/llui/llmenugl.cpp
LLComboBox, LLMenuItemCallGL, and LLMenuItemCheckGL updated to use getControlVariable() / getEnabledControlVariable() instead of direct member access.
Style-Driven Padding and Layout Propagation
indra/llui/llfolderview.cpp, indra/newview/llconversationview.cpp
FolderView and ConversationView code updated to source arrow/icon/text padding and indentation from mStyle.
Unused Member Removal
indra/newview/llinventorybridge.h
Removes the unused mTimeSinceRequestStart timer member from LLInvFVBridge.

🎯 4 (Complex) | ⏱️ ~75 minutes


"🐰
I nibbled through the code tonight,
Shared styles snug and memory light,
Tooltips wait until they're asked,
Control vars grouped, no wasted task,
Hops of refactor — clean and bright!"

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is comprehensive and covers memory reductions and defects fixed, but the Related Issues section is incomplete with no issue link provided as required by the template. Add a valid GitHub issue link in the Related Issues section (e.g., 'closes #XXX') to satisfy the template requirement and provide necessary context for reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective: reducing per-item memory usage in LLFolderView and fixing related defects, which aligns with the comprehensive changeset across 11 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Infer (1.2.0)
indra/llui/llfolderviewitem.cpp

indra/llui/llfolderviewitem.cpp:26:10: fatal error: 'linden_common.h' file not found
26 | #include "linden_common.h"
| ^~~~~~~~~~~~~~~~~
1 error generated.
Error: the following clang command did not run successfully:
/opt/infer-linux-x86_64-v1.2.0/lib/infer/facebook-clang-plugins/clang/install/bin/clang-18
@/tmp/coderabbit-infer/61ccaf92ebf81ef0566156898171f03272a164db-a0ca06c6df31d8cd/tmp/clang_command_.tmp.9433aa.txt
++Contents of '/tmp/coderabbit-infer/61ccaf92ebf81ef0566156898171f03272a164db-a0ca06c6df31d8cd/tmp/clang_command_.tmp.9433aa.txt':
"-cc1" "-load"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../../facebook-clang-plugins/libtooling/build/FacebookClangPlugin.dylib"
"-add-plugin" "BiniouASTExporter" "-plugin-arg-BiniouASTExporter" "-"
"-plugin-arg-BiniouASTExporter" "PREPEND_CURRENT_DIR=1"
"-plugin-arg-BiniouASTExporter" "MAX_STRING_SIZE=65535" "-cc1" "-triple"
"x86_64-unknown-linux-gnu" "-emit-obj" "-mrelax-all" "-

... [truncated 1090 characters] ...

all/lib/clang/18/include"
"-internal-isystem" "/usr/local/include" "-internal-isystem"
"/usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include"
"-internal-externc-isystem" "/usr/include/x86_64-linux-gnu"
"-internal-externc-isystem" "/include" "-internal-externc-isystem"
"/usr/include" "-Wno-ignored-optimization-argument" "-Wno-everything"
"-fdeprecated-macro" "-ferror-limit" "19" "-fgnuc-version=4.2.1"
"-fskip-odr-check-in-gmf" "-fcxx-exceptions" "-fexceptions"
"-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o"
"/tmp/coderabbit-infer/a0ca06c6df31d8cd/file.o" "-x" "c++"
"indra/llui/llfolderviewitem.cpp" "-O0" "-fno-builtin" "-include"
"/opt/infer-linux-x86_64-v1.2.0/lib/infer/infer/bin/../lib/clang_wrappers/global_defines.h"
"-Wno-everything"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
indra/llui/llfolderviewitem.cpp (1)

397-420: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Mirror the new buffer invalidation in refreshSuffix().

refresh() now clears the lazy font buffers, but arrange() can refresh visible rows through refreshSuffix() instead. If a row loses its suffix on that path, draw() stops entering the suffix branch and the old mSuffixFontBuffer stays allocated until the row is hidden. The label buffer should be invalidated there too because mLabelStyle is updated in the same block.

Suggested fix
 void LLFolderViewItem::refreshSuffix()
 {
     LLFolderViewModelItem const* vmi = getViewModelItem();
@@
     if (mRoot->useLabelSuffix())
     {
         // Very Expensive!
         // Can do a number of expensive checks, like checking active motions, wearables or friend list
         mLabelStyle = vmi->getLabelStyle();
         pLabelFont = nullptr;
         mLabelSuffix = utf8str_to_wstring(vmi->getLabelSuffix());
+        if (mLabelFontBuffer)
+        {
+            mLabelFontBuffer->reset();
+        }
+        if (mSuffixFontBuffer)
+        {
+            mSuffixFontBuffer->reset();
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@indra/llui/llfolderviewitem.cpp` around lines 397 - 420, The issue:
refreshSuffix() updates mLabelStyle/mLabelSuffix like refresh() but doesn't
invalidate the lazy font buffers, leaving mLabelFontBuffer and mSuffixFontBuffer
allocated when the suffix is removed; update refreshSuffix() to mirror refresh()
by resetting/invalidation of both mLabelFontBuffer and mSuffixFontBuffer when
the label suffix/state changes so draw() will recreate buffers lazily; locate
refreshSuffix(), and inside the branch that updates mLabelStyle/mLabelSuffix
ensure you call mLabelFontBuffer->reset() (if non-null) and
mSuffixFontBuffer->reset() (if non-null), and avoid allocating new buffers there
(keep lazy creation in draw()).
🧹 Nitpick comments (1)
indra/llui/llview.cpp (1)

227-237: ⚡ Quick win

Free tooltip storage when tooltip is cleared.

Line 233 currently keeps mToolTipMsg allocated when msg is empty, which weakens the lazy-allocation memory savings for views that clear tooltips at runtime.

Proposed patch
 void LLView::setToolTip(const LLStringExplicit& msg)
 {
-    if (mToolTipMsg)
-    {
-        *mToolTipMsg = msg;
-    }
-    else if (!msg.empty())
-    {
-        mToolTipMsg = new LLUIString(msg);
-    }
+    if (msg.empty())
+    {
+        delete mToolTipMsg;
+        mToolTipMsg = nullptr;
+        return;
+    }
+
+    if (mToolTipMsg)
+    {
+        *mToolTipMsg = msg;
+    }
+    else
+    {
+        mToolTipMsg = new LLUIString(msg);
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@indra/llui/llview.cpp` around lines 227 - 237, LLView::setToolTip currently
leaves mToolTipMsg allocated when called with an empty msg; update the method so
that if msg.empty() it frees the existing tooltip storage (delete mToolTipMsg
and set mToolTipMsg = nullptr), otherwise preserve the existing behavior (assign
to *mToolTipMsg if present, or new LLUIString(msg) if null). Refer to
LLView::setToolTip and the mToolTipMsg/LLUIString symbols when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@indra/llui/llfolderviewitem.cpp`:
- Around line 1038-1050: The current setVisible() only releases mLabelFontBuffer
and mSuffixFontBuffer when the widget visibility bit flips, so items clipped by
the scroll viewport keep their font buffers; change the logic to evict buffers
whenever the item is not actually visible in the viewport (not just when
LLView::setVisible is called). Add a check (either in
LLFolderViewItem::setVisible or in an arrange/early-draw path such as
LLFolderViewFolder::arrange or LLFolderViewItem::drawLabel/draw) that tests
whether the item's rect intersects the folder/viewport visible rect (use the
view/parent clipping/visible-rect helper or compute intersection) and call
mLabelFontBuffer.reset() and mSuffixFontBuffer.reset() when the item is
clipped/out-of-viewport so buffers are released for off-screen rows and rebuilt
lazily in drawLabel()/draw().

---

Outside diff comments:
In `@indra/llui/llfolderviewitem.cpp`:
- Around line 397-420: The issue: refreshSuffix() updates
mLabelStyle/mLabelSuffix like refresh() but doesn't invalidate the lazy font
buffers, leaving mLabelFontBuffer and mSuffixFontBuffer allocated when the
suffix is removed; update refreshSuffix() to mirror refresh() by
resetting/invalidation of both mLabelFontBuffer and mSuffixFontBuffer when the
label suffix/state changes so draw() will recreate buffers lazily; locate
refreshSuffix(), and inside the branch that updates mLabelStyle/mLabelSuffix
ensure you call mLabelFontBuffer->reset() (if non-null) and
mSuffixFontBuffer->reset() (if non-null), and avoid allocating new buffers there
(keep lazy creation in draw()).

---

Nitpick comments:
In `@indra/llui/llview.cpp`:
- Around line 227-237: LLView::setToolTip currently leaves mToolTipMsg allocated
when called with an empty msg; update the method so that if msg.empty() it frees
the existing tooltip storage (delete mToolTipMsg and set mToolTipMsg = nullptr),
otherwise preserve the existing behavior (assign to *mToolTipMsg if present, or
new LLUIString(msg) if null). Refer to LLView::setToolTip and the
mToolTipMsg/LLUIString symbols when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 44ed3c4f-fa40-45a8-a5c0-6548ee4b1f25

📥 Commits

Reviewing files that changed from the base of the PR and between 7dc9954 and de88742.

📒 Files selected for processing (11)
  • indra/llui/llcombobox.cpp
  • indra/llui/llfolderview.cpp
  • indra/llui/llfolderviewitem.cpp
  • indra/llui/llfolderviewitem.h
  • indra/llui/llmenugl.cpp
  • indra/llui/lluictrl.cpp
  • indra/llui/lluictrl.h
  • indra/llui/llview.cpp
  • indra/llui/llview.h
  • indra/newview/llconversationview.cpp
  • indra/newview/llinventorybridge.h
💤 Files with no reviewable changes (1)
  • indra/newview/llinventorybridge.h

Comment thread indra/llui/llfolderviewitem.cpp
LLFontVertexBuffer::render does not compare the rendered string, so any
path that changes mLabelSuffix must reset the cached geometry or draw()
replays the old text. refresh() already does this; mirror it in
refreshSuffix(), which is reached via arrange() for items deferred from
postBuild(). Currently the buffer is always null on that path (items
arrange before first draw), so this is hardening against reordering or
future callers rather than a user-visible defect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@RyeMutt RyeMutt merged commit 88b6187 into develop Jun 10, 2026
17 checks passed
@RyeMutt RyeMutt deleted the rye/folderview-memory branch June 10, 2026 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant