Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 21 additions & 20 deletions app/components-react/shared/PerformanceMetrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import styles from './PerformanceMetrics.m.less';
import { $t } from '../../services/i18n';
import { useRealmObject } from 'components-react/hooks/realm';
import { IPinnedStatistics } from 'services/customization';
import { getPerformanceMetricMetadata } from './performance-metrics';

type TPerformanceMetricsMode = 'full' | 'limited';
const METRICS = ['cpu', 'fps', 'droppedFrames', 'bandwidth'] as (keyof IPinnedStatistics)[];

function pinTooltip(mode: TPerformanceMetricsMode, stat: string) {
return mode === 'full' ? $t('Click to add %{stat} info to your footer', { stat }) : '';
Expand All @@ -29,7 +29,7 @@ export default memo(function PerformanceMetrics(props: {
mode: TPerformanceMetricsMode;
className?: string;
}) {
const { CustomizationService, PerformanceService } = Services;
const { CustomizationService, PerformanceService, StreamingService } = Services;

const pinnedStats = useRealmObject(CustomizationService.state.pinnedStatistics);

Expand All @@ -40,27 +40,29 @@ export default memo(function PerformanceMetrics(props: {
droppedFrames: PerformanceService.views.droppedFrames,
percentDropped: PerformanceService.views.percentDropped,
bandwidth: PerformanceService.views.bandwidth,
bandwidthByDisplay: PerformanceService.views.bandwidthByDisplay,
isDualOutputMode: StreamingService.views.isDualOutputMode,
}),
false,
);

const metadata = useMemo(
() => ({
cpu: { value: `${v.cpuPercent}%`, label: $t('CPU'), icon: 'icon-cpu' },
fps: { value: v.frameRate, label: 'FPS', icon: 'icon-fps' },
droppedFrames: {
value: `${v.droppedFrames} (${v.percentDropped}%)`,
label: $t('Dropped Frames'),
icon: 'icon-dropped-frames',
},
bandwidth: { value: v.bandwidth, label: 'kb/s', icon: 'icon-bitrate' },
}),
[v.bandwidth, v.cpuPercent, v.droppedFrames, v.frameRate, v.percentDropped],
() => getPerformanceMetricMetadata(v),
[
v.bandwidth,
v.bandwidthByDisplay.horizontal,
v.bandwidthByDisplay.vertical,
v.cpuPercent,
v.droppedFrames,
v.frameRate,
v.isDualOutputMode,
v.percentDropped,
],
);

const shownCells = useMemo(
() => METRICS.filter(val => props.mode === 'full' || pinnedStats[val]),
[props.mode, pinnedStats],
() => metadata.filter(metric => props.mode === 'full' || pinnedStats[metric.attribute]),
[metadata, props.mode, pinnedStats],
);

const updatePinnedStats = useCallback(
Expand All @@ -80,17 +82,16 @@ export default memo(function PerformanceMetrics(props: {
props.className,
)}
>
{shownCells.map((attribute: keyof IPinnedStatistics) => {
const data = metadata[attribute];
{shownCells.map(data => {
return (
<MetricItem
key={`metric-${attribute}`}
key={`metric-${data.key}`}
mode={props.mode}
attribute={attribute}
attribute={data.attribute}
value={data.value}
label={data.label}
icon={data.icon}
isPinned={pinnedStats[attribute]}
isPinned={pinnedStats[data.attribute]}
onToggle={updatePinnedStats}
/>
);
Expand Down
71 changes: 71 additions & 0 deletions app/components-react/shared/performance-metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { IPinnedStatistics } from 'services/customization';
import { $t } from 'services/i18n';

export interface IPerformanceMetricValues {
cpuPercent: string;
frameRate: string;
droppedFrames: number;
percentDropped: string;
bandwidth: string;
bandwidthByDisplay: {
horizontal: string;
vertical: string;
};
isDualOutputMode: boolean;
}

export interface IPerformanceMetricMetadata {
key: string;
attribute: keyof IPinnedStatistics;
value: string | number;
label: string;
icon: string;
}

export function getPerformanceMetricMetadata(
values: IPerformanceMetricValues,
): IPerformanceMetricMetadata[] {
const metrics: IPerformanceMetricMetadata[] = [
{
key: 'cpu',
attribute: 'cpu',
value: `${values.cpuPercent}%`,
label: $t('CPU'),
icon: 'icon-cpu',
},
{
key: 'fps',
attribute: 'fps',
value: values.frameRate,
label: 'FPS',
icon: 'icon-fps',
},
{
key: 'droppedFrames',
attribute: 'droppedFrames',
value: `${values.droppedFrames} (${values.percentDropped}%)`,
label: $t('Dropped Frames'),
icon: 'icon-dropped-frames',
},
];

if (values.isDualOutputMode) {
metrics.push({
key: 'bandwidth',
attribute: 'bandwidth',
value: `H: ${values.bandwidthByDisplay.horizontal} V: ${values.bandwidthByDisplay.vertical}`,
label: 'kb/s',
icon: 'icon-bitrate',
});
} else {
metrics.push({
key: 'bandwidth',
attribute: 'bandwidth',
value: values.bandwidth,
label: 'kb/s',
icon: 'icon-bitrate',
});
}

return metrics;
}
14 changes: 14 additions & 0 deletions app/services/performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { StreamingService, EStreamingState } from 'services/streaming';
import { VideoSettingsService } from 'services/settings-v2/video';
import { DualOutputService } from 'services/dual-output';
import { UsageStatisticsService } from './usage-statistics';
import {
createEmptyDisplayStats,
TStreamingPerformanceDisplayStatsByDisplay,
} from './streaming/streaming-statistics';

interface IPerformanceState {
CPU: number;
Expand All @@ -28,6 +32,7 @@ interface IPerformanceState {
numberEncodedFrames: number;
numberRenderedFrames: number;
streamingBandwidth: number;
streamingBandwidthByDisplay: TStreamingPerformanceDisplayStatsByDisplay;
frameRate: number;
}

Expand Down Expand Up @@ -87,6 +92,13 @@ class PerformanceServiceViews extends ViewHandler<IPerformanceState> {
return (this.state.streamingBandwidth ?? 0).toFixed(0);
}

get bandwidthByDisplay() {
return {
horizontal: (this.state.streamingBandwidthByDisplay?.horizontal.kbitsPerSec ?? 0).toFixed(0),
vertical: (this.state.streamingBandwidthByDisplay?.vertical.kbitsPerSec ?? 0).toFixed(0),
};
}

get streamQuality() {
if (
this.state.percentageDroppedFrames > 50 ||
Expand Down Expand Up @@ -127,6 +139,7 @@ export class PerformanceService extends StatefulService<IPerformanceState> {
numberEncodedFrames: 0,
numberRenderedFrames: 0,
streamingBandwidth: 0,
streamingBandwidthByDisplay: createEmptyDisplayStats(),
frameRate: 0,
};

Expand Down Expand Up @@ -203,6 +216,7 @@ export class PerformanceService extends StatefulService<IPerformanceState> {
numberDroppedFrames: streamingStats.droppedFrames,
percentageDroppedFrames,
streamingBandwidth: streamingStats.kbitsPerSec,
streamingBandwidthByDisplay: streamingStats.byDisplay ?? createEmptyDisplayStats(),
// averageTimeToRenderFrame: obs.Global.averageFrameRenderTime,
// diskSpaceAvailable: obs.Global.diskSpaceAvailable,
// memoryUsage: obs.Global.memoryUsage,
Expand Down
88 changes: 88 additions & 0 deletions app/services/streaming/streaming-statistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
export type TStreamingPerformanceStatsDisplay = 'horizontal' | 'vertical';

export interface IStreamingPerformanceDisplayStats {
kbitsPerSec: number;
dataOutput: number;
}

export type TStreamingPerformanceDisplayStatsByDisplay = Record<
TStreamingPerformanceStatsDisplay,
IStreamingPerformanceDisplayStats
>;

export interface IStreamingPerformanceStatsInstance {
display?: TStreamingPerformanceStatsDisplay;
displayStats?: TStreamingPerformanceDisplayStatsByDisplay;
droppedFrames: number;
totalFrames: number;
kbitsPerSec: number;
dataOutput: number;
}

export interface IStreamingPerformanceStats {
droppedFrames: number;
totalFrames: number;
kbitsPerSec: number;
dataOutput: number;
byDisplay?: TStreamingPerformanceDisplayStatsByDisplay;
}

export interface IStreamingPerformanceStatsOptions {
calculateByDisplay?: boolean;
}

export function createEmptyDisplayStats(): TStreamingPerformanceDisplayStatsByDisplay {
return {
horizontal: { kbitsPerSec: 0, dataOutput: 0 },
vertical: { kbitsPerSec: 0, dataOutput: 0 },
};
}

function addDisplayStats(
target: TStreamingPerformanceDisplayStatsByDisplay,
display: TStreamingPerformanceStatsDisplay,
stats: IStreamingPerformanceDisplayStats,
) {
target[display].kbitsPerSec += stats.kbitsPerSec;
target[display].dataOutput += stats.dataOutput;
}

export function calculateStreamingPerformanceStats(
instances: IStreamingPerformanceStatsInstance[],
options: IStreamingPerformanceStatsOptions = {},
): IStreamingPerformanceStats {
let droppedFrames = 0;
let totalFrames = 0;
let kbitsPerSec = 0;
let dataOutput = 0;
const byDisplay = options.calculateByDisplay ? createEmptyDisplayStats() : undefined;

instances.forEach(instance => {
droppedFrames += instance.droppedFrames;
totalFrames += instance.totalFrames;
kbitsPerSec += instance.kbitsPerSec;
dataOutput += instance.dataOutput;

if (byDisplay && instance.displayStats) {
addDisplayStats(byDisplay, 'horizontal', instance.displayStats.horizontal);
addDisplayStats(byDisplay, 'vertical', instance.displayStats.vertical);
} else if (byDisplay && instance.display) {
addDisplayStats(byDisplay, instance.display, {
kbitsPerSec: instance.kbitsPerSec,
dataOutput: instance.dataOutput,
});
}
});

if (instances.length > 1) {
kbitsPerSec = Math.round(kbitsPerSec / instances.length);
}

return {
droppedFrames,
totalFrames,
kbitsPerSec,
dataOutput,
...(byDisplay ? { byDisplay } : {}),
};
}
Loading
Loading