Repository navigation
Conversation
Tessellated paths are kept least recently drawn last, up to 2048 shapes and 16 MiB of weight (the memory their keys and triangles own) per thread. A shape heavier than 16 MiB is kept in a slot of its own beside the rest until the next such shape takes it: one drawn every frame lets go of none of the rest, but two drawn in turn are each tessellated every time. Tessellation is untouched and drawn output identical. This replaces two generations of 1024 shapes bounded by count alone, which kept a window-sized path in a new shape every frame, never drawn again, in all 2048 entries and held hundreds of megabytes. Generations capped by bytes were tried and left: drawing a reused set again moved its shapes from the older generation into the newer, which could turn over and drop the rest of the set, and a shape over the cap did the same. The list keeps the 2048 most recent distinct shapes, so every shape the generations kept stays kept except as the weight cap forces and in the slot case above. Also counts hits, misses, evictions and the weight tessellated and kept per thread, behind test-support as PathCacheStats, so tests and gpui_perf can observe what the cache does.
Adds paths-moving-holes (a window-sized fill with 200 holes in a new shape every frame) and paths-still-holes (the same fill and 400 sparklines, drawn unchanged every frame), and reports per scenario the live heap growth and the kept path shapes' hits, misses, evictions, weight per tessellated path and weight held at the end. The heap row counts bytes live at the scenario's end less those live before it was built, so earlier scenarios in a run do not show in it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
fast/path_cache.rskeeps tessellated paths in two generations bounded only by count (2048 shapes). A window-sized path whose shape changes every frame never hits, but each build is still kept until all 2048 entries are full. In the newpaths-moving-holesscenario (a 1440×900 fill with 200 moving holes), each path weighs about 140 KiB, and the process holds 289.8 MiB more after the scenario than before it, nearly all of it kept shapes.Change
Why a list, not generations capped by bytes
Adding a byte cap to the two generations breaks reuse. A hit in the older generation moves the shape into the newer one, which can fill it and drop the rest of the older one. A set of shapes redrawn every frame that weighs between one and two generations would then miss every frame, where main hits. The list keeps the 2048 most recently drawn distinct shapes. That includes every shape main's generations keep, so apart from the case below, only the weight cap lets a shape go earlier than main does.
every_shape_two_generations_keep_is_keptchecks this against a model of main's generations.Costs
paths-still-holes, 401 hits a frame), inside the run-to-run spread. A hit now relinks its shape.two_shapes_over_the_limit_drawn_in_turn_are_tessellated_every_timepins this.Measurement
gpui_perf --headless --retention off, release build, base (main's cache logic) and branch runs alternated:paths-moving-holes, 3000 framespaths-still-holes, 2000 frames, 4 runsAcross the 11
workspace-*scenarios, hits are identical in 10, and misses are never above main's.Tests
cargo test -p gpui --lib --features test-support path_cacheunique_shapes_never_weigh_more_than_the_limit;unique_shapes_never_outnumber_the_count;unique_shapes_over_the_limit_are_held_one_at_a_time;unique_window_sized_paths_are_kept_within_a_bounded_number_of_bytes.a_set_drawn_every_frame_between_half_the_limit_and_the_limit_is_kepta_set_drawn_every_frame_is_kept_among_unique_shapesa_shape_drawn_again_stays_kept_among_unique_large_onesa_set_of_window_sized_paths_between_half_and_all_the_bytes_is_drawn_from_kept_onesevery_shape_two_generations_keep_is_kepta_shape_over_the_limit_is_kept_alone_while_it_is_drawn_again;a_frame_with_a_shape_over_the_limit_keeps_the_set_drawn_every_frame;two_shapes_over_the_limit_drawn_in_turn_are_tessellated_every_time.shapes_built_again_after_being_let_go_are_tessellated_again_the_same; a random sequence that checks the links, index and weights stay consistent.Reproduce
The
path …rows and thelive heap growthrow report the cache.Scope
Outside
fast/, the PR touches four lines ingpui.rsthat re-exportPathCacheStatsandpath_cache_statsbehindtest-support, one paragraph indocs/architecture.md, and the newgpui_perfscenarios and rows. The path cache exists only in gpui-fast, so this is a memory-growth fix to a gpui-fast feature, not a GPUI upstream matter.