Keep the Sound tab smooth while it pans (#360)
game-maker / gate (push) Successful in 3m51s
game-maker / suite (push) Successful in 15m55s

Panning the Sound tab drew the whole wave anew on every move, one
filled rectangle per pixel column, and gave the ruler's labels new
texts whenever a tick left the area. At 2560 px across that took the
tab down to 16 fps.

- The wave is drawn half the area's width past either side of it. A
  pan within that moves the drawing. Zoom, height, size or a pan
  past the margin draw it anew.
- The bars of the wave share one fill instead of one fill each.
- A ruler label keeps the second it already shows. Before, a pan
  shifted every label by one and each had to be rendered anew.
- A clip's name is given its style only when the style changes.
- The rule for what a drawing covers is pure in `waveView.ts`
  (`shownSeconds`, `drawnSeconds`, `coversShown`).

Measured with a copy of the user's project, "Take 1" with 4 clips,
240 pan moves with the middle button, headless on stein2:

- 2560x1440: main thread 712 -> 373 ms/s, texts rendered 694 -> 65
- 1280x800: main thread 429 -> 225 ms/s

What is left in the profile is Pixi's render pass and the pointer
events, which hovering pays as well.

Tests:
- Unit wave-view: 12 green (1 new), counter-probe red
- e2e 34 (stein2, melektaus-atx was down): ALL PASSED, 92 checks
  (1 new). Counter-probe with the labels given out by position: red,
  7 labels set anew instead of 1.
- Gate (stein2): 272 files, 4210 tests green

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-09 06:19:05 +02:00
1 parent 490fcba88f
commit cf96442835
5 files changed
+160 -15

No files matched your search

+29
View File
@@ -1980,6 +1980,35 @@ export async function sound(t) {
Math.abs(refitted.top - fullTop) < 0.5,
JSON.stringify({ levelScale, levelRect, fullTop, secondAt, zoomedOut, zoomedIn, refitted }),
);
// 34h-2b. A pan moves the wave drawn already and leaves each ruler label on its second (#360):
// drawing both anew on every move took the frames down.
const panFrom = await json('__gm.soundTab.at(0.375)');
await t.wheel(panFrom.x, panFrom.y, -924);
const panInfo = async () => ({
...(await json('__gm.soundTab.drawInfo()')),
at: (await json('__gm.soundTab.at(0.375)')).x,
});
const panBefore = await panInfo();
await t.mouse('mouseMoved', panFrom.x, panFrom.y);
await t.mouse('mousePressed', panFrom.x, panFrom.y, 'middle');
for (let i = 1; i <= 8; i++) {
await t.mouse('mouseMoved', panFrom.x - 30 * i, panFrom.y, 'middle', { buttons: 4 });
await sleep(20);
}
await t.mouse('mouseReleased', panFrom.x - 240, panFrom.y, 'middle');
await sleep(100);
const panAfter = await panInfo();
const panMoved = panAfter.at - panBefore.at;
await click(levelFit.x, levelFit.y);
await sleep(100);
check(
'a pan moves the wave drawn already, and the ruler labels keep their seconds',
Math.abs(panMoved + 240) < 1 &&
panAfter.waveDraws === panBefore.waveDraws &&
Math.abs(panAfter.shift - panBefore.shift - panMoved) < 0.5 &&
panAfter.rulerTexts - panBefore.rulerTexts <= 2,
JSON.stringify({ panBefore, panAfter, panMoved }),
);
const clipProps = async () => (await clips())[0]?.props;
let dragFrom = await json('__gm.soundTab.stripAt(0.1)');
let dragTo = await json('__gm.soundTab.stripAt(0.3)');