import React from 'react'; import { connect } from 'react-redux'; import { Controls as PlayerControls, connectPlayer } from 'Player'; import { AreaChart, Area, ComposedChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, ReferenceLine, CartesianGrid, Label, } from 'recharts'; import { Checkbox } from 'UI'; import { durationFromMsFormatted } from 'App/date'; import { formatBytes } from 'App/utils'; import stl from './performance.module.css'; import BottomBlock from '../BottomBlock'; import InfoLine from '../BottomBlock/InfoLine'; const CPU_VISUAL_OFFSET = 10; const FPS_COLOR = '#C5E5E7'; const FPS_STROKE_COLOR = '#92C7CA'; const FPS_LOW_COLOR = 'pink'; const FPS_VERY_LOW_COLOR = 'red'; const CPU_COLOR = '#A8D1DE'; const CPU_STROKE_COLOR = '#69A5B8'; const USED_HEAP_COLOR = '#A9ABDC'; const USED_HEAP_STROKE_COLOR = '#8588CF'; const TOTAL_HEAP_STROKE_COLOR = '#4A4EB7'; const NODES_COUNT_COLOR = '#C6A9DC'; const NODES_COUNT_STROKE_COLOR = '#7360AC'; const HIDDEN_SCREEN_COLOR = '#CCC'; const CURSOR_COLOR = '#394EFF'; const Gradient = ({ color, id }) => ( ); const TOTAL_HEAP = 'Allocated Heap'; const USED_HEAP = 'JS Heap'; const FPS = 'Framerate'; const CPU = 'CPU Load'; const NODES_COUNT = 'Nodes Сount'; const FPSTooltip = ({ active, payload }) => { if (!active || !payload || payload.length < 3) { return null; } if (payload[0].value === null) { return (
{'Page is not active. User switched the tab or hid the window.'}
); } let style; if (payload[1].value != null && payload[1].value > 0) { style = { color: FPS_LOW_COLOR }; } if (payload[2].value != null && payload[2].value > 0) { style = { color: FPS_VERY_LOW_COLOR }; } return (
{`${FPS}: `} {Math.trunc(payload[0].value)}
); }; const CPUTooltip = ({ active, payload }) => { if (!active || payload.length < 1 || payload[0].value === null) { return null; } return (
{`${CPU}: `} {payload[0].value - CPU_VISUAL_OFFSET} {'%'}
); }; const HeapTooltip = ({ active, payload }) => { if (!active || payload.length < 2) return null; return (

{`${TOTAL_HEAP}: `} {formatBytes(payload[0].value)}

{`${USED_HEAP}: `} {formatBytes(payload[1].value)}

); }; const NodesCountTooltip = ({ active, payload }) => { if (!active || !payload || payload.length === 0) return null; return (

{`${NODES_COUNT}: `} {payload[0].value}

); }; const TICKS_COUNT = 10; function generateTicks(data: Array): Array { if (data.length === 0) return []; const minTime = data[0].time; const maxTime = data[data.length - 1].time; const ticks = []; const tickGap = (maxTime - minTime) / (TICKS_COUNT + 1); for (let i = 0; i < TICKS_COUNT; i++) { const tick = tickGap * (i + 1) + minTime; ticks.push(tick); } return ticks; } const LOW_FPS = 30; const VERY_LOW_FPS = 20; const LOW_FPS_MARKER_VALUE = 5; const HIDDEN_SCREEN_MARKER_VALUE = 20; function addFpsMetadata(data) { return [...data].map((point, i) => { let fpsVeryLowMarker = null; let fpsLowMarker = null; let hiddenScreenMarker = 0; if (point.fps != null) { fpsVeryLowMarker = 0; fpsLowMarker = 0; if (point.fps < VERY_LOW_FPS) { fpsVeryLowMarker = LOW_FPS_MARKER_VALUE; } else if (point.fps < LOW_FPS) { fpsLowMarker = LOW_FPS_MARKER_VALUE; } } if ( point.fps == null || (i > 0 && data[i - 1].fps == null) //|| //(i < data.length-1 && data[i + 1].fps == null) ) { hiddenScreenMarker = HIDDEN_SCREEN_MARKER_VALUE; } if (point.cpu != null) { point.cpu += CPU_VISUAL_OFFSET; } return { ...point, fpsLowMarker, fpsVeryLowMarker, hiddenScreenMarker, }; }); } @connect((state) => ({ userDeviceHeapSize: state.getIn(['sessions', 'current', 'userDeviceHeapSize']), userDeviceMemorySize: state.getIn(['sessions', 'current', 'userDeviceMemorySize']), })) export default class Performance extends React.PureComponent { _timeTicks = generateTicks(this.props.performanceChartData); _data = addFpsMetadata(this.props.performanceChartData); // state = { // totalHeap: false, // usedHeap: true, // fps: true, // } // onCheckboxClick = (e, { name, checked }) => this.setState({ [ name ]: checked }) onDotClick = ({ index }) => { const point = this._data[index]; if (!!point) { PlayerControls.jump(point.time); } }; onChartClick = (e) => { if (e === null) return; const { activeTooltipIndex } = e; const point = this._data[activeTooltipIndex]; if (!!point) { PlayerControls.jump(point.time); } }; render() { const { userDeviceHeapSize, userDeviceMemorySize, connType, connBandwidth, performanceChartTime, avaliability = {}, } = this.props; const { fps, cpu, heap, nodes } = avaliability; const avaliableCount = [fps, cpu, heap, nodes].reduce((c, av) => (av ? c + 1 : c), 0); const height = avaliableCount === 0 ? '0' : `${100 / avaliableCount}%`; return (
Performance
{/* */} = 1000 ? `${connBandwidth / 1000} Mbps` : `${connBandwidth} Kbps` } display={connBandwidth != null} />
{fps && ( {/* */} {/* */} {/* */} {/* */} )} {cpu && ( {/* */} ''} domain={[0, 'dataMax']} ticks={this._timeTicks} > )} {heap && ( {/* */} ''} // tick={false} + this._timeTicks to cartesian array domain={[0, 'dataMax']} ticks={this._timeTicks} > max * 1.2]} /> )} {nodes && ( {/* */} ''} domain={[0, 'dataMax']} ticks={this._timeTicks} > max * 1.2]} /> )}
); } } export const ConnectedPerformance = connectPlayer((state) => ({ performanceChartTime: state.performanceChartTime, performanceChartData: state.performanceChartData, connType: state.connType, connBandwidth: state.connBandwidth, avaliability: state.performanceAvaliability, }))(Performance);