mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
perf: zone CPU profiler (PerfLog) + phase-restructured CPU spectrum render
Инструментация (ВРЕМЕННАЯ, не мержить в main): - PerfLog.pas: зонные таймеры, отчёт раз в 5с в stderr (core% = доля ядра), EWSDR_PERF=0 выкл, EWSDR_PERF_FILE=путь — дублировать в файл - Замеры // PERF: сеть (DDC IQ/wideband), Pluto (rx_conv/tx_resamp/tx_fill), DSP (push_iq/fexchange/slices/tx), аудио, UI (тики/draw/paint/gl_swap, разбивка draw_spectrum по фазам) - doc/PERF_PROFILING.md: карта зон, методика, выводы Оптимизация CPU-рендера спектра (по результатам замеров на эфире): диагноз — дорога не пиксельная работа, а чередование raw(ScanLine)/ canvas(QPainter) доступа к битмапу на Qt6 (~6 переходов за кадр, каждый синкает весь битмап; полоса фильтра в тысячи пикселей стоила 4x дороже полного memcpy кадра). DrawSpectrum перестроен в фазы calc->raw->canvas->raw (2 перехода): FillBandRaw вместо Canvas.FillRect, DrawSliceFilterMarkers разделён на raw-полосы + canvas-линии, композиты флагов и альфа-OR в финальной raw-фазе. Кадр 8.3мс -> 6.1мс; spec_bands 1.87мс -> 0.03мс. Альфа-OR проход на Qt6 подтверждён нужным (без него canvas-пиксели теряют альфу и пропадают при блите), сам цикл ~0.08мс — оставлен; тумблер EWSDR_ALPHA_FIX=0 только для экспериментов. Кисть перед AGC-подписями теперь ставится явно (была остаточной после перестройки фаз). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ interface
|
||||
uses
|
||||
Classes, SysUtils, Math, SyncObjs,
|
||||
BeaconDecoder,
|
||||
PerfLog, // ВРЕМЕННО (perf/cpu-profiling): зонные CPU-таймеры
|
||||
WDSP;
|
||||
|
||||
const
|
||||
@@ -1364,10 +1365,12 @@ procedure TWDSPEngine.PushIQItemToDSP(const Item: TIQQueueItem);
|
||||
var
|
||||
i, Pos: Integer;
|
||||
IR, QR: LongInt;
|
||||
T0: Int64; // PERF
|
||||
const
|
||||
SCALE = 1.0 / 8388608.0;
|
||||
begin
|
||||
if FTXActive and not FKeepRXDuringTX then Exit;
|
||||
T0 := PerfNow; // PERF
|
||||
// Post-TX hold: дропаем входящие RX-IQ пакеты пока активно окно мьюта.
|
||||
// Это закрывает ОБА канала сразу — и водопад/спектр (анализатор не
|
||||
// кормится), и аудио (RXA не запускается, FOnAudio не вызывается).
|
||||
@@ -1401,9 +1404,11 @@ begin
|
||||
if FRXAccPos >= FBufSize then
|
||||
ProcessRXBlock;
|
||||
end;
|
||||
PerfAdd(pzDspPushIQ, T0); // PERF: весь путь пакета через DSP-поток
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.FeedDisplaySample(const I, Q: Double);
|
||||
var T0: Int64; // PERF
|
||||
begin
|
||||
if not FAnalyzerOpen then Exit;
|
||||
|
||||
@@ -1413,7 +1418,9 @@ begin
|
||||
|
||||
if FDispPos >= DISPLAY_BLOCK_SIZE then
|
||||
begin
|
||||
T0 := PerfNow; // PERF
|
||||
Spectrum0(1, RX_DISP_ID, 0, 0, @FDispBuf[0]);
|
||||
PerfAdd(pzDspSpectrum0, T0); // PERF: скармливание блока анализатору
|
||||
FDispPos := 0;
|
||||
end;
|
||||
end;
|
||||
@@ -1437,12 +1444,14 @@ procedure TWDSPEngine.ProcessRXBlock;
|
||||
var
|
||||
i: Integer;
|
||||
Err: Integer;
|
||||
T0: Int64; // PERF
|
||||
// OutL/OutR теперь поля класса — не аллоцируем каждый раз
|
||||
begin
|
||||
FRXAccPos := 0;
|
||||
if not FInitialized then Exit;
|
||||
|
||||
try
|
||||
T0 := PerfNow; // PERF
|
||||
// Упаковываем в interleaved буфер для fexchange0
|
||||
for i := 0 to FBufSize - 1 do
|
||||
begin
|
||||
@@ -1453,6 +1462,7 @@ begin
|
||||
// DSP обработка
|
||||
Err := 0;
|
||||
fexchange0(RXA_CHAN, @FRXIn[0], @FRXOut[0], @Err);
|
||||
PerfAdd(pzDspFexMain, T0); // PERF: главный RX-канал WDSP
|
||||
|
||||
// S-meter: читаем ОДИН раз в DSP-колбэке и кешируем в FSMeter.
|
||||
// GetRXAMeter сбрасывает аккумулятор после вызова, поэтому второй
|
||||
@@ -1474,12 +1484,14 @@ begin
|
||||
and (not FTXActive or (FKeepRXDuringTX and FMonitorRXDuringTX))
|
||||
and (FPostTXMuteUntil = 0) then
|
||||
begin
|
||||
T0 := PerfNow; // PERF
|
||||
for i := 0 to FAudioBufSize - 1 do
|
||||
begin
|
||||
FOutL[i] := FRXOut[i * 2] * FVolume;
|
||||
FOutR[i] := FRXOut[i * 2 + 1] * FVolume;
|
||||
end;
|
||||
FOnAudio(FOutL, FOutR, FAudioBufSize);
|
||||
PerfAdd(pzDspAudioCb, T0); // PERF: маршрут RX-аудио (web/радио/звуковуха)
|
||||
end;
|
||||
|
||||
// Софтовые слайсы: fan-out того же IQ-блока на доп. WDSP-каналы.
|
||||
@@ -1586,8 +1598,10 @@ procedure TWDSPEngine.ProcessSlices;
|
||||
var
|
||||
s, i, k, Err: Integer;
|
||||
V: Double;
|
||||
T0, TS: Int64; // PERF
|
||||
begin
|
||||
if not Assigned(FOnSliceAudio) then Exit;
|
||||
T0 := PerfNow; // PERF
|
||||
FSliceLock.Enter;
|
||||
try
|
||||
for s := 0 to MAX_SLICES - 1 do
|
||||
@@ -1601,7 +1615,9 @@ begin
|
||||
FSliceIn[i * 2 + 1] := FRXAccQ[i];
|
||||
end;
|
||||
Err := 0;
|
||||
TS := PerfNow; // PERF
|
||||
fexchange0(FSlices[s].Chan, @FSliceIn[0], @FSliceOut[0], @Err);
|
||||
PerfAdd(pzDspSliceFex, TS); // PERF: fexchange0 одного слайса
|
||||
// S-метр канала читаем здесь (DSP-поток) — GetRXAMeter сбрасывает аккумулятор.
|
||||
FSlices[s].SMeter := GetRXAMeter(FSlices[s].Chan, RXA_S_AV);
|
||||
V := FSlices[s].Volume;
|
||||
@@ -1615,6 +1631,7 @@ begin
|
||||
finally
|
||||
FSliceLock.Leave;
|
||||
end;
|
||||
PerfAdd(pzDspSlices, T0); // PERF: fan-out всех слайсов целиком
|
||||
end;
|
||||
|
||||
function TWDSPEngine.AddSlice(Id, Mode, FilterLo, FilterHi: Integer;
|
||||
@@ -2198,8 +2215,10 @@ var
|
||||
i, tail: Integer;
|
||||
Err: Integer;
|
||||
Avail: Integer;
|
||||
T0: Int64; // PERF
|
||||
begin
|
||||
if not FInitialized then Exit;
|
||||
T0 := PerfNow; // PERF
|
||||
|
||||
Avail := (FTXMicHead - FTXMicTail + TX_MIC_RING) and (TX_MIC_RING - 1);
|
||||
|
||||
@@ -2235,6 +2254,7 @@ begin
|
||||
// FTXOut содержит FTXOutBufSize IQ пар @ FTXSampleRate (interleaved double)
|
||||
if Assigned(FOnTXIQ) then
|
||||
FOnTXIQ(FTXOut, FTXOutBufSize);
|
||||
PerfAdd(pzDspTxBlock, T0); // PERF: TX-блок целиком (fexchange0 + analyzer + IQ out)
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.PushTXMicSamples16(const Src: array of SmallInt; N: Integer);
|
||||
@@ -2532,8 +2552,10 @@ var
|
||||
Flag: Integer;
|
||||
i, N: Integer;
|
||||
DispID: Integer;
|
||||
T0: Int64; // PERF
|
||||
begin
|
||||
if not FInitialized or not FAnalyzerOpen then Exit;
|
||||
T0 := PerfNow; // PERF
|
||||
N := EnsureRange(FDisplayPixelCount, 1, SPECTRUM_PIXELS);
|
||||
DispID := FActiveDisplayID; // RX_DISP_ID или TX_DISP_ID, зависит от FTXActive
|
||||
|
||||
@@ -2556,6 +2578,7 @@ begin
|
||||
if Assigned(FOnWaterfall) then
|
||||
FOnWaterfall(FWaterfallPixels, N);
|
||||
end;
|
||||
PerfAdd(pzDispGetPixels, T0); // PERF: GetPixels + колбэки спектр/водопад
|
||||
end;
|
||||
|
||||
procedure TWDSPEngine.GetSpectrumData(var Pixels: array of Single;
|
||||
|
||||
Reference in New Issue
Block a user