feat(profiler): expose scene picture and flush timings

This commit is contained in:
Danila Poyarkov 2026-05-17 03:09:16 +03:00
parent 1326aac68d
commit faaa741911
6 changed files with 53 additions and 4 deletions

View file

@ -116,6 +116,14 @@ function canUseScenePicture(
)
}
const now = typeof performance !== 'undefined' ? () => performance.now() : () => 0
function measure<T>(fn: () => T): { value: T; duration: number } {
const start = now()
const value = fn()
return { value, duration: now() - start }
}
export function render(
r: SkiaRenderer,
graph: SceneGraph,
@ -126,6 +134,9 @@ export function render(
): void {
const p = r.profiler
p.beginFrame()
p.setScenePictureDrawTime(0)
p.setScenePictureRecordTime(0)
p.setFlushTime(0)
graph.clearAbsPosCache()
@ -167,7 +178,11 @@ export function render(
if (canUsePicture) {
p.setScenePictureMode('hit')
p.beginPhase('render:drawPicture')
if (r.scenePicture) canvas.drawPicture(r.scenePicture)
if (r.scenePicture) {
const picture = r.scenePicture
const { duration } = measure(() => canvas.drawPicture(picture))
p.setScenePictureDrawTime(duration)
}
p.endPhase('render:drawPicture')
} else if (hasVolatileOverlays) {
p.setScenePictureMode('volatile', cacheMissReason)
@ -181,7 +196,8 @@ export function render(
r._nodeCount = 0
r._culledCount = 0
p.beginPhase('render:recordPicture')
recordScenePicture(r, canvas, graph, sceneVersion)
const { duration } = measure(() => recordScenePicture(r, canvas, graph, sceneVersion))
p.setScenePictureRecordTime(duration)
p.endPhase('render:recordPicture')
}
p.endPhase('render:scene')
@ -231,7 +247,8 @@ export function render(
}
p.beginPhase('render:flush')
r.surface.flush()
const { duration: flushDuration } = measure(() => r.surface.flush())
p.setFlushTime(flushDuration)
p.endPhase('render:flush')
p.setNodeCounts(r._nodeCount, r._culledCount)

View file

@ -29,6 +29,11 @@ export function createFrameCapture(
culledNodes: stats.culledNodes,
drawCalls: stats.drawCalls,
scenePictureCacheHit: stats.scenePictureCacheHit,
scenePictureMode: stats.scenePictureMode,
scenePictureMissReason: stats.scenePictureMissReason,
scenePictureDrawTimeMs: stats.scenePictureDrawTime,
scenePictureRecordTimeMs: stats.scenePictureRecordTime,
flushTimeMs: stats.flushTime,
rootProfiles: session.stack.getRootProfiles()
}
}

View file

@ -22,6 +22,11 @@ export interface FrameCapture {
culledNodes: number
drawCalls: number
scenePictureCacheHit: boolean
scenePictureMode: 'hit' | 'record' | 'volatile' | 'none'
scenePictureMissReason: string
scenePictureDrawTimeMs: number
scenePictureRecordTimeMs: number
flushTimeMs: number
rootProfiles: NodeProfile[]
}

View file

@ -27,6 +27,9 @@ export class FrameStats {
scenePictureCacheHit = false
scenePictureMode: 'hit' | 'record' | 'volatile' | 'none' = 'none'
scenePictureMissReason = ''
scenePictureDrawTime = 0
scenePictureRecordTime = 0
flushTime = 0
private frameTimeBuffer = new Float64Array(BUFFER_SIZE)
private cpuTimeBuffer = new Float64Array(BUFFER_SIZE)

View file

@ -106,7 +106,7 @@ export class HudRenderer {
]
const visiblePhases = phaseNames.filter((n) => (phases.get(n) ?? 0) > 0.01)
const statsRows = 3
const statsRows = 4
const phaseRows = visiblePhases.length > 0 ? 1 + visiblePhases.length : 0
const statsHeight = (statsRows + phaseRows) * LINE_HEIGHT
const graphSection = hasGraph ? GRAPH_HEIGHT + PADDING + LINE_HEIGHT : 0
@ -151,6 +151,13 @@ export class HudRenderer {
this.hudFont
)
canvas.drawText(`Cache: ${cacheStatus}`, col2, y, this.textPaint, this.hudFont)
y += LINE_HEIGHT
const pictureLabel = stats.scenePictureMode === 'record' ? 'record' : 'picture'
const pictureTime =
stats.scenePictureMode === 'record' ? stats.scenePictureRecordTime : stats.scenePictureDrawTime
canvas.drawText(`${pictureLabel}: ${pictureTime.toFixed(1)}ms`, col1, y, this.textPaint, this.hudFont)
canvas.drawText(`flush: ${stats.flushTime.toFixed(1)}ms`, col2, y, this.textPaint, this.hudFont)
if (visiblePhases.length > 0) {
y += LINE_HEIGHT

View file

@ -89,6 +89,18 @@ export class RenderProfiler {
this.stats.scenePictureMissReason = reason
}
setScenePictureDrawTime(ms: number): void {
this.stats.scenePictureDrawTime = ms
}
setScenePictureRecordTime(ms: number): void {
this.stats.scenePictureRecordTime = ms
}
setFlushTime(ms: number): void {
this.stats.flushTime = ms
}
beginCapture(): void {
this.capturing = true
this.captureSession = createCaptureSession(now())