Skip to content

Commit

Permalink
feat: Add Metrics Limits overlay
Browse files Browse the repository at this point in the history
  • Loading branch information
cyaiox committed Feb 6, 2024
1 parent 8c58bd1 commit efba29c
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 2 deletions.
133 changes: 133 additions & 0 deletions packages/@dcl/inspector/src/components/Renderer/Metrics/Metrics.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
.Metrics {
--metrics-bottom: 8px;
--metrics-left: 8px;
--metrics-button-height: 30px;
--metrics-button-width: 30px;

display: flex;
align-items: center;
position: absolute;
bottom: var(--metrics-bottom);
left: var(--metrics-left);
z-index: 1;
}

.Metrics .Buttons {
display: flex;
flex-direction: row;
gap: 8px;
}

.Metrics .Buttons .Button {
display: flex;
align-items: center;
justify-content: center;
height: var(--metrics-button-height);
width: var(--metrics-button-width);
padding: 5px;
border-radius: 4px;
background-color: transparent;
}

.Metrics .Buttons .Button.Active svg {
color: var(--base-06);
}

.Metrics .Buttons .Button svg {
color: var(--base-02);
}

.Metrics > div.LimitExceeded {
display: flex;
align-items: center;
text-transform: uppercase;
cursor: default;
}

.Metrics > .Overlay {
display: flex;
flex-direction: column;
width: 250px;
position: absolute;
overflow-y: auto;
left: 0;
bottom: calc(var(--metrics-bottom) + var(--metrics-button-height) + 8px);
background-color: var(--base-19);
padding: 13px 12px;
border-radius: 4px;
gap: 16px;
}

.Metrics > .Overlay h2.Header {
display: flex;
font-size: 14px;
font-weight: 500;
line-height: 17px;
color: var(--base-01);
margin-bottom: 0;
gap: 4px;
}

.Metrics > .Overlay .Item .Title,
.Metrics > .Overlay .Item .Description,
.Metrics > .Overlay .Item .Description .Key {
font-size: 11px;
font-weight: 500;
color: var(--base-01);
margin-bottom: 0;
}

.Metrics > .Overlay .secondary {
color: var(--base-09);
}

.Metrics > .Overlay .Items {
display: flex;
flex-direction: column;
gap: 8px;
}

.Metrics > .Overlay .Items .Item {
display: flex;
gap: 4px;
padding: 8px 0;
border-bottom: 1px solid var(--base-18);
}

.Metrics > .Overlay .Items .Item:last-of-type {
border-bottom: none;
}

.Metrics > .Overlay .Item .Title {
display: flex;
flex: 1;
align-items: center;
}

.Metrics > .Overlay .Item .Description {
display: flex;
flex: 1;
align-items: center;
gap: 4px;
line-height: 14px;
}

.Metrics > .Overlay .Item .Description .Key {
display: flex;
align-items: center;
justify-content: center;
padding: 0 4px;
height: 18px;
border-radius: 4px;
background-color: var(--base-13);
}

.Metrics .Buttons .Button.LimitExceeded svg,
.Metrics > .Overlay .Item .Description.LimitExceeded,
.Metrics > .Overlay .Item .Description.LimitExceeded .secondary {
color: var(--error-dark);
}

.Metrics .Buttons .Button.Active.LimitExceeded svg {
color: var(--error-main);
}
112 changes: 112 additions & 0 deletions packages/@dcl/inspector/src/components/Renderer/Metrics/Metrics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, { useCallback, useEffect, useMemo } from 'react'
import cx from 'classnames'
import { IoGridOutline as SquaresGridIcon, IoAlertCircleOutline as AlertIcon } from 'react-icons/io5'

import { withSdk, WithSdkProps } from '../../../hoc/withSdk'
import { useChange } from '../../../hooks/sdk/useChange'
import { useOutsideClick } from '../../../hooks/useOutsideClick'
import { Button } from '../../Button'
import { getSceneLimits } from './utils'
import type { Metrics } from './types'

import './Metrics.css'

const ICON_SIZE = 18

