This commit is contained in:
Andras Schmelczer 2026-05-16 15:05:35 +01:00
commit 1fe5015056
55 changed files with 2036 additions and 685 deletions

View file

@ -15,6 +15,7 @@ import { GardenUi } from './game-loop-types';
import { IntroPrompt } from './intro-prompt';
import { GardenPointerInput } from './pointer-input';
import { RenderInputCache } from './render-input-cache';
import { ToolbarContrastMonitor } from './toolbar-contrast-monitor';
export default class GameLoop {
private static readonly MAX_MIRROR_SEGMENT_COUNT =
@ -34,6 +35,7 @@ export default class GameLoop {
private readonly agentPopulation: AgentPopulation;
private readonly export4KRenderer: Export4KRenderer;
private readonly framePerformance = new FramePerformance();
private readonly toolbarContrastMonitor: ToolbarContrastMonitor;
private readonly devStatsElement: HTMLDivElement | null;
private readonly seed = Math.floor(Math.random() * 0xffffffff).toString(16);
private readonly resizeListener = this.resize.bind(this);
@ -56,6 +58,7 @@ export default class GameLoop {
this.resources = new GameLoopResources(canvas, device, this.canvasSize);
this.introPrompt = new IntroPrompt(ui.prompt);
this.eraserPreview = new EraserPreview(canvas, ui.eraserPreview);
this.toolbarContrastMonitor = new ToolbarContrastMonitor(canvas, ui.toolbar, device);
this.agentPopulation = new AgentPopulation(this.resources.agentGenerationPipeline);
this.agentPopulation.initializeIntroAgents(this.canvasSize);
this.pointerInput = new GardenPointerInput({
@ -85,8 +88,8 @@ export default class GameLoop {
getSourceTextureView: () => this.resources.textures.sourceMapA.getTextureView(),
getVibeId: () => activeVibe.id,
});
this.keydownListener = (event: KeyboardEvent) => {
this.audio.start(activeVibe, { userGesture: event.isTrusted });
this.keydownListener = () => {
this.audio.start(activeVibe, { userGesture: true });
this.introPrompt.complete();
};
@ -151,6 +154,7 @@ export default class GameLoop {
window.removeEventListener('resize', this.resizeListener);
window.removeEventListener('keydown', this.keydownListener);
this.pointerInput.detach();
this.toolbarContrastMonitor.destroy();
this.devStatsElement?.remove();
this.introPrompt.destroy();
this.resources.destroy();
@ -165,7 +169,7 @@ export default class GameLoop {
const frameCpuStartedAt = this.framePerformance.markCpuStart();
const deltaTime = this.deltaTimeCalculator.calculateDeltaTimeInSeconds(time);
this.framePerformance.update(deltaTime);
this.framePerformance.update(time);
this.agentPopulation.growBudget(
deltaTime,
this.framePerformance.smoothedFps,
@ -175,7 +179,6 @@ export default class GameLoop {
this.resize();
this.resizeSimulationToCanvas();
const scaledTime = time * settings.renderSpeed;
const { channelColors, backgroundColor } = this.renderInputs.get();
const introProgress = this.introPrompt.progress;
const cameraZoom = 1;
@ -195,7 +198,7 @@ export default class GameLoop {
});
this.resources.setFrameParameters({
time: scaledTime,
time,
deltaTime,
canvasSize: this.canvasSize,
activeAgentCount: this.agentPopulation.activeAgentCount,
@ -210,7 +213,10 @@ export default class GameLoop {
});
const encodeCpuStartedAt = this.framePerformance.markCpuStart();
this.resources.executeFrame(settings.renderSpeed, isErasing);
this.resources.executeFrame(
isErasing,
this.toolbarContrastMonitor.takeReadbackRequest(time)
);
const encodeCpuMs = this.framePerformance.measureSince(encodeCpuStartedAt);
this.pointerInput.clearSwipesIfIdle();
@ -223,7 +229,6 @@ export default class GameLoop {
agentBudgetMax: settings.agentBudgetMax,
canvas: this.canvas,
devicePixelRatio: this.devicePixelRatio,
renderSpeed: settings.renderSpeed,
});
this.updateDevStats(time);