CNA-BUG-208: On Emscripten a diagnostics frame excludes the requestAnimationFrame wait, so FrameSample.framesPerSecond is a work rate there

CNA snapshot 009d40f5  ·  Known Issues › Current bugs  ·  source links pinned to 009d40f5

✓

Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.

The web loop opens the frame scope inside EmscriptenMainLoopCallback and waits for the next animation frame after it returns, so web frame durations and FPS exclude pacing, unlike the native Tick the documentation describes.

Identifier
CNA-BUG-208
Category
Bug
Subsystem
Diagnostics & Inspector
Status
Open
Verified against
CNA 009d40f5 (009d40f5dd085c4e674d3479675fac84b12b3e0a)
Severity
Low (a triage suggestion, not a project priority)
Evidence basis
Source-verified: read at TARGET, not executed
Tests touching this area
Yes: see Current tests
Affected contract
CNA::Diagnostics::FrameSample (durationNs, framesPerSecond) as published by Game's frame scope

Expected behaviour

docs/diagnostics.md: “Native Game::Tick and the Emscripten main-loop callback publish one frame ... The top-level tick measurement includes fixed-step pacing waits”. A frame should mean the same on both loops, or the difference should be documented.

Actual behaviour at TARGET

Native: CNA_DIAGNOSTICS_FRAME_SCOPE() opens at the top of Game::Tick, which contains the fixed-step Delay/yield loop, so a frame spans the pacing wait and framesPerSecond tracks the target rate. Emscripten: Game::RunLoop calls EmscriptenMainLoopCallback(), whose body holds the frame scope and the Game/Tick zone and paces with an accumulator that never waits, and then calls CNA_WaitForAnimationFrame() (an EM_ASYNC_JS await on requestAnimationFrame) outside it. A web frame's durationNs therefore covers event polling, updates and drawing only, and framesPerSecond (1e9 / durationNs) reports how fast that work ran, for example 500 for 2 ms of work at a 60 Hz display.

Source locations

Evidence

Checked by reading Game.cpp and Diagnostics.cpp at 009d40f5. Not built for Emscripten and not observed in a browser.

Focused reproduction

No focused reproduction is known. Nothing has been invented here; the evidence above is what exists.

Current tests

FrameHistoryIsBoundedOrderedAndReportsExactFps checks FPS against synthetic frame durations; no test covers the Emscripten loop's frame boundaries.

Regression test

An Emscripten run (or a fake loop test) asserting that a published frame spans the wait between callbacks, or a documentation note that web FPS measures work time.

Blast radius

Emscripten builds at STATS or FULL that read FrameSample in process (the Inspector is not available on the web). Native loops are unaffected.

Workaround

Derive the display rate from consecutive startTimestampNs values instead of framesPerSecond.

The same subject is explained at several altitudes. These are the neighbouring pages at each one.

Known issues
Bug index