const Metrics = withSdk<WithSdkProps>(({ sdk }) => {
const [showMetrics, setShowMetrics] = React.useState(false)
const [metrics, setMetrics] = React.useState<Partial<Metrics>>({
triangles: 0,
entities: 0,
bodies: 0,
materials: 0,
textures: 0
})

const handleUpdateMetrics = useCallback(() => {
const ROOT_ENGINE = sdk.engine.RootEntity
const triangles = sdk.scene.meshes.reduce((acc, mesh) => acc + mesh.getTotalVertices(), 0)
const entities = (sdk.components.Nodes.getOrNull(ROOT_ENGINE)?.value ?? [ROOT_ENGINE]).length - 1
setMetrics({
triangles: triangles,
entities: entities,
bodies: sdk.scene.meshes.length,
materials: sdk.scene.materials.length,
textures: sdk.scene.textures.length
})
}, [sdk])

useEffect(handleUpdateMetrics, [handleUpdateMetrics])

useChange(handleUpdateMetrics, [handleUpdateMetrics])

const scene = useMemo(() => {
return sdk.components.Scene.get(sdk.engine.RootEntity)
}, [sdk])

const limits = useMemo<Metrics>(() => {
const parcels = scene.layout.base.x * scene.layout.base.y
return getSceneLimits(parcels)
}, [scene])

const limitsExceeded = useMemo<Record<string, boolean>>(() => {
return Object.fromEntries(
Object.entries(metrics)
.map(([key, value]) => [key, value > limits[key as keyof Metrics]])
.filter(([, value]) => value)
)
}, [metrics, limits])

const handleToggleMetricsOverlay = useCallback(
(e: React.MouseEvent<HTMLButtonElement> | MouseEvent) => {
e.preventDefault()
e.stopPropagation()
setShowMetrics((value) => !value)
},
[showMetrics, setShowMetrics]
)

const overlayRef = useOutsideClick(handleToggleMetricsOverlay)

return (
<div className="Metrics">
<div className="Buttons">
<Button
className={cx({ Active: showMetrics, LimitExceeded: Object.values(limitsExceeded).length > 0 })}
onClick={handleToggleMetricsOverlay}
>
<SquaresGridIcon size={ICON_SIZE} />
</Button>
</div>
{Object.values(limitsExceeded).length > 0 && (
<div className="LimitExceeded">
<AlertIcon />
Too many {Object.keys(limitsExceeded)[0].toUpperCase()}
</div>
)}
{showMetrics && (
<div ref={overlayRef} className="Overlay">
<h2 className="Header">
{scene.layout.base.x}x{scene.layout.base.y} LAND
<span className="secondary">
{scene.layout.base.x * 16}x{scene.layout.base.y * 16}m
</span>
</h2>
<div className="Items">
{Object.entries(metrics).map(([key, value]) => (
<div className="Item" key={key}>
<div className="Title">{key.toUpperCase()}</div>
<div className={cx('Description', { LimitExceeded: limitsExceeded[key] })}>
<span className="primary">{value}</span>/
<span className="secondary">{limits[key as keyof Metrics]}</span>
</div>
</div>
))}
</div>
</div>
)}
</div>
)
})

export default React.memo(Metrics)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Metrics from './Metrics'
export { Metrics }

Check warning on line 2 in packages/@dcl/inspector/src/components/Renderer/Metrics/index.ts

View check run for this annotation

Codecov / codecov/patch

packages/@dcl/inspector/src/components/Renderer/Metrics/index.ts#L2

Added line #L2 was not covered by tests
15 changes: 15 additions & 0 deletions packages/@dcl/inspector/src/components/Renderer/Metrics/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface Metrics {
triangles: number
entities: number
bodies: number
materials: number
textures: number
}

export enum Limits {
triangles = 10000,
entities = 200,
bodies = 300,
materials = 20,
textures = 10
}

Check warning on line 15 in packages/@dcl/inspector/src/components/Renderer/Metrics/types.ts

View check run for this annotation

Codecov / codecov/patch

packages/@dcl/inspector/src/components/Renderer/Metrics/types.ts#L2-L15

Added lines #L2 - L15 were not covered by tests
11 changes: 11 additions & 0 deletions packages/@dcl/inspector/src/components/Renderer/Metrics/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Limits, type Metrics } from './types'

export function getSceneLimits(parcels: number): Metrics {
return {
triangles: parcels * Limits.triangles,
entities: parcels * Limits.entities,
bodies: parcels * Limits.bodies,
materials: Math.floor(Math.log2(parcels + 1) * Limits.materials),
textures: Math.floor(Math.log2(parcels + 1) * Limits.textures)
}
}

Check warning on line 11 in packages/@dcl/inspector/src/components/Renderer/Metrics/utils.ts

View check run for this annotation

Codecov / codecov/patch

packages/@dcl/inspector/src/components/Renderer/Metrics/utils.ts#L2-L11

Added lines #L2 - L11 were not covered by tests
4 changes: 2 additions & 2 deletions packages/@dcl/inspector/src/components/Renderer/Renderer.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
position: relative;
}

.Renderer>div:first-child {
.Renderer > div:first-child {
display: flex;
}

.Renderer>canvas {
.Renderer > canvas {
width: 100%;
height: 100%;
touch-action: none;
Expand Down
2 changes: 2 additions & 0 deletions packages/@dcl/inspector/src/components/Renderer/Renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { analytics, Event } from '../../lib/logic/analytics'
import { Warnings } from '../Warnings'
import { CameraSpeed } from './CameraSpeed'
import { Shortcuts } from './Shortcuts'
import { Metrics } from './Metrics'

import './Renderer.css'

Expand Down Expand Up @@ -261,6 +262,7 @@ const Renderer: React.FC = () => {
{isLoading && <Loading />}
<Warnings />
<CameraSpeed />
<Metrics />
<Shortcuts canvas={canvasRef} onResetCamera={resetCamera} onZoomIn={zoomIn} onZoomOut={zoomOut} />
<canvas ref={canvasRef} id="canvas" touch-action="none" />
</div>
Expand Down

0 comments on commit efba29c

Please sign in to comment.