rendering stats

This commit is contained in:
azykov@mail.ru
2026-06-21 19:10:18 +03:00
parent a60e01e4d1
commit 0b7cdd9f05
10 changed files with 96 additions and 28 deletions
@@ -1,5 +1,6 @@
import { observer } from "mobx-react-lite";
import { measuredRenderTime } from "../../utils/layerTimings";
export const DebugLabelsLayer = observer(function () {
export const DebugLabelsLayer = observer(measuredRenderTime("DebugLabels", function () {
return null; //TODO
});
}));
@@ -1,7 +1,7 @@
import { observer } from "mobx-react-lite";
import { ObjectGeometriesLayer } from "./ObjectGeometriesLayer";
import { Canvas } from "@react-three/fiber";
import { SceneEditorCameraLayer } from "./SceneEditorGameraLayer";
import { SceneEditorCameraLayer } from "./SceneEditor/SceneEditorGameraLayer";
import { LightsLayer } from "./LighsLayer";
import { StatsLayer } from './StatsLayer';
@@ -1,8 +1,9 @@
import { observer } from "mobx-react-lite";
import { measuredRenderTime } from "../../utils/layerTimings";
export const LightsLayer = observer(function () {
export const LightsLayer = observer(measuredRenderTime("Lights", function () {
return <>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} intensity={1} />
</>;
});
}));
@@ -4,9 +4,9 @@ import { ObjectGeometryList } from "../../rendering/ObjectGeometryList";
import { observer } from "mobx-react-lite";
import { state } from "../../state";
import { useLayerFrame } from "../../utils/hooks/useLayerFrame";
import { measuredRenderTime } from "../../utils/layerTimings";
export const ObjectGeometriesLayer = observer(function () {
export const ObjectGeometriesLayer = observer(measuredRenderTime("ObjectGeometries", function () {
const geometries = state.view.objectGeometries;
const threeScene = useThree(s => s.scene);
@@ -24,4 +24,4 @@ export const ObjectGeometriesLayer = observer(function () {
useLayerFrame("ObjectGeometries", () => list.current?.tick());
return null;
});
}));
@@ -1,12 +1,13 @@
import { OrbitControls } from "@react-three/drei";
import { state } from "../../state";
import { state } from "../../../state";
import { useRef } from "react";
import { useSyncCamera } from "../../utils/hooks/useSyncCamera";
import { useSyncCamera } from "../../../utils/hooks/useSyncCamera";
import { MOUSE } from "three";
import { observer } from "mobx-react-lite";
import type { V3 } from "../../types";
import type { V3 } from "../../../types";
import { measuredRenderTime } from "../../../utils/layerTimings";
export const SceneEditorCameraLayer = observer(function () {
export const SceneEditorCameraLayer = observer(measuredRenderTime("Camera", function () {
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
useSyncCamera(() => state.view.camera?.camera);
@@ -38,4 +39,4 @@ export const SceneEditorCameraLayer = observer(function () {
rotateSpeed={0.2}
enableDamping={false}
/>;
});
}));
+13 -5
View File
@@ -49,12 +49,20 @@ export const StatsLayer = function () {
];
for (const [name, entry] of Object.entries(layerTimings)) {
if (entry.frames > 0) {
const avg = (entry.acc / entry.frames).toFixed(3);
lines.push(`${name} ${avg} ms`);
const parts: string[] = [];
if (entry.frame.frames > 0) {
parts.push(`frame ${(entry.frame.acc / entry.frame.frames).toFixed(3)}`);
}
entry.acc = 0;
entry.frames = 0;
if (entry.render.frames > 0) {
parts.push(`render ${(entry.render.acc / entry.render.frames).toFixed(3)}`);
}
if (parts.length > 0) {
lines.push(`${name} ${parts.join(" ")} ms`);
}
entry.frame.acc = 0;
entry.frame.frames = 0;
entry.render.acc = 0;
entry.render.frames = 0;
}
if (divRef.current) {
+2 -4
View File
@@ -15,15 +15,13 @@ export type RenderInfo = {
export class RootState {
public readonly world = new WorldState();
public readonly worldEditor: WorldEditorState;
public readonly worldEditor = new WorldEditorState(this.world);
public game: GameState | undefined;
public renderInfo: RenderInfo | undefined;
public readonly menu = new MenuState();
public readonly view = new ViewState(this.world);
public readonly view = new ViewState(this.world, this.worldEditor);
constructor() {
this.worldEditor = new WorldEditorState(this.world);
makeAutoObservable(
this,
{
+20 -1
View File
@@ -3,13 +3,19 @@ import type { ObjectInstance, ObjectType } from "../types";
import { MobxReconcilerMap } from '../utils/reconciler';
import { ObjectGeometryViewItem, ObjectTypeGeometryViewItem, ControlledCameraViewItem, ViewItem } from './viewLayers';
import type { WorldState } from "./worldState";
import { ObjectSelectionViewItem } from "./viewLayers/objectSelection";
import type { WorldEditorState } from "./worldEditorState";
export class ViewState {
public objectTypeGeometries: MobxReconcilerMap<ObjectType, ObjectTypeGeometryViewItem>;
public objectGeometries: MobxReconcilerMap<ObjectInstance, ObjectGeometryViewItem>;
public camera?: ControlledCameraViewItem;
constructor(world: WorldState) {
private readonly editor: WorldEditorState;
constructor(world: WorldState, editor: WorldEditorState) {
this.editor = editor;
makeAutoObservable(
this,
);
@@ -38,11 +44,24 @@ export class ViewState {
this.objectGeometries.dispose();
}
public get selectedObject(): ObjectSelectionViewItem | undefined {
const sel = this.editor.selection;
if (sel?.type !== 'object')
return;
const geom = this.objectGeometries.get(sel.id);
if (!geom)
return;
return new ObjectSelectionViewItem(geom, sel.editMode);
}
public get items(): ViewItem[] {
return [
...this.objectTypeGeometries,
...this.objectGeometries,
...[this.camera ? [this.camera] : []],
...[this.selectedObject ? [this.selectedObject] : []],
];
}
}
+2 -4
View File
@@ -1,5 +1,5 @@
import { useFrame, type RootState } from "@react-three/fiber";
import { layerTimings } from "../layerTimings";
import { recordLayerFrameTime } from "../layerTimings";
export function useLayerFrame(
name: string,
@@ -9,8 +9,6 @@ export function useLayerFrame(
useFrame((state, delta) => {
const start = performance.now();
callback(state, delta);
const entry = layerTimings[name] ??= { acc: 0, frames: 0 };
entry.acc += performance.now() - start;
entry.frames++;
recordLayerFrameTime(name, performance.now() - start);
}, priority);
}
+43 -1
View File
@@ -1 +1,43 @@
export const layerTimings: Record<string, { acc: number; frames: number }> = {};
import type { ReactNode } from "react";
export type LayerTiming = {
frame: { acc: number; frames: number };
render: { acc: number; frames: number };
};
export const layerTimings: Record<string, LayerTiming> = {};
function getEntry(name: string): LayerTiming {
return layerTimings[name] ??= {
frame: { acc: 0, frames: 0 },
render: { acc: 0, frames: 0 },
};
}
export function recordLayerFrameTime(name: string, ms: number) {
const t = getEntry(name).frame;
t.acc += ms;
t.frames++;
}
export function startLayerRender(name: string): () => void {
const start = performance.now();
return () => {
const t = getEntry(name).render;
t.acc += performance.now() - start;
t.frames++;
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function measuredRenderTime<TArgs extends any[], TResult extends ReactNode>(
name: string,
callback: (...args: TArgs) => TResult
): ((...args: TArgs) => TResult) {
return (...args) => {
const endRender = startLayerRender(name);
const result = callback(...args);
endRender();
return result;
};
}