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 { Link } from 'react-router-dom'
|
||||
import { Example } from '../examples'
|
||||
import { useMergedRefs } from '../hooks/useMegedRefs'
|
||||
import { useMergedRefs } from '../hooks/useMergedRefs'
|
||||
import { StandaloneIcon } from './Icons'
|
||||
import { Markdown } from './Markdown'
|
||||
|
||||
|
|
|
@ -421,6 +421,7 @@ export const debugFlags: {
|
|||
pointerCaptureTrackingObject: DebugFlag<Map<Element, number>>;
|
||||
elementRemovalLogging: DebugFlag<boolean>;
|
||||
debugSvg: DebugFlag<boolean>;
|
||||
showFps: DebugFlag<boolean>;
|
||||
throwToBlob: DebugFlag<boolean>;
|
||||
logMessages: DebugFlag<any[]>;
|
||||
resetConnectionEveryPing: DebugFlag<boolean>;
|
||||
|
|
|
@ -38,6 +38,9 @@ export const debugFlags = {
|
|||
debugSvg: createDebugValue('debugSvg', {
|
||||
defaults: { all: false },
|
||||
}),
|
||||
showFps: createDebugValue('showFps', {
|
||||
defaults: { all: false },
|
||||
}),
|
||||
throwToBlob: createDebugValue('throwToBlob', {
|
||||
defaults: { all: false },
|
||||
}),
|
||||
|
|
|
@ -575,7 +575,7 @@
|
|||
width: 100%;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
grid-template-columns: 1fr auto auto auto;
|
||||
justify-content: space-between;
|
||||
padding-left: var(--space-4);
|
||||
border-top: 1px solid var(--color-background);
|
||||
|
@ -589,6 +589,15 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tlui-debug-panel__fps {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.tlui-debug-panel__fps__slow {
|
||||
font-weight: bold;
|
||||
color: var(--color-warn);
|
||||
}
|
||||
|
||||
/* -------------------- Menu Zone ------------------- */
|
||||
|
||||
.tlui-menu-zone {
|
||||
|
|
|
@ -47,9 +47,12 @@ export const DebugPanel = React.memo(function DebugPanel({
|
|||
renderDebugMenuItems: (() => React.ReactNode) | null
|
||||
}) {
|
||||
const msg = useTranslation()
|
||||
const showFps = useValue('show_fps', () => debugFlags.showFps.get(), [debugFlags])
|
||||
|
||||
return (
|
||||
<div className="tlui-debug-panel">
|
||||
<CurrentState />
|
||||
{showFps && <FPS />}
|
||||
<ShapeCount />
|
||||
<DropdownMenu.Root id="debug">
|
||||
<DropdownMenu.Trigger>
|
||||
|
@ -68,6 +71,73 @@ const CurrentState = track(function CurrentState() {
|
|||
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 editor = useEditor()
|
||||
const count = useValue('rendering shapes count', () => editor.getRenderingShapes().length, [
|
||||
|
@ -249,6 +319,7 @@ const DebugMenuContent = track(function DebugMenuContent({
|
|||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Group>
|
||||
<DebugFlagToggle flag={debugFlags.debugSvg} />
|
||||
<DebugFlagToggle flag={debugFlags.showFps} />
|
||||
<DebugFlagToggle flag={debugFlags.forceSrgb} />
|
||||
<DebugFlagToggle flag={debugFlags.debugGeometry} />
|
||||
<DebugFlagToggle flag={debugFlags.hideShapes} />
|
||||
|
|
Loading…
Reference in a new issue