Show the frame rate the measured times allow in the overlay (#275)
game-maker / gate (push) Successful in 2m35s
game-maker / suite (push) Successful in 11m46s

The fps line of the performance overlay stops at the display's rate,
60 on most screens. It does not tell how much room a game has left.
The new top line answers that from the times the overlay measures.

- possibleFps in src/editor/frameMeter.ts is 1000 ms over the sum of
  the five areas, rounded down. It has no number while nothing took
  any time, and the overlay shows a dash then.
- The editor's share is in the sum, because it costs time while
  playing in the editor. A released game can do a little better.
- It is an upper bound. The GPU's time is not measured and not in it.
- e2e 59e reads the lines one further down and checks the new line
  against the times it measured itself.

Tests:
- Unit frame-meter: 11 passed (+2 for possibleFps)
- Gate: 197 files, 2855 tests, CHECK EXIT 0 (2853 + 2)
- e2e 59: ALL PASSED, 19 checks (+1), on melektaus-atx
- Counter-check: possibleFps without the editor's share fails the
  unit test
- The e2e total of 1602 is counted (1601 + 1), not measured

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-09-30 11:18:39 +02:00
1 parent 77f4c2c1a3
commit f14c4ecb77
4 files changed
+43 -8

No files matched your search

+18 -1
View File
@@ -4,7 +4,7 @@
* each system it ticked – and only those.
*/
import { describe, expect, it } from 'vitest';
import { FrameMeter, performanceLines } from '../../src/editor/frameMeter';
import { FrameMeter, performanceLines, possibleFps } from '../../src/editor/frameMeter';
import type { System } from '../../src/runtime/systems/System';
import { World } from '../../src/runtime/World';
import { makeSpriteScene } from '../helpers';
@@ -69,6 +69,7 @@ describe('FrameMeter', () => {
describe('performanceLines', () => {
it('shows dashes until there is something measured', () => {
expect(performanceLines({ fps: null, entities: 1, times: null })).toEqual([
'Possible – fps',
'– fps · 1 entity',
'Behaviors – ms',
'Physics – ms',
@@ -81,6 +82,7 @@ describe('performanceLines', () => {
it('lines the numbers up in milliseconds with two decimals', () => {
const times = { behaviors: 1.234, physics: 12.5, animation: 0, drawing: 3, editor: 0.006 };
expect(performanceLines({ fps: 60, entities: 128, times })).toEqual([
'Possible 59 fps',
'60 fps · 128 entities',
'Behaviors 1.23 ms',
'Physics 12.50 ms',
@@ -91,6 +93,21 @@ describe('performanceLines', () => {
});
});
describe('possibleFps', () => {
it('is a second over what a frame takes in all areas, rounded down', () => {
expect(possibleFps({ behaviors: 0.5, physics: 0.25, animation: 0, drawing: 0.75, editor: 0.5 })).toBe(
500,
);
expect(possibleFps({ behaviors: 0.1, physics: 0.1, animation: 0.1, drawing: 0.1, editor: 0.3 })).toBe(
1428,
);
});
it('has no number while nothing took any time', () => {
expect(possibleFps({ behaviors: 0, physics: 0, animation: 0, drawing: 0, editor: 0 })).toBeNull();
});
});
describe('World.timeSystem', () => {
function counting(runsWhilePaused = false): System & { runs: number } {
return {