| status | open |
|---|---|
| summary | What the navbar/HydroHomie cook audit landed, why the containers/* freeze was reverted (it cost the navbar its whole installation for 2 operators), and parent_hierarchy's always-cook hover chain -- awaiting a hand test against a live control. |
| since | 2026-08-24 (branch cook-diet) |
| skill | fns-registry |
An audit of what the toolkit costs per frame while nobody is using it.
Numbers below are measured on FunctionStore_tools_2025_DEV, 4 pane bars,
60 fps (16.7 ms budget).
Cook counts, not cook times: OP.totalCooks snapshotted, then diffed over
a few hundred frames. cpuCookTime is a decaying rolling average and reads
high right after load, so it is useful for ranking and misleading as a total.
An operator "cooks every frame" when its delta matches the frame delta.
| Fix | Effect |
|---|---|
OpTemplates/kindergaertner_mymod observer walks immediate children, keyed by OP.id |
5.573 ms -> 0.009 ms per pass (623x); rename no longer fires a phantom create+delete |
FNS_Navbar/containers/* master templates frozen (allowCooking) |
was 28 ops -> 0 |
PathCellClickInject/press time-slicing gated by its panel execute, stopped 12 frames after release |
48 ops -> 0 across 4 bars |
HydroHomie: disabling the reminder now takes display down with it |
0.55 ms -> 0.014 ms |
Navbar total: 112 -> 64 always-cooking operators across four bars.
allowCookingcan only be disabled on COMPs. Setting it on a CHOP/DAT/TOP raises. This is why the press gate works (pressis a baseCOMP) and why HydroHomie's LFO could not be gated the same way.allowCookingis not inherited. Freezing a parent container buys nothing; the flag has to go on each operator that should stop.- Reach for
timeslicebeforeallowCooking. Time-slicing is usually the only thing in a widget cooking on its own, and it is a parameter, not a teardown.presswas gated by flippingallowCookingon the whole COMP, which destroyed and rebuilt all 14 of its operators on every press to stop two of them -- coarse, and a hitch you can feel. Measured with the COMP awake: 9 of 14 cooked every frame (the two Time-Sliced Speed CHOPs plus the seven they pull);timeslice = Falseon just those two took it to 1 cook in 505 frames withallowCookingleft ON. The integrator still works when it is switched back on --State_long_fractclimbs 0.05 -> 1.0 over 23 frames at 0.333s -- so the behaviour is unchanged. Verified across all 4 bars: 0 always-cooking operators of 140. - Never freeze a COMP that carries a registry host. A host whose COMP comes
up
allowCooking = Falsecannot compile its extension at load, so it never registers -- and everything downstream is silently absent. Freezing the threecontainers/*masters cost the navbar its entire installation: all three were missing from the registry and from all 7 bars, while theirRegstatusstill readRegistered: ...from the string saved in the tox. Once cooking is back on, the extension initializes, registers, and the next sync injects into every bar with no other change. (An already-compiled extension keeps working if you freeze it afterwards -- it is the load path that breaks, which is why this survives a session and only bites on the next open.)
It was measured against a parent_hierarchy that had not yet had its flag work
(cooktype = selective, timeslice off, chopexec5 inactive). With that
applied to the master, the freeze is no longer buying what the 28 in the table
says. Re-measured over 328 frames across all 416 operators under
containers/, with every container cooking:
2 always-cooking operators -- hijack_dragdrop/null1 and
parent_hierarchy/popMenu_bar/logic1. Both are CHOPs, so allowCooking cannot
be set on them anyway (see the invariant above); 2 ops is the floor.
Trading the navbar's entire installation for 2 operators is not a trade, so the
masters cook. PathCellClickInject/press stays frozen -- it is a baseCOMP, it
is gated by its own panel execute, and it is not in a host's parent chain.
60 of the remaining 64 ops are parent_hierarchy, 15 per bar: four
cooktype = always nulls, three Time-Sliced logic CHOPs, two maths, a feedback
CHOP, and four chopexecs holding the whole thing in permanent demand. The
inputs are event-driven (a Panel CHOP on ../panenav/path, plus hotkeys) -- so
the per-frame cooking comes from the flags, not from the work.
Measured on a live bar: setting the four nulls to cooktype = selective,
timeslice off on logic1/logic2/logic5, and chopexec5 inactive took the
widget from 15 always-cooking ops to 0 (2 cooks in 359 frames), with no
errors. Disabling chopexec5 is what silences feedback1 -- a Feedback CHOP
cooks every frame for as long as anything demands it, and that chopexec was the
only consumer. Its own comment reads "legacy in-bar placement only -- absent at
the mirror-scheme source", which is why it is a candidate at all.
Rolled out, that is 112 -> ~8 ops for the whole navbar.
Status: applied and awaiting a hand test. It is on the master
(FNS_Navbar/containers/parent_hierarchy) and on pane1's instance only;
pane2, pane5 and panebar_default were deliberately left on the original flags
so there is a live control to compare against. pane1 measured 0
always-cooking operators over 3189 frames, no errors.
The master carries it because _injectItem re-copies an instance whenever its
stored nbsrc does not match the source's OP.id -- and ids are reassigned on
reload, so a pane-only edit is silently wiped the next time the project opens.
Master + no RefreshWidget gives both: the A/B holds for now, and a reload
re-stamps every bar from the modified master.
In pane1 (a NETWORKEDITOR pane -- never test this on a Textport pane, they show no breadcrumb), against pane2 as the control:
- breadcrumb segments highlight on hover;
- clicking a segment navigates to that parent;
- the highlight clears when the mouse leaves -- this is the
chopexec5half; - any hotkey+hover combo still fires.
If only (3) fails, re-enable chopexec5 on the master and re-stamp; it costs 2
ops/bar back and keeps the rest. If (1) or (2) fails, the flags are
load-bearing -- revert the master to cooktype = always / timeslice on /
chopexec5 active, then RefreshWidget('ParentHierarchy'). If all pass,
RefreshWidget('ParentHierarchy') rolls it to every bar immediately.
- Non-editor panes carry a full navbar. A Textport pane was running 16 ops/frame for a breadcrumb it never displays. Gating the registry so it skips pane types with no breadcrumb saves the whole per-bar cost on each one.
panebar_defaultis TD's prototype bar and cooks like a live one (16 ops). Freezing it is easy, but a newly created pane copies it and would come up frozen -- so it needs the pane-creation path checked first.- HydroHomie's floor is
lfo1+timer1, about 0.014 ms. An LFO CHOP is unconditionally time-dependent (928/931 frames with play off and viewer off),allowCookingwill not take on a CHOP, and bypassing it starves the crossCHOP expressions into a TypeError. Not worth further surgery.
FNS_Navbar was dissolved. parent_hierarchy split into FNS_iopBrowser
(the OP/IOP browser) and FNS_ParentHierarchy (the breadcrumb, the hover chain
and the nugget); hijack_dragdrop and PathCellClickInject moved into
FNS_CustomParTools. Read every path above as historical.
The owner exercised the hover on 2026-09-09. It did nothing at all, silently. Bisecting the three changes against a live bar:
| change | verdict | why |
|---|---|---|
null1-null5 -> cooktype = selective |
REVERTED | the nulls cooked 22 times against panel2's 159, so the chopexecs watching them never saw a value change. This is what made the widget dead. |
timeslice off on logic1/logic2/logic5 |
kept | measured to make no difference to the signal |
chopexec5 inactive |
REVERTED | it is not a trigger, it is the RESET: onOnToOff puts celloverid back to -1 when the hover ends. Disabled, the hover latches and the guard if val == -1: return can never fire. |
Cost with the nulls back on automatic: 2 cooking ops per bar, 10 across five
bars -- against 65 for a full revert and 4 for the broken-but-quiet state. Most
of the saving survives.
The lesson worth keeping: cooktype = selective on a CHOP whose only consumer
is a CHOP Execute DAT silently disables the callback. The op stops cooking, the
DAT never sees a change, and nothing anywhere reports an error. That is why this
sat "applied and awaiting a hand test" for two weeks looking fine.
Do not measure this widget class at the master. panel2 and
hijack_dragdrop/panel1 read ../panenav, TouchDesigner's own pane-bar element,
which exists only in the pane-bar COPY. At the master they warn and carry 0
channels, which reads exactly like a dead feature and is not one.
The live control is gone. pane2, pane5 and panebar_default held instances on
the ORIGINAL flags; destroying the old master took them with it. Any future
comparison has to be staged deliberately.
hijack_dragdrop/null1 is still there, fed by panel1 on ../panenav/path,
and it carries 67 channels in a pane-bar copy. It remains one of the two ops
allowCooking cannot gate.