|
|
Donner SVG 0.8.0-pre
SVG editor and embeddable C++20 engine.
|
Per-frame attribution of ASYNCIFY suspend cost (the single-canvas presenter work). More...
#include <chrono>#include <cstdint>Classes | |
| struct | donner::FrameSuspendTotals |
| Suspend totals accumulated between BeginSuspendFrame and EndSuspendFrame on the calling thread. More... | |
| class | donner::ScopedSuspendPoint |
| Brackets one call that may suspend the wasm stack under ASYNCIFY. More... | |
Namespaces | |
| namespace | donner |
| Top-level Donner namespace, which is split into different sub-namespaces such as donner::svg and donner::css. | |
Enumerations | |
| enum class | donner::SuspendKind : std::uint8_t { donner::TileYield = 0 , donner::GpuReadback = 1 , donner::DeviceWait = 2 , donner::Startup = 3 } |
| Why a call site can suspend. Kept small and stable: it is published to the stats surface as an array index. More... | |
Functions | |
| void | donner::BeginSuspendFrame () |
| Start a new attribution window on the calling thread, discarding whatever was accumulated since the previous EndSuspendFrame. | |
| FrameSuspendTotals | donner::EndSuspendFrame () |
| Close the attribution window opened by BeginSuspendFrame and return what it accumulated. Safe to call without a matching begin; the totals are then everything since the thread's last reset. | |
| FrameSuspendTotals | donner::PeekSuspendFrame () |
| Read the current window's totals without closing it. | |
| FrameSuspendTotals | donner::LifetimeSuspendTotals () |
| Lifetime totals for the calling thread, never reset by frame boundaries. Used by the boot report, where there is no frame to attribute against. | |
Variables | |
| constexpr std::size_t | donner::kSuspendKindCount = 4 |
| Number of distinct SuspendKind values. | |
Per-frame attribution of ASYNCIFY suspend cost (the single-canvas presenter work).
The browser editor links with whole-module -sASYNCIFY. Any call that reaches a JS-side Asyncify.handleSleep / handleAsync unwinds the wasm stack, returns to the browser event loop, and rewinds when the awaited promise settles. With the whole application on one thread those suspend points sit directly under the UI frame, so their wall cost is UI frame cost and has to be attributed before a frame budget means anything.
There is no wasm-visible Asyncify counter to read, and the JS runtime's Asyncify object is a closure-renamed module-scope binding, so the probe instead brackets the call sites that can suspend. Bracketing is exact for wall time because Asyncify preserves the shadow stack across an unwind: entering a ScopedSuspendPoint runs its constructor once before the call, the unwind returns out of the enclosing function without running destructors, the rewind restores the stack pointer and locals, and the destructor finally runs once on the normal path out. The measured interval therefore spans the whole suspend, not just the pre-suspend CPU work.
Counters are thread-local: each thread attributes the suspends it performs, and the frame publisher reads only its own thread's totals.
| struct donner::FrameSuspendTotals |
Suspend totals accumulated between BeginSuspendFrame and EndSuspendFrame on the calling thread.
| Class Members | ||
|---|---|---|
| uint32_t | count = 0 | Suspend points entered during the frame. |
| uint32_t | countByKind[kSuspendKindCount] = {} | Per-kind entry counts, indexed by SuspendKind. |
| double | longestMs = 0.0 | Longest single suspend during the frame, in milliseconds. |
| double | msByKind[kSuspendKindCount] = {} | Per-kind wall time in milliseconds, indexed by SuspendKind. |
| double | totalMs = 0.0 | Summed wall time inside those suspend points, in milliseconds. |