optimisations
This commit is contained in:
114
src/shader/core/RenderController.ts
Normal file
114
src/shader/core/RenderController.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { TIMING } from '../../utils/constants';
|
||||
|
||||
/**
|
||||
* Manages animation timing and frame rate control
|
||||
* Extracted from FakeShader for better separation of concerns
|
||||
*/
|
||||
export class RenderController {
|
||||
private animationId: number | null = null;
|
||||
private startTime: number = Date.now();
|
||||
private targetFPS: number = TIMING.DEFAULT_FPS;
|
||||
private frameInterval: number = TIMING.MILLISECONDS_PER_SECOND / this.targetFPS;
|
||||
private lastFrameTime: number = 0;
|
||||
private timeSpeed: number = 1.0;
|
||||
private isRendering: boolean = false;
|
||||
private pendingRenders: string[] = [];
|
||||
private idCounter: number = 0;
|
||||
|
||||
private onRenderFrame?: (time: number, renderId: string) => void;
|
||||
|
||||
setRenderFrameHandler(handler: (time: number, renderId: string) => void): void {
|
||||
this.onRenderFrame = handler;
|
||||
}
|
||||
|
||||
start(): void {
|
||||
if (this.animationId !== null) return;
|
||||
|
||||
const animate = (timestamp: number) => {
|
||||
if (timestamp - this.lastFrameTime >= this.frameInterval) {
|
||||
const currentTime = (Date.now() - this.startTime) / TIMING.MILLISECONDS_PER_SECOND * this.timeSpeed;
|
||||
const renderId = this.generateId();
|
||||
|
||||
this.onRenderFrame?.(currentTime, renderId);
|
||||
this.lastFrameTime = timestamp;
|
||||
}
|
||||
|
||||
this.animationId = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
this.animationId = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.animationId !== null) {
|
||||
cancelAnimationFrame(this.animationId);
|
||||
this.animationId = null;
|
||||
}
|
||||
}
|
||||
|
||||
setTargetFPS(fps: number): void {
|
||||
this.targetFPS = Math.max(TIMING.MIN_FPS, Math.min(TIMING.MAX_FPS, fps));
|
||||
this.frameInterval = TIMING.MILLISECONDS_PER_SECOND / this.targetFPS;
|
||||
}
|
||||
|
||||
setTimeSpeed(speed: number): void {
|
||||
this.timeSpeed = speed;
|
||||
}
|
||||
|
||||
getTimeSpeed(): number {
|
||||
return this.timeSpeed;
|
||||
}
|
||||
|
||||
getCurrentTime(): number {
|
||||
return (Date.now() - this.startTime) / TIMING.MILLISECONDS_PER_SECOND * this.timeSpeed;
|
||||
}
|
||||
|
||||
isAnimating(): boolean {
|
||||
return this.animationId !== null;
|
||||
}
|
||||
|
||||
generateId(): string {
|
||||
return `render_${this.idCounter++}_${Date.now()}`;
|
||||
}
|
||||
|
||||
setRenderingState(isRendering: boolean): void {
|
||||
this.isRendering = isRendering;
|
||||
}
|
||||
|
||||
isCurrentlyRendering(): boolean {
|
||||
return this.isRendering;
|
||||
}
|
||||
|
||||
addPendingRender(renderId: string): void {
|
||||
this.pendingRenders.push(renderId);
|
||||
|
||||
// Keep only the latest render to avoid backlog
|
||||
if (this.pendingRenders.length > 3) {
|
||||
const latestId = this.pendingRenders[this.pendingRenders.length - 1];
|
||||
this.pendingRenders = [latestId];
|
||||
}
|
||||
}
|
||||
|
||||
removePendingRender(renderId: string): void {
|
||||
const index = this.pendingRenders.indexOf(renderId);
|
||||
if (index !== -1) {
|
||||
this.pendingRenders.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
getPendingRenders(): string[] {
|
||||
return [...this.pendingRenders];
|
||||
}
|
||||
|
||||
clearPendingRenders(): void {
|
||||
this.pendingRenders = [];
|
||||
}
|
||||
|
||||
getFrameRate(): number {
|
||||
return this.targetFPS;
|
||||
}
|
||||
|
||||
getFrameInterval(): number {
|
||||
return this.frameInterval;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user