some easy wins

This commit is contained in:
2025-07-05 23:02:15 +02:00
parent 3bf96a5721
commit f7054d8300
2 changed files with 508 additions and 79 deletions

View File

@ -50,6 +50,21 @@ export class FakeShader {
private isRendering: boolean = false;
private pendingRenders: string[] = [];
private renderMode: string = 'classic';
private offscreenCanvas: OffscreenCanvas | null = null;
private offscreenCtx: OffscreenCanvasRenderingContext2D | null = null;
private useOffscreen: boolean = false;
// Adaptive resolution scaling
private adaptiveCanvas: HTMLCanvasElement;
private adaptiveCtx: CanvasRenderingContext2D;
private currentScale: number = 1.0;
private targetRenderTime: number = 16; // Target 60 FPS
private performanceHistory: number[] = [];
private lastScaleAdjustment: number = 0;
private minScale: number = 0.25;
private maxScale: number = 1.0;
private renderStartTime: number = 0;
private mouseX: number = 0;
private mouseY: number = 0;
private mousePressed: boolean = false;
@ -84,6 +99,12 @@ export class FakeShader {
this.ctx = canvas.getContext('2d')!;
this.code = code;
// Initialize adaptive resolution canvas
this.initializeAdaptiveCanvas();
// Initialize offscreen canvas if supported
this.initializeOffscreenCanvas();
// Initialize worker
this.worker = new Worker(new URL('./ShaderWorker.ts', import.meta.url), { type: 'module' });
this.worker.onmessage = (e: MessageEvent<WorkerResponse>) => this.handleWorkerMessage(e.data);
@ -92,6 +113,35 @@ export class FakeShader {
this.compile();
}
private initializeAdaptiveCanvas(): void {
this.adaptiveCanvas = document.createElement('canvas');
this.adaptiveCtx = this.adaptiveCanvas.getContext('2d')!;
this.updateAdaptiveCanvasSize();
}
private updateAdaptiveCanvasSize(): void {
const scaledWidth = Math.floor(this.canvas.width * this.currentScale);
const scaledHeight = Math.floor(this.canvas.height * this.currentScale);
if (this.adaptiveCanvas.width !== scaledWidth || this.adaptiveCanvas.height !== scaledHeight) {
this.adaptiveCanvas.width = scaledWidth;
this.adaptiveCanvas.height = scaledHeight;
}
}
private initializeOffscreenCanvas(): void {
if (typeof OffscreenCanvas !== 'undefined') {
try {
this.offscreenCanvas = new OffscreenCanvas(this.canvas.width, this.canvas.height);
this.offscreenCtx = this.offscreenCanvas.getContext('2d');
this.useOffscreen = this.offscreenCtx !== null;
} catch (error) {
console.warn('OffscreenCanvas not supported:', error);
this.useOffscreen = false;
}
}
}
private handleWorkerMessage(response: WorkerResponse): void {
switch (response.type) {
case 'compiled':
@ -105,7 +155,14 @@ export class FakeShader {
case 'rendered':
this.isRendering = false;
if (response.success && response.imageData) {
this.ctx.putImageData(response.imageData, 0, 0);
// Put ImageData on adaptive resolution canvas
this.adaptiveCtx.putImageData(response.imageData, 0, 0);
// Upscale to main canvas with proper interpolation
this.upscaleToMainCanvas();
// Monitor performance and adjust scale
this.updatePerformanceMetrics();
} else {
console.error('Render failed:', response.error);
this.fillBlack();
@ -151,27 +208,37 @@ export class FakeShader {
return;
}
// Update adaptive canvas size based on current scale
this.updateAdaptiveCanvasSize();
// Start performance timing
this.renderStartTime = performance.now();
this.isRendering = true;
const currentTime = (Date.now() - this.startTime) / 1000;
// Scale mouse coordinates to match render resolution
const scaledMouseX = this.mouseX * this.currentScale;
const scaledMouseY = this.mouseY * this.currentScale;
this.worker.postMessage({
id,
type: 'render',
width: this.canvas.width,
height: this.canvas.height,
width: this.adaptiveCanvas.width,
height: this.adaptiveCanvas.height,
time: currentTime,
renderMode: this.renderMode,
mouseX: this.mouseX,
mouseY: this.mouseY,
mouseX: scaledMouseX,
mouseY: scaledMouseY,
mousePressed: this.mousePressed,
mouseVX: this.mouseVX,
mouseVY: this.mouseVY,
mouseVX: this.mouseVX * this.currentScale,
mouseVY: this.mouseVY * this.currentScale,
mouseClickTime: this.mouseClickTime,
touchCount: this.touchCount,
touch0X: this.touch0X,
touch0Y: this.touch0Y,
touch1X: this.touch1X,
touch1Y: this.touch1Y,
touch0X: this.touch0X * this.currentScale,
touch0Y: this.touch0Y * this.currentScale,
touch1X: this.touch1X * this.currentScale,
touch1Y: this.touch1Y * this.currentScale,
pinchScale: this.pinchScale,
pinchRotation: this.pinchRotation,
accelX: this.accelX,
@ -295,6 +362,82 @@ export class FakeShader {
this.worker.terminate();
}
private upscaleToMainCanvas(): void {
// Clear main canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
// Set interpolation based on scale
if (this.currentScale < 0.5) {
// Use smooth interpolation for heavily downscaled content
this.ctx.imageSmoothingEnabled = true;
this.ctx.imageSmoothingQuality = 'high';
} else {
// Use pixel-perfect scaling for minimal downscaling
this.ctx.imageSmoothingEnabled = false;
}
// Draw scaled content to main canvas
this.ctx.drawImage(
this.adaptiveCanvas,
0, 0, this.adaptiveCanvas.width, this.adaptiveCanvas.height,
0, 0, this.canvas.width, this.canvas.height
);
}
private updatePerformanceMetrics(): void {
const renderTime = performance.now() - this.renderStartTime;
// Add to performance history
this.performanceHistory.push(renderTime);
if (this.performanceHistory.length > 10) {
this.performanceHistory.shift(); // Keep only last 10 measurements
}
// Adjust scale if we have enough data and enough time has passed
const now = performance.now();
if (this.performanceHistory.length >= 3 && now - this.lastScaleAdjustment > 500) {
this.adjustRenderScale();
this.lastScaleAdjustment = now;
}
}
private adjustRenderScale(): void {
// Calculate average render time from recent history
const avgRenderTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
const tolerance = 2; // 2ms tolerance
if (avgRenderTime > this.targetRenderTime + tolerance) {
// Too slow - scale down
const newScale = Math.max(this.minScale, this.currentScale * 0.85);
if (newScale !== this.currentScale) {
this.currentScale = newScale;
console.log(`Scaling down to ${(this.currentScale * 100).toFixed(0)}% (${avgRenderTime.toFixed(1)}ms avg)`);
}
} else if (avgRenderTime < this.targetRenderTime - tolerance && this.currentScale < this.maxScale) {
// Fast enough - try scaling up
const newScale = Math.min(this.maxScale, this.currentScale * 1.1);
if (newScale !== this.currentScale) {
this.currentScale = newScale;
console.log(`Scaling up to ${(this.currentScale * 100).toFixed(0)}% (${avgRenderTime.toFixed(1)}ms avg)`);
}
}
}
setAdaptiveQuality(enabled: boolean, targetFPS: number = 60): void {
if (enabled) {
this.targetRenderTime = 1000 / targetFPS;
this.currentScale = 1.0;
this.performanceHistory = [];
} else {
this.currentScale = 1.0;
}
}
getCurrentScale(): number {
return this.currentScale;
}
static generateRandomCode(): string {
const presets = [
'x^y',