Fix skipped realtime analyzer frames

This commit is contained in:
Mikei386
2026-08-05 15:26:56 +02:00
parent cef997d246
commit afcceec986
2 changed files with 22 additions and 3 deletions
+7
View File
@@ -10,4 +10,11 @@ assert(!/\bopt-w-\d+\b/.test(styleSelect), 'style selector must not have a fixed
assert(/adjustStyleDropdownWidth\(\)/.test(main), 'dynamic style selector sizing is missing'); assert(/adjustStyleDropdownWidth\(\)/.test(main), 'dynamic style selector sizing is missing');
assert(/styleSel\.style\.width\s*=/.test(main), 'dynamic style selector width assignment is missing'); assert(/styleSel\.style\.width\s*=/.test(main), 'dynamic style selector width assignment is missing');
assert(/currentMetricsSeq\s*=\s*Number\.isFinite\(env\.audio\?\.phoenixMetricsSeq\)/.test(main),
'render loop must track the metrics sequence used by the RTA');
assert(/renderRequestGeneration\s*\+=\s*1/.test(main),
'render requests must carry a generation that survives asynchronous rendering');
assert(/renderRequestGeneration\s*===\s*renderGeneration\)\s*renderDirty\s*=\s*false/.test(main),
'an in-flight render must not clear a newer render request');
console.log('GUI contract regression tests passed'); console.log('GUI contract regression tests passed');
+15 -3
View File
@@ -2634,6 +2634,7 @@ let prevSaverActive = false;
let audioRecoverInFlight = false; let audioRecoverInFlight = false;
let lastAudioRecoverAt = 0; let lastAudioRecoverAt = 0;
let renderDirty = true; let renderDirty = true;
let renderRequestGeneration = 0;
let lastRenderedAudioSeq = 0; let lastRenderedAudioSeq = 0;
let processingProfileDirty = true; let processingProfileDirty = true;
let appliedProcessingProfileView = ''; let appliedProcessingProfileView = '';
@@ -2642,6 +2643,7 @@ const DATA_ONLY_RENDER_REASONS = new Set(['audio', 'spectro', 'visuals']);
function requestRender(reason = 'ui') { function requestRender(reason = 'ui') {
renderDirty = true; renderDirty = true;
renderRequestGeneration += 1;
if (!DATA_ONLY_RENDER_REASONS.has(reason)) processingProfileDirty = true; if (!DATA_ONLY_RENDER_REASONS.has(reason)) processingProfileDirty = true;
env.__lastRenderReason = reason; env.__lastRenderReason = reason;
} }
@@ -2686,7 +2688,14 @@ async function loop(now){
} }
const targetMs = audioOk ? (1000 / desiredFpsForStyle(renderStyle)) : (1000 / 20); const targetMs = audioOk ? (1000 / desiredFpsForStyle(renderStyle)) : (1000 / 20);
const target = targetMs; const target = targetMs;
const currentAudioSeq = Number.isFinite(env.audio?.xySeq) ? env.audio.xySeq : 0; const currentMetricsSeq = Number.isFinite(env.audio?.phoenixMetricsSeq)
? env.audio.phoenixMetricsSeq
: 0;
const currentVisualsSeq = Number.isFinite(env.audio?.phoenixVisualsSeq)
? env.audio.phoenixVisualsSeq
: 0;
const currentXySeq = Number.isFinite(env.audio?.xySeq) ? env.audio.xySeq : 0;
const currentAudioSeq = Math.max(currentMetricsSeq, currentVisualsSeq, currentXySeq);
const audioDirty = currentAudioSeq > lastRenderedAudioSeq; const audioDirty = currentAudioSeq > lastRenderedAudioSeq;
const shouldRenderFrame = const shouldRenderFrame =
renderDirty || renderDirty ||
@@ -2714,14 +2723,17 @@ async function loop(now){
} }
prevSaverActive = saverActive; prevSaverActive = saverActive;
if (!saverActive && shouldRenderFrame) { if (!saverActive && shouldRenderFrame) {
const renderGeneration = renderRequestGeneration;
meterFacade.setFrameStamp?.(++frameCounter); meterFacade.setFrameStamp?.(++frameCounter);
drawBG(); drawBG();
if (!currentView || currentViewId !== renderStyle) setView(renderStyle); if (!currentView || currentViewId !== renderStyle) setView(renderStyle);
if (currentView.render) await currentView.render(env, viewState); if (currentView.render) await currentView.render(env, viewState);
drawOptionsPanelBackdrop(); drawOptionsPanelBackdrop();
drawAudioLostOverlay(); drawAudioLostOverlay();
renderDirty = false; // A packet or UI event can request another frame while the async view
lastRenderedAudioSeq = currentAudioSeq; // render is in progress. Do not erase that newer request here.
if (renderRequestGeneration === renderGeneration) renderDirty = false;
lastRenderedAudioSeq = Math.max(lastRenderedAudioSeq, currentAudioSeq);
} }
} catch(e){ } catch(e){
showErr('Render error: ' + (e?.message || String(e))); showErr('Render error: ' + (e?.message || String(e)));