Codes like POP-1c or B4b-2 name a task in a planning document, not anything in the code. A reader has no way to resolve them and they age into noise the moment the document is closed. Where a code was used as a reference, the sentence now names the function it meant.
112 lines
3.7 KiB
C#
112 lines
3.7 KiB
C#
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Plugin.SelfTest;
|
|
|
|
namespace HellionChat.SelfTests;
|
|
|
|
// Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO
|
|
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs),
|
|
// then writes a single perf-baseline.json into the plugin ConfigDirectory so
|
|
// the cycle-notes author can copy the baseline figures without a separate
|
|
// profiling harness. The step only records — it never fails on a threshold
|
|
// (the budgets are evaluated by a human against the JSON ("optional,
|
|
// manual"). It returns Waiting until the sample window fills, mirroring the
|
|
// per-frame poll idiom of ThemeSwitchSelfTestStep.
|
|
internal sealed class PerformanceBaselineStep : ISelfTestStep
|
|
{
|
|
// Steady-state window. 1000 frames ≈ 16s at 60fps, long enough to
|
|
// average out GC blips without making the manual step tedious.
|
|
private const int TargetFrames = 1000;
|
|
|
|
// Rough draw-call proxy: ImGui emits 6 indices per quad, so vertices/6 is an
|
|
// intentional under-count of draw work, not the exact quad count.
|
|
private const int VerticesPerQuadProxy = 6;
|
|
|
|
private readonly Plugin _plugin;
|
|
|
|
private int _frames;
|
|
private ulong _lastFrameCount;
|
|
private double _drawMsSum;
|
|
private double _drawMsMax;
|
|
private long _vertexSum;
|
|
private long _vertexMax;
|
|
private double _deltaMsSum;
|
|
private double _deltaMsMax;
|
|
private string? _logPath;
|
|
|
|
public PerformanceBaselineStep(Plugin plugin)
|
|
{
|
|
_plugin = plugin;
|
|
}
|
|
|
|
public string Name => "Hellion Chat - Performance baseline capture";
|
|
|
|
public SelfTestStepResult RunStep()
|
|
{
|
|
var io = ImGui.GetIO();
|
|
|
|
// Count each real frame once. Without the FrameCount gate a step that
|
|
// is polled more than once per frame would inflate the sample count.
|
|
var frameCount = Plugin.Interface.UiBuilder.FrameCount;
|
|
if (frameCount != _lastFrameCount)
|
|
{
|
|
_lastFrameCount = frameCount;
|
|
_frames++;
|
|
|
|
var drawMs = _plugin.LastDrawMs;
|
|
_drawMsSum += drawMs;
|
|
if (drawMs > _drawMsMax)
|
|
_drawMsMax = drawMs;
|
|
|
|
long vertices = io.MetricsRenderVertices;
|
|
_vertexSum += vertices;
|
|
if (vertices > _vertexMax)
|
|
_vertexMax = vertices;
|
|
|
|
var deltaMs = io.DeltaTime * 1000f;
|
|
_deltaMsSum += deltaMs;
|
|
if (deltaMs > _deltaMsMax)
|
|
_deltaMsMax = deltaMs;
|
|
}
|
|
|
|
if (_frames < TargetFrames)
|
|
{
|
|
ImGui.Text(
|
|
$"Sampling steady-state… {_frames}/{TargetFrames} frames. "
|
|
+ "Keep the chat window visible and idle."
|
|
);
|
|
return SelfTestStepResult.Waiting;
|
|
}
|
|
|
|
_logPath ??= WriteBaselineLog();
|
|
ImGui.Text($"Baseline captured ({TargetFrames} frames). Wrote: {_logPath}");
|
|
return SelfTestStepResult.Pass;
|
|
}
|
|
|
|
public void CleanUp()
|
|
{
|
|
_frames = 0;
|
|
_lastFrameCount = 0;
|
|
_drawMsSum = 0;
|
|
_drawMsMax = 0;
|
|
_vertexSum = 0;
|
|
_vertexMax = 0;
|
|
_deltaMsSum = 0;
|
|
_deltaMsMax = 0;
|
|
_logPath = null;
|
|
}
|
|
|
|
private string WriteBaselineLog()
|
|
{
|
|
// Disk write happens here, never in the per-frame hot path.
|
|
return PerformanceBaselineLog.Write(
|
|
avgDrawMs: _drawMsSum / TargetFrames,
|
|
maxDrawMs: _drawMsMax,
|
|
avgDrawCallsProxy: _vertexSum / (double)TargetFrames / VerticesPerQuadProxy,
|
|
maxDrawCallsProxy: _vertexMax / (double)VerticesPerQuadProxy,
|
|
avgDeltaMs: _deltaMsSum / TargetFrames,
|
|
maxDeltaMs: _deltaMsMax,
|
|
frames: TargetFrames
|
|
);
|
|
}
|
|
}
|