add more stats to perf overlay

This commit is contained in:
scamiv
2025-11-26 14:46:05 +01:00
parent 37315e5fa5
commit 0da975f615
3 changed files with 131 additions and 12 deletions
+53 -12
View File
@@ -31,6 +31,7 @@ import {
drainTileUpdates,
SharedTileRingBuffers,
SharedTileRingViews,
TILE_RING_HEADER_OVERFLOW,
} from "../core/worker/SharedTileRing";
import { WorkerClient } from "../core/worker/WorkerClient";
import {
@@ -577,7 +578,8 @@ export class ClientGameRunner {
batch.length > 0 &&
lastTick !== undefined
) {
const combinedGu = this.mergeGameUpdates(batch);
const { gameUpdate: combinedGu, tileMetrics } =
this.mergeGameUpdates(batch);
if (combinedGu) {
this.gameView.update(combinedGu);
}
@@ -615,6 +617,10 @@ export class ClientGameRunner {
ticksPerRender,
workerTicksPerSecond,
renderTicksPerSecond,
tileMetrics.count,
tileMetrics.utilization,
tileMetrics.overflow,
tileMetrics.drainTime,
),
);
@@ -632,9 +638,15 @@ export class ClientGameRunner {
requestAnimationFrame(processFrame);
}
private mergeGameUpdates(
batch: GameUpdateViewData[],
): GameUpdateViewData | null {
private mergeGameUpdates(batch: GameUpdateViewData[]): {
gameUpdate: GameUpdateViewData | null;
tileMetrics: {
count: number;
utilization: number;
overflow: number;
drainTime: number;
};
} {
if (batch.length === 0) {
return null;
}
@@ -660,31 +672,60 @@ export class ClientGameRunner {
}
}
let tileMetrics = {
count: 0,
utilization: 0,
overflow: 0,
drainTime: 0,
};
if (this.tileRingViews) {
const MAX_TILE_UPDATES_PER_RENDER = 100000;
const tileRefs: TileRef[] = [];
const drainStart = performance.now();
drainTileUpdates(
this.tileRingViews,
MAX_TILE_UPDATES_PER_RENDER,
tileRefs,
);
const drainTime = performance.now() - drainStart;
// Calculate ring buffer utilization and overflow
const TILE_RING_CAPACITY = 262144;
const utilization = (tileRefs.length / TILE_RING_CAPACITY) * 100;
const overflow = Atomics.load(
this.tileRingViews.header,
TILE_RING_HEADER_OVERFLOW,
);
tileMetrics = {
count: tileRefs.length,
utilization,
overflow,
drainTime,
};
for (const ref of tileRefs) {
combinedPackedTileUpdates.push(BigInt(ref));
}
} else {
// Non-SAB mode: count tile updates from batch
let totalTileUpdates = 0;
for (const gu of batch) {
gu.packedTileUpdates.forEach((tu) => {
combinedPackedTileUpdates.push(tu);
});
totalTileUpdates += gu.packedTileUpdates.length;
}
tileMetrics.count = totalTileUpdates;
}
return {
tick: last.tick,
updates: combinedUpdates,
packedTileUpdates: new BigUint64Array(combinedPackedTileUpdates),
playerNameViewData: last.playerNameViewData,
tickExecutionDuration: last.tickExecutionDuration,
gameUpdate: {
tick: last.tick,
updates: combinedUpdates,
packedTileUpdates: new BigUint64Array(combinedPackedTileUpdates),
playerNameViewData: last.playerNameViewData,
tickExecutionDuration: last.tickExecutionDuration,
},
tileMetrics,
};
}
+5
View File
@@ -137,6 +137,11 @@ export class TickMetricsEvent implements GameEvent {
public readonly workerTicksPerSecond?: number,
// Approximate render tick() calls per second
public readonly renderTicksPerSecond?: number,
// Tile update metrics
public readonly tileUpdatesCount?: number,
public readonly ringBufferUtilization?: number,
public readonly ringBufferOverflows?: number,
public readonly ringDrainTime?: number,
) {}
}
@@ -319,6 +319,14 @@ export class PerformanceOverlay extends LitElement implements Layer {
this.layerStats.clear();
this.layerBreakdown = [];
// reset tile metrics
this.tileUpdatesPerRender = 0;
this.tileUpdatesPeak = 0;
this.ringBufferUtilization = 0;
this.ringBufferOverflows = 0;
this.ringDrainTime = 0;
this.totalTilesUpdated = 0;
this.requestUpdate();
};
@@ -437,6 +445,24 @@ export class PerformanceOverlay extends LitElement implements Layer {
@state()
private renderTicksPerSecond: number = 0;
@state()
private tileUpdatesPerRender: number = 0;
@state()
private tileUpdatesPeak: number = 0;
@state()
private ringBufferUtilization: number = 0;
@state()
private ringBufferOverflows: number = 0;
@state()
private ringDrainTime: number = 0;
@state()
private totalTilesUpdated: number = 0;
updateTickMetrics(
tickExecutionDuration?: number,
tickDelay?: number,
@@ -444,6 +470,10 @@ export class PerformanceOverlay extends LitElement implements Layer {
ticksPerRender?: number,
workerTicksPerSecond?: number,
renderTicksPerSecond?: number,
tileUpdatesCount?: number,
ringBufferUtilization?: number,
ringBufferOverflows?: number,
ringDrainTime?: number,
) {
if (!this.isVisible || !this.userSettings.performanceOverlay()) return;
@@ -497,6 +527,26 @@ export class PerformanceOverlay extends LitElement implements Layer {
this.renderTicksPerSecond = renderTicksPerSecond;
}
if (tileUpdatesCount !== undefined) {
this.tileUpdatesPerRender = tileUpdatesCount;
this.tileUpdatesPeak = Math.max(this.tileUpdatesPeak, tileUpdatesCount);
this.totalTilesUpdated += tileUpdatesCount;
}
if (ringBufferUtilization !== undefined) {
this.ringBufferUtilization =
Math.round(ringBufferUtilization * 100) / 100;
}
if (ringBufferOverflows !== undefined) {
// Accumulate overflows (overflows is a flag, so add 1 if set)
this.ringBufferOverflows += ringBufferOverflows;
}
if (ringDrainTime !== undefined) {
this.ringDrainTime = Math.round(ringDrainTime * 100) / 100;
}
this.requestUpdate();
}
@@ -527,6 +577,14 @@ export class PerformanceOverlay extends LitElement implements Layer {
executionSamples: [...this.tickExecutionTimes],
delaySamples: [...this.tickDelayTimes],
},
tiles: {
updatesPerRender: this.tileUpdatesPerRender,
peakUpdates: this.tileUpdatesPeak,
ringBufferUtilization: this.ringBufferUtilization,
ringBufferOverflows: this.ringBufferOverflows,
ringDrainTimeMs: this.ringDrainTime,
totalTilesUpdated: this.totalTilesUpdated,
},
layers: this.layerBreakdown.map((layer) => ({ ...layer })),
};
}
@@ -658,6 +716,21 @@ export class PerformanceOverlay extends LitElement implements Layer {
Backlog turns:
<span>${this.backlogTurns}</span>
</div>
<div class="performance-line">
Tile updates/render:
<span>${this.tileUpdatesPerRender}</span>
(peak: <span>${this.tileUpdatesPeak}</span>)
</div>
<div class="performance-line">
Ring buffer:
<span>${this.ringBufferUtilization}%</span>
(${this.totalTilesUpdated} total, ${this.ringBufferOverflows}
overflows)
</div>
<div class="performance-line">
Ring drain time:
<span>${this.ringDrainTime.toFixed(2)}ms</span>
</div>
${this.layerBreakdown.length
? html`<div class="layers-section">
<div class="performance-line">