debug: add FPS counter (#2558)
Adds an FPS counter to detect when there's a UI slowdown. (btw, drive-by typo fix for a file) https://github.com/tldraw/tldraw/assets/469604/b83d4b10-35d9-4584-af46-c63b5cc107ac ### Change Type - [ ] `patch` — Bug fix - [x] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. - [ ] Unit Tests - [ ] End to end tests ### Release Notes - Adds FPS counter to debug panel. --------- Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
3e50d66f33
commit
014a95cf51
6 changed files with 86 additions and 2 deletions
|
@ -2,7 +2,7 @@ import classNames from 'classnames'
|
||||||
import { ForwardedRef, forwardRef, useEffect, useId, useLayoutEffect, useRef } from 'react'
|
import { ForwardedRef, forwardRef, useEffect, useId, useLayoutEffect, useRef } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Example } from '../examples'
|
import { Example } from '../examples'
|
||||||
import { useMergedRefs } from '../hooks/useMegedRefs'
|
import { useMergedRefs } from '../hooks/useMergedRefs'
|
||||||
import { StandaloneIcon } from './Icons'
|
import { StandaloneIcon } from './Icons'
|
||||||
import { Markdown } from './Markdown'
|
import { Markdown } from './Markdown'
|
||||||
|
|
||||||
|
|
|
@ -421,6 +421,7 @@ export const debugFlags: {
|
||||||
pointerCaptureTrackingObject: DebugFlag<Map<Element, number>>;
|
pointerCaptureTrackingObject: DebugFlag<Map<Element, number>>;
|
||||||
elementRemovalLogging: DebugFlag<boolean>;
|
elementRemovalLogging: DebugFlag<boolean>;
|
||||||
debugSvg: DebugFlag<boolean>;
|
debugSvg: DebugFlag<boolean>;
|
||||||
|
showFps: DebugFlag<boolean>;
|
||||||
throwToBlob: DebugFlag<boolean>;
|
throwToBlob: DebugFlag<boolean>;
|
||||||
logMessages: DebugFlag<any[]>;
|
logMessages: DebugFlag<any[]>;
|
||||||
resetConnectionEveryPing: DebugFlag<boolean>;
|
resetConnectionEveryPing: DebugFlag<boolean>;
|
||||||
|
|
|
@ -38,6 +38,9 @@ export const debugFlags = {
|
||||||
debugSvg: createDebugValue('debugSvg', {
|
debugSvg: createDebugValue('debugSvg', {
|
||||||
defaults: { all: false },
|
defaults: { all: false },
|
||||||
}),
|
}),
|
||||||
|
showFps: createDebugValue('showFps', {
|
||||||
|
defaults: { all: false },
|
||||||
|
}),
|
||||||
throwToBlob: createDebugValue('throwToBlob', {
|
throwToBlob: createDebugValue('throwToBlob', {
|
||||||
defaults: { all: false },
|
defaults: { all: false },
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -575,7 +575,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 1fr auto auto;
|
grid-template-columns: 1fr auto auto auto;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-left: var(--space-4);
|
padding-left: var(--space-4);
|
||||||
border-top: 1px solid var(--color-background);
|
border-top: 1px solid var(--color-background);
|
||||||
|
@ -589,6 +589,15 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tlui-debug-panel__fps {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tlui-debug-panel__fps__slow {
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-warn);
|
||||||
|
}
|
||||||
|
|
||||||
/* -------------------- Menu Zone ------------------- */
|
/* -------------------- Menu Zone ------------------- */
|
||||||
|
|
||||||
.tlui-menu-zone {
|
.tlui-menu-zone {
|
||||||
|
|
|
@ -47,9 +47,12 @@ export const DebugPanel = React.memo(function DebugPanel({
|
||||||
renderDebugMenuItems: (() => React.ReactNode) | null
|
renderDebugMenuItems: (() => React.ReactNode) | null
|
||||||
}) {
|
}) {
|
||||||
const msg = useTranslation()
|
const msg = useTranslation()
|
||||||
|
const showFps = useValue('show_fps', () => debugFlags.showFps.get(), [debugFlags])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tlui-debug-panel">
|
<div className="tlui-debug-panel">
|
||||||
<CurrentState />
|
<CurrentState />
|
||||||
|
{showFps && <FPS />}
|
||||||
<ShapeCount />
|
<ShapeCount />
|
||||||
<DropdownMenu.Root id="debug">
|
<DropdownMenu.Root id="debug">
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
|
@ -68,6 +71,73 @@ const CurrentState = track(function CurrentState() {
|
||||||
return <div className="tlui-debug-panel__current-state">{editor.getPath()}</div>
|
return <div className="tlui-debug-panel__current-state">{editor.getPath()}</div>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function FPS() {
|
||||||
|
const fpsRef = React.useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
const TICK_LENGTH = 250
|
||||||
|
let maxKnownFps = 0
|
||||||
|
let cancelled = false
|
||||||
|
|
||||||
|
let start = performance.now()
|
||||||
|
let currentTickLength = 0
|
||||||
|
let framesInCurrentTick = 0
|
||||||
|
let isSlow = false
|
||||||
|
|
||||||
|
// A "tick" is the amount of time between renders. Even though
|
||||||
|
// we'll loop on every frame, we will only paint when the time
|
||||||
|
// since the last paint is greater than the tick length.
|
||||||
|
|
||||||
|
// When we paint, we'll calculate the FPS based on the number
|
||||||
|
// of frames that we've seen since the last time we rendered,
|
||||||
|
// and the actual time since the last render.
|
||||||
|
function loop() {
|
||||||
|
if (cancelled) return
|
||||||
|
|
||||||
|
// Count the frame
|
||||||
|
framesInCurrentTick++
|
||||||
|
|
||||||
|
// Check if we should render
|
||||||
|
currentTickLength = performance.now() - start
|
||||||
|
|
||||||
|
if (currentTickLength > TICK_LENGTH) {
|
||||||
|
// Calculate the FPS and paint it
|
||||||
|
const fps = Math.round(
|
||||||
|
framesInCurrentTick * (TICK_LENGTH / currentTickLength) * (1000 / TICK_LENGTH)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (fps > maxKnownFps) {
|
||||||
|
maxKnownFps = fps
|
||||||
|
}
|
||||||
|
|
||||||
|
const slowFps = maxKnownFps * 0.75
|
||||||
|
if ((fps < slowFps && !isSlow) || (fps >= slowFps && isSlow)) {
|
||||||
|
isSlow = !isSlow
|
||||||
|
}
|
||||||
|
|
||||||
|
fpsRef.current!.innerHTML = `FPS ${fps.toString()}`
|
||||||
|
fpsRef.current!.className =
|
||||||
|
`tlui-debug-panel__fps` + (isSlow ? ` tlui-debug-panel__fps__slow` : ``)
|
||||||
|
|
||||||
|
// Reset the values
|
||||||
|
currentTickLength -= TICK_LENGTH
|
||||||
|
framesInCurrentTick = 0
|
||||||
|
start = performance.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
loop()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <div ref={fpsRef} />
|
||||||
|
}
|
||||||
|
|
||||||
const ShapeCount = function ShapeCount() {
|
const ShapeCount = function ShapeCount() {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
const count = useValue('rendering shapes count', () => editor.getRenderingShapes().length, [
|
const count = useValue('rendering shapes count', () => editor.getRenderingShapes().length, [
|
||||||
|
@ -249,6 +319,7 @@ const DebugMenuContent = track(function DebugMenuContent({
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
<DebugFlagToggle flag={debugFlags.debugSvg} />
|
<DebugFlagToggle flag={debugFlags.debugSvg} />
|
||||||
|
<DebugFlagToggle flag={debugFlags.showFps} />
|
||||||
<DebugFlagToggle flag={debugFlags.forceSrgb} />
|
<DebugFlagToggle flag={debugFlags.forceSrgb} />
|
||||||
<DebugFlagToggle flag={debugFlags.debugGeometry} />
|
<DebugFlagToggle flag={debugFlags.debugGeometry} />
|
||||||
<DebugFlagToggle flag={debugFlags.hideShapes} />
|
<DebugFlagToggle flag={debugFlags.hideShapes} />
|
||||||
|
|
Loading…
Reference in a new issue