mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
Offscreen or clipped working rings dropped their CSS animation through one shared IntersectionObserver; revealing a ring restarts it and the existing animationstart anchor keeps every ring on the shared phase. The spinner bench gains an always-on comparison variant and reports compositor frame counts.
56 lines
2.1 KiB
JavaScript
56 lines
2.1 KiB
JavaScript
import { writeFileSync } from 'node:fs'
|
|
|
|
export async function traceIterations(cdp, outputPath, durationMs = 2200) {
|
|
await cdp.send('Tracing.start', {
|
|
categories: 'devtools.timeline,toplevel,cc,disabled-by-default-cc.debug.scheduler',
|
|
transferMode: 'ReturnAsStream'
|
|
})
|
|
await new Promise((resolve) => setTimeout(resolve, durationMs))
|
|
const completion = new Promise((resolve) => cdp.once('Tracing.tracingComplete', resolve))
|
|
await cdp.send('Tracing.end')
|
|
const { stream } = await completion
|
|
let json = ''
|
|
try {
|
|
while (true) {
|
|
const part = await cdp.send('IO.read', { handle: stream })
|
|
json += part.data
|
|
if (part.eof) {
|
|
break
|
|
}
|
|
}
|
|
} finally {
|
|
await cdp.send('IO.close', { handle: stream })
|
|
}
|
|
writeFileSync(outputPath, json)
|
|
const events = JSON.parse(json).traceEvents
|
|
const compositorThreads = new Set(
|
|
events
|
|
.filter((event) => event.name === 'thread_name' && event.args?.name === 'Compositor')
|
|
.map((event) => `${event.pid}:${event.tid}`)
|
|
)
|
|
const onCompositor = events.filter((event) => compositorThreads.has(`${event.pid}:${event.tid}`))
|
|
const perSecond = (count) => Math.round((count * 1000) / durationMs)
|
|
return {
|
|
durationMs,
|
|
iterationEvents: events.filter(
|
|
(event) => event.name === 'EventDispatch' && event.args?.data?.type === 'animationiteration'
|
|
).length,
|
|
styleUpdates: events.filter((event) => event.name === 'UpdateLayoutTree').length,
|
|
// A spinner that wakes the compositor every refresh shows up as many no-damage frames.
|
|
compositorFramesPerSecond: perSecond(
|
|
onCompositor.filter((event) => event.name === 'Scheduler::BeginImplFrame').length
|
|
),
|
|
noDamageFramesPerSecond: perSecond(
|
|
onCompositor.filter((event) => event.name === 'EarlyOut_NoDamage').length
|
|
),
|
|
compositorThreadMsPerSecond:
|
|
Math.round(
|
|
(onCompositor
|
|
.filter((event) => event.name === 'ThreadControllerImpl::RunTask')
|
|
.reduce((total, event) => total + (event.tdur ?? event.dur ?? 0), 0) /
|
|
durationMs) *
|
|
10
|
|
) / 10
|
|
}
|
|
}
|