- Filter text allows restricting plots to ones matching the name
- Hierarchical view displays plots as a tree, separating levels based on
slash-separated names, i.e.: "GPU/Counters/Draws" and
"GPU/Counters/Primitives" will be leaf nodes under GPU > Counters
The manual rendering was a View method bound to View members. Move it
into a ManualWindow class that owns the active chunk, scroll-reset and
shown state and holds a reference to the shared manual data.
The manual data is invariant — derived only from the embedded manual — so
each View re-parsed it needlessly. It's now computed once and shared by all
Views and the LLM.
The intro and global-settings achievements recorded state even while
achievements were disabled, so the intro toast could be consumed before the
user ever enabled the feature — and then never appear when they later opted
in. The intro now fires at the enable points, so it shows the first time the
feature is enabled.
Achieve() now propagates a category unlock to the category's remaining
items, matching the constructor's load-time behavior. This lets an
achievement complete in the session where its category first unlocks.
The live per-frame scan visits only the set of locks with nonzero wait
counts, updated at each lock event: every wait-for edge originates at a
waiter, so uncontented locks cannot contribute anything. File loads replay
events without touching the set and are covered by the one-shot full-map
scan at end of load.
The scan consumes the final Waiting/SharedHolding flags of each lock
map: a thread blocked in a lock acquisition emits no Obtain and no other
event, so an unclosed wait at the last known state is a permanent wait
per the event contract. A failed try_lock never emits a wait, so it
cannot fabricate an edge. Locks recorded by clients with inverted
release ordering are skipped entirely, as their holder attribution is
ambiguous.
A thread that holds a shared lock and waits for exclusive cannot
progress while its own shared hold persists; this is reported as a
size-1 cycle.