Skip to content

Commit c8429f4

Browse files
author
DavidQ
committed
Optimize overlay performance.
PR Details: - Improves efficiency and scalability
1 parent afa6d05 commit c8429f4

7 files changed

Lines changed: 128 additions & 33 deletions

File tree

docs/dev/CODEX_COMMANDS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ MODEL: GPT-5.4
22
REASONING: medium
33

44
COMMAND:
5-
Implement event-driven overlay updates:
6-
- Use events instead of polling
7-
- Improve responsiveness
5+
Optimize overlay performance:
6+
- Reduce overhead
7+
- Improve event efficiency
88
- Update roadmap status only

docs/dev/COMMIT_COMMENT.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Add event-driven overlay updates.
1+
Optimize overlay performance.
22

33
PR Details:
4-
- Improves responsiveness and efficiency
4+
- Improves efficiency and scalability
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[ ] Events trigger updates
2-
[ ] No polling overhead
31
[ ] Performance improved
2+
[ ] No regressions
3+
[ ] Stable behavior
44
[ ] Roadmap updated

docs/dev/roadmaps/MASTER_ROADMAP_HIGH_LEVEL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@
824824
- [x] ensure future phases can build cleanly
825825

826826
### Track H — Final Stability Gate
827-
- [ ] full-repo validation sweep
827+
- [.] full-repo validation sweep
828828
- [x] zero regression requirement
829829
- [x] contract freeze readiness
830830
- [x] readiness for long-term maintenance mode

docs/pr/BUILD_PR.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
# BUILD_PR_LEVEL_21_4_OVERLAY_EVENT_DRIVEN_UPDATES
1+
# BUILD_PR_LEVEL_21_5_OVERLAY_PERFORMANCE_OPTIMIZATION
22

33
## Purpose
4-
Move overlay updates to an event-driven model.
4+
Optimize overlay system performance under advanced usage.
55

66
## Roadmap Improvement
7-
Improves efficiency and responsiveness of overlays.
7+
Enhances efficiency and scalability of Level 21 overlays.
88

99
## Scope
10-
- Trigger overlay updates via events
11-
- Reduce unnecessary polling
12-
- Validate responsiveness
10+
- Reduce render overhead
11+
- Optimize event handling
12+
- Validate performance gains
1313

1414
## Test Steps
15-
1. Trigger gameplay events
16-
2. Verify overlay updates instantly
17-
3. Confirm no extra processing
15+
1. Run multiple overlays
16+
2. Measure performance
17+
3. Compare before/after
1818

1919
## Expected
20-
- Event-driven updates working
2120
- Improved performance
21+
- No regressions

samples/phase-17/shared/overlayGameplayRuntime.js

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,30 @@ function resolveOverlayRuntimeEventQueue(context = {}, gameplayState = null, syn
267267
return null;
268268
}
269269

