rendering stats
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
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
|
return null; //TODO
|
||||||
});
|
}));
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { ObjectGeometriesLayer } from "./ObjectGeometriesLayer";
|
import { ObjectGeometriesLayer } from "./ObjectGeometriesLayer";
|
||||||
import { Canvas } from "@react-three/fiber";
|
import { Canvas } from "@react-three/fiber";
|
||||||
import { SceneEditorCameraLayer } from "./SceneEditorGameraLayer";
|
import { SceneEditorCameraLayer } from "./SceneEditor/SceneEditorGameraLayer";
|
||||||
import { LightsLayer } from "./LighsLayer";
|
import { LightsLayer } from "./LighsLayer";
|
||||||
import { StatsLayer } from './StatsLayer';
|
import { StatsLayer } from './StatsLayer';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { measuredRenderTime } from "../../utils/layerTimings";
|
||||||
|
|
||||||
export const LightsLayer = observer(function () {
|
export const LightsLayer = observer(measuredRenderTime("Lights", function () {
|
||||||
return <>
|
return <>
|
||||||
<ambientLight intensity={0.5} />
|
<ambientLight intensity={0.5} />
|
||||||
<directionalLight position={[5, 5, 5]} intensity={1} />
|
<directionalLight position={[5, 5, 5]} intensity={1} />
|
||||||
</>;
|
</>;
|
||||||
});
|
}));
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import { ObjectGeometryList } from "../../rendering/ObjectGeometryList";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { state } from "../../state";
|
import { state } from "../../state";
|
||||||
import { useLayerFrame } from "../../utils/hooks/useLayerFrame";
|
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 geometries = state.view.objectGeometries;
|
||||||
|
|
||||||
const threeScene = useThree(s => s.scene);
|
const threeScene = useThree(s => s.scene);
|
||||||
@@ -24,4 +24,4 @@ export const ObjectGeometriesLayer = observer(function () {
|
|||||||
useLayerFrame("ObjectGeometries", () => list.current?.tick());
|
useLayerFrame("ObjectGeometries", () => list.current?.tick());
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
}));
|
||||||
|
|||||||
+6
-5
@@ -1,12 +1,13 @@
|
|||||||
import { OrbitControls } from "@react-three/drei";
|
import { OrbitControls } from "@react-three/drei";
|
||||||
import { state } from "../../state";
|
import { state } from "../../../state";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { useSyncCamera } from "../../utils/hooks/useSyncCamera";
|
import { useSyncCamera } from "../../../utils/hooks/useSyncCamera";
|
||||||
import { MOUSE } from "three";
|
import { MOUSE } from "three";
|
||||||
import { observer } from "mobx-react-lite";
|
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);
|
const controlsRef = useRef<React.ComponentRef<typeof OrbitControls>>(null);
|
||||||
|
|
||||||
useSyncCamera(() => state.view.camera?.camera);
|
useSyncCamera(() => state.view.camera?.camera);
|
||||||
@@ -38,4 +39,4 @@ export const SceneEditorCameraLayer = observer(function () {
|
|||||||
rotateSpeed={0.2}
|
rotateSpeed={0.2}
|
||||||
enableDamping={false}
|
enableDamping={false}
|
||||||
/>;
|
/>;
|
||||||
});
|
}));
|
||||||
@@ -49,12 +49,20 @@ export const StatsLayer = function () {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (const [name, entry] of Object.entries(layerTimings)) {
|
for (const [name, entry] of Object.entries(layerTimings)) {
|
||||||
if (entry.frames > 0) {
|
const parts: string[] = [];
|
||||||
const avg = (entry.acc / entry.frames).toFixed(3);
|
if (entry.frame.frames > 0) {
|
||||||
lines.push(`${name} ${avg} ms`);
|
parts.push(`frame ${(entry.frame.acc / entry.frame.frames).toFixed(3)}`);
|
||||||
}
|
}
|
||||||
entry.acc = 0;
|
if (entry.render.frames > 0) {
|
||||||
entry.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) {
|
if (divRef.current) {
|
||||||
|
|||||||
@@ -15,15 +15,13 @@ export type RenderInfo = {
|
|||||||
|
|
||||||
export class RootState {
|
export class RootState {
|
||||||
public readonly world = new WorldState();
|
public readonly world = new WorldState();
|
||||||
public readonly worldEditor: WorldEditorState;
|
public readonly worldEditor = new WorldEditorState(this.world);
|
||||||
public game: GameState | undefined;
|
public game: GameState | undefined;
|
||||||
public renderInfo: RenderInfo | undefined;
|
public renderInfo: RenderInfo | undefined;
|
||||||
public readonly menu = new MenuState();
|
public readonly menu = new MenuState();
|
||||||
public readonly view = new ViewState(this.world);
|
public readonly view = new ViewState(this.world, this.worldEditor);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.worldEditor = new WorldEditorState(this.world);
|
|
||||||
|
|
||||||
makeAutoObservable(
|
makeAutoObservable(
|
||||||
this,
|
this,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,13 +3,19 @@ import type { ObjectInstance, ObjectType } from "../types";
|
|||||||
import { MobxReconcilerMap } from '../utils/reconciler';
|
import { MobxReconcilerMap } from '../utils/reconciler';
|
||||||
import { ObjectGeometryViewItem, ObjectTypeGeometryViewItem, ControlledCameraViewItem, ViewItem } from './viewLayers';
|
import { ObjectGeometryViewItem, ObjectTypeGeometryViewItem, ControlledCameraViewItem, ViewItem } from './viewLayers';
|
||||||
import type { WorldState } from "./worldState";
|
import type { WorldState } from "./worldState";
|
||||||
|
import { ObjectSelectionViewItem } from "./viewLayers/objectSelection";
|
||||||
|
import type { WorldEditorState } from "./worldEditorState";
|
||||||
|
|
||||||
export class ViewState {
|
export class ViewState {
|
||||||
public objectTypeGeometries: MobxReconcilerMap<ObjectType, ObjectTypeGeometryViewItem>;
|
public objectTypeGeometries: MobxReconcilerMap<ObjectType, ObjectTypeGeometryViewItem>;
|
||||||
public objectGeometries: MobxReconcilerMap<ObjectInstance, ObjectGeometryViewItem>;
|
public objectGeometries: MobxReconcilerMap<ObjectInstance, ObjectGeometryViewItem>;
|
||||||
public camera?: ControlledCameraViewItem;
|
public camera?: ControlledCameraViewItem;
|
||||||
|
|
||||||
constructor(world: WorldState) {
|
private readonly editor: WorldEditorState;
|
||||||
|
|
||||||
|
constructor(world: WorldState, editor: WorldEditorState) {
|
||||||
|
this.editor = editor;
|
||||||
|
|
||||||
makeAutoObservable(
|
makeAutoObservable(
|
||||||
this,
|
this,
|
||||||
);
|
);
|
||||||
@@ -38,11 +44,24 @@ export class ViewState {
|
|||||||
this.objectGeometries.dispose();
|
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[] {
|
public get items(): ViewItem[] {
|
||||||
return [
|
return [
|
||||||
...this.objectTypeGeometries,
|
...this.objectTypeGeometries,
|
||||||
...this.objectGeometries,
|
...this.objectGeometries,
|
||||||
...[this.camera ? [this.camera] : []],
|
...[this.camera ? [this.camera] : []],
|
||||||
|
...[this.selectedObject ? [this.selectedObject] : []],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useFrame, type RootState } from "@react-three/fiber";
|
import { useFrame, type RootState } from "@react-three/fiber";
|
||||||
import { layerTimings } from "../layerTimings";
|
import { recordLayerFrameTime } from "../layerTimings";
|
||||||
|
|
||||||
export function useLayerFrame(
|
export function useLayerFrame(
|
||||||
name: string,
|
name: string,
|
||||||
@@ -9,8 +9,6 @@ export function useLayerFrame(
|
|||||||
useFrame((state, delta) => {
|
useFrame((state, delta) => {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
callback(state, delta);
|
callback(state, delta);
|
||||||
const entry = layerTimings[name] ??= { acc: 0, frames: 0 };
|
recordLayerFrameTime(name, performance.now() - start);
|
||||||
entry.acc += performance.now() - start;
|
|
||||||
entry.frames++;
|
|
||||||
}, priority);
|
}, priority);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user