UIDrawable per-instance memory diet (always-embedded render_sprite, duplicate RenderTextures, string members) #338
Labels
No labels
Alpha Release Requirement
Bugfix
Demo Target
Documentation
Major Feature
Minor Feature
priority:tier1-active
priority:tier2-foundation
priority:tier3-future
priority:tier4-deferred
Refactoring & Cleanup
system:animation
system:documentation
system:grid
system:input
system:performance
system:procgen
system:python-binding
system:rendering
system:ui-hierarchy
Tiny Feature
workflow:blocked
workflow:needs-benchmark
workflow:needs-documentation
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
john/McRogueFace#338
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
From the 2026-07-02 memory-model review. Internal; no API change. Lower priority than the GridData work -- drawable counts are hundreds, not millions -- but the base-class fat is paid by every Frame/Caption/Sprite/Grid.
Findings
UIDrawable::render_sprite(sf::Sprite, ~272 bytes) is embedded unconditionally (src/UIDrawable.h:276) but only meaningful whenrender_texture(already aunique_ptr) exists. Move it into a small heap struct alongside the RenderTexture:unique_ptr<CacheData>{render_texture, render_sprite}.UIGridcarries twosf::Sprite+ twosf::RenderTexture(sprite,output,renderTexture,rotationTexture,src/UIGrid.h:75-78);rotationTextureis only needed when rotation is active -- lazy-allocate.std::strings per drawable (name,parent_scene) plus 4unique_ptrmouse-callback slots (src/UIDrawable.h:49-52) -- consider a single lazily-allocated callback block; most drawables have zero handlers.UICaptionembedssf::Text(~280 B),UIFrameembedssf::RectangleShape(~304 B) -- unavoidable while SFML types are the backing store; note only.Approach
Measure first:
sizeof()report per class in a unit test, then a scene with ~5k drawables before/after (RSS + construction time). Only take changes that don't complicate the render path.Related: #117 (entity pool -- allocation strategy), #145 (TexturePool -- GPU-side twin of this issue), #255.
Measured (2026-07-11) via
tests/regression/issue_338_drawable_footprint_test.py, 5000 instances, RSS delta / count (C++ object + Python wrapper + allocator overhead):Frame≈ 551 B/instanceCaption≈ 1135 B/instance (the embeddedsf::Text)At the realistic scale this issue itself names (hundreds of drawables, not millions), that's tens of KB total.
Done (commit
5b2a412):UIGrid::rotationTextureandUIGridView::rotationTextureare now lazily-allocatedunique_ptrs — only camera-rotated grids allocate the second RenderTexture. Rotation tests green through the lazy path.Deferred (this issue stays open for it):
render_sprite(sf::Sprite, ~272 B) into the RenderTexture cache struct. It's the only meaningful per-drawable win, but it touches every render path — which this issue explicitly says to avoid ("only take changes that don't complicate the render path") — for a payoff of ~tens of KB at real scale. Not worth the render-path risk right now; revisit if a use case pushes drawable counts into the many-thousands or a profiler flags it.name/parent_scenestrings and the 4 callback-slot → single-block consolidation: same cost/benefit calculus, deferred.Risk budget redirected to the GridData SoA work (#332), where the memory win is 12× and it unblocks numpy views.