270+
function hasOverlayRuntimeSyncPatch(syncPatch) {
271+
if (!syncPatch || typeof syncPatch !== 'object') {
272+
return false;
273+
}
274+
if (syncPatch.visible === true || syncPatch.visible === false) {
275+
return true;
276+
}
277+
if (Number.isFinite(Number(syncPatch.interactionIndex))) {
278+
return true;
279+
}
280+
return String(syncPatch.activeOverlayId || '').trim().length > 0;
281+
}
282+
283+
function computeOverlayRuntimeCompatSignature(syncPatch, count) {
284+
if (!hasOverlayRuntimeSyncPatch(syncPatch)) {
285+
return '';
286+
}
287+
const visibleToken = syncPatch.visible === true ? '1' : (syncPatch.visible === false ? '0' : 'x');
288+
const hasInteractionIndex = Number.isFinite(Number(syncPatch.interactionIndex));
289+
const interactionToken = hasInteractionIndex ? String(Math.trunc(Number(syncPatch.interactionIndex))) : 'x';
290+
const activeOverlayId = String(syncPatch.activeOverlayId || '').trim();
291+
return `${visibleToken}|${interactionToken}|${activeOverlayId}|${Math.max(0, Number(count) || 0)}`;
292+
}
293+
270294
export function enqueueOverlayGameplayRuntimeSyncEvent(target, event = {}) {
271295
if (!target || typeof target !== 'object') {
272296
return false;
@@ -303,6 +327,43 @@ function normalizeOverlayRuntimeSyncEvent(event) {
303327
};
304328
}
305329

330+
function consumeOverlayRuntimeSyncEvents(eventQueue = []) {
331+
if (!Array.isArray(eventQueue) || eventQueue.length === 0) {
332+
return {
333+
eventsProcessed: 0,
334+
patch: {},
335+
};
336+
}
337+
338+
const patch = {};
339+
let eventsProcessed = 0;
340+
for (let i = 0; i < eventQueue.length; i += 1) {
341+
const event = normalizeOverlayRuntimeSyncEvent(eventQueue[i]);
342+
if (!event) {
343+
continue;
344+
}
345+
if (event.type !== 'overlay-runtime-sync' && event.type !== 'overlay-state-sync') {
346+
continue;
347+
}
348+
eventsProcessed += 1;
349+
if (event.visible === true || event.visible === false) {
350+
patch.visible = event.visible;
351+
}
352+
if (Number.isFinite(event.interactionIndex)) {
353+
patch.interactionIndex = event.interactionIndex;
354+
}
355+
if (event.activeOverlayId) {
356+
patch.activeOverlayId = event.activeOverlayId;
357+
}
358+
}
359+
eventQueue.length = 0;
360+
361+
return {
362+
eventsProcessed,
363+
patch,
364+
};
365+
}
366+
306367
function applyOverlayRuntimeSyncPatch(runtime, runtimeExtensions, patch = {}) {
307368
const count = runtimeExtensions.length;
308369
let desyncCorrected = false;
@@ -372,27 +433,29 @@ export function synchronizeOverlayGameplayRuntimeState(runtime, context = {}) {
372433
const count = runtimeExtensions.length;
373434
let desyncCorrected = false;
374435
let eventsProcessed = 0;
436+
let syncMode = 'cached';
375437

376438
if (Array.isArray(eventQueue) && eventQueue.length > 0) {
377-
const pending = eventQueue.splice(0, eventQueue.length);
378-
for (let i = 0; i < pending.length; i += 1) {
379-
const event = normalizeOverlayRuntimeSyncEvent(pending[i]);
380-
if (!event) {
381-
continue;
382-
}
383-
if (event.type !== 'overlay-runtime-sync' && event.type !== 'overlay-state-sync') {
384-
continue;
385-
}
386-
const corrected = applyOverlayRuntimeSyncPatch(runtime, runtimeExtensions, event);
439+
const consumedEvents = consumeOverlayRuntimeSyncEvents(eventQueue);
440+
eventsProcessed = consumedEvents.eventsProcessed;
441+
if (hasOverlayRuntimeSyncPatch(consumedEvents.patch)) {
442+
const corrected = applyOverlayRuntimeSyncPatch(runtime, runtimeExtensions, consumedEvents.patch);
387443
desyncCorrected = desyncCorrected || corrected;
388-
eventsProcessed += 1;
444+
}
445+
if (eventsProcessed > 0) {
446+
syncMode = 'events';
389447
}
390448
}
391449

392-
// Compatibility fallback for pre-event producers.
450+
// Compatibility fallback for pre-event producers with change detection to avoid repeated polling overhead.
393451
if (eventsProcessed === 0 && syncState) {
394-
const corrected = applyOverlayRuntimeSyncPatch(runtime, runtimeExtensions, syncState);
395-
desyncCorrected = desyncCorrected || corrected;
452+
const compatSignature = computeOverlayRuntimeCompatSignature(syncState, count);
453+
if (compatSignature && runtime.interactionCompatSyncSignature !== compatSignature) {
454+
const corrected = applyOverlayRuntimeSyncPatch(runtime, runtimeExtensions, syncState);
455+
desyncCorrected = desyncCorrected || corrected;
456+
syncMode = 'compat';
457+
runtime.interactionCompatSyncSignature = compatSignature;
458+
}
396459
}
397460

398461
const interactionIndex = normalizeInteractionIndex(runtime);
@@ -405,9 +468,10 @@ export function synchronizeOverlayGameplayRuntimeState(runtime, context = {}) {
405468
cycleKey: String(runtime.interactionCycleKey || LEVEL17_OVERLAY_CYCLE_KEY),
406469
desyncCorrected,
407470
eventsProcessed,
408-
syncMode: eventsProcessed > 0 ? 'events' : 'compat',
471+
syncMode,
409472
};
410473
writeOverlayRuntimeSyncSnapshot(syncState, snapshot);
474+
runtime.interactionCompatSyncSignature = computeOverlayRuntimeCompatSignature(syncState, count);
411475
return snapshot;
412476
}
413477

tests/runtime/Phase19OverlayExpansionFramework.test.mjs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ function assertOverlayStateSynchronizationAndDesyncRecovery() {
277277
const idleInteraction = getOverlayGameplayRuntimeInteractionSnapshot(runtime, { gameplayState });
278278
assert.equal(idleInteraction.activeOverlayId, 'runtime-b', 'No-event sync should keep prior synchronized state stable.');
279279
assert.equal(gameplayState.overlayRuntimeState.eventsProcessed, 0, 'No queued events should avoid extra event processing.');
280+
assert.equal(gameplayState.overlayRuntimeState.syncMode, 'cached', 'No-event sync should use cached state and avoid fallback re-apply.');
280281

281282
const renderRuntimeB = renderOverlayGameplayRuntime(runtime, {
282283
activeOverlayId: 'runtime-b',
@@ -309,10 +310,40 @@ function assertOverlayStateSynchronizationAndDesyncRecovery() {
309310
assert.equal(renderRuntimeA, 1, 'Gameplay sync should recover to visible overlay rendering without desync.');
310311
}
311312

313+
function assertOverlayEventCoalescingEfficiency() {
314+
const framework = createPhase19OverlayExpansionFramework();
315+
framework.registerExtension(definePhase19OverlayExtension({
316+
id: 'phase19-overlay-event-coalesce',
317+
overlays: [
318+
{ id: 'runtime-a', label: 'Runtime A' },
319+
{ id: 'runtime-b', label: 'Runtime B' },
320+
],
321+
initialOverlayId: 'runtime-a',
322+
runtimeExtensions: [
323+
{ overlayId: 'runtime-a', onRender() {} },
324+
{ overlayId: 'runtime-b', onRender() {} },
325+
],
326+
}));
327+
328+
const runtime = framework.createRuntimeForExtension('phase19-overlay-event-coalesce');
329+
const gameplayState = { overlayRuntimeState: {} };
330+
enqueueOverlayGameplayRuntimeSyncEvent(gameplayState, { visible: true, interactionIndex: 0, activeOverlayId: 'runtime-a' });
331+
enqueueOverlayGameplayRuntimeSyncEvent(gameplayState, { visible: false });
332+
enqueueOverlayGameplayRuntimeSyncEvent(gameplayState, { visible: true, interactionIndex: 999, activeOverlayId: 'runtime-b' });
333+
334+
const interaction = getOverlayGameplayRuntimeInteractionSnapshot(runtime, { gameplayState });
335+
assert.equal(interaction.activeOverlayId, 'runtime-b', 'Coalesced event processing should keep the final overlay selection.');
336+
assert.equal(interaction.visible, true, 'Coalesced event processing should keep the final visibility state.');
337+
assert.equal(gameplayState.overlayRuntimeState.eventsProcessed, 3, 'Event efficiency path should consume all queued events in one synchronization pass.');
338+
assert.equal(gameplayState.overlayRuntimeState.desyncCorrected, true, 'Event coalescing should preserve desync correction behavior.');
339+
assert.equal(gameplayState.overlayRuntimeEvents.length, 0, 'Event queue should be drained after synchronization.');
340+
}
341+
312342
export function run() {
313343
assertExpansionRegistrationAndCompatibility();
314344
assertExtensionLifecycleMutations();
315345
assertDynamicPanelSizingCapability();
316346
assertContextAwareOverlayBehavior();
317347
assertOverlayStateSynchronizationAndDesyncRecovery();
348+
assertOverlayEventCoalescingEfficiency();
318349
}

0 commit comments

Comments
 (0)