essais
This commit is contained in:
145
src/shader/core/ShaderCompiler.ts
Normal file
145
src/shader/core/ShaderCompiler.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import { ShaderFunction } from '../types';
|
||||
import { PERFORMANCE } from '../../utils/constants';
|
||||
|
||||
/**
|
||||
* Handles shader code compilation and optimization
|
||||
*/
|
||||
export class ShaderCompiler {
|
||||
/**
|
||||
* Compiles shader code into an executable function
|
||||
*/
|
||||
static compile(code: string): ShaderFunction {
|
||||
const safeCode = this.sanitizeCode(code);
|
||||
|
||||
// Check if expression is static (contains no variables)
|
||||
const isStatic = this.isStaticExpression(safeCode);
|
||||
|
||||
if (isStatic) {
|
||||
// Pre-compute static value
|
||||
const staticValue = this.evaluateStaticExpression(safeCode);
|
||||
return (_ctx) => staticValue;
|
||||
}
|
||||
|
||||
return new Function(
|
||||
'ctx',
|
||||
`
|
||||
// Destructure context for backward compatibility with existing shader code
|
||||
const {
|
||||
x, y, t, i, r, a, u, v, c, f, d, n, b, bn, bs, be, bw,
|
||||
w, h, p, z, j, o, g, m, l, k, s, e, mouseX, mouseY,
|
||||
mousePressed, mouseVX, mouseVY, mouseClickTime, touchCount,
|
||||
touch0X, touch0Y, touch1X, touch1Y, pinchScale, pinchRotation,
|
||||
accelX, accelY, accelZ, gyroX, gyroY, gyroZ, audioLevel,
|
||||
bassLevel, midLevel, trebleLevel, bpm, _t, bx, by, sx, sy, qx, qy
|
||||
} = ctx;
|
||||
|
||||
// Shader-specific helper functions
|
||||
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
||||
const lerp = (a, b, t) => a + (b - a) * t;
|
||||
const smooth = (edge, x) => { const t = Math.min(Math.max((x - edge) / (1 - edge), 0), 1); return t * t * (3 - 2 * t); };
|
||||
const step = (edge, x) => x < edge ? 0 : 1;
|
||||
const fract = (x) => x - Math.floor(x);
|
||||
const mix = (a, b, t) => a + (b - a) * t;
|
||||
|
||||
// Timeout protection
|
||||
const startTime = performance.now();
|
||||
let iterations = 0;
|
||||
|
||||
function checkTimeout() {
|
||||
iterations++;
|
||||
if (iterations % ${PERFORMANCE.TIMEOUT_CHECK_INTERVAL} === 0 && performance.now() - startTime > ${PERFORMANCE.MAX_SHADER_TIMEOUT_MS}) {
|
||||
throw new Error('Shader timeout');
|
||||
}
|
||||
}
|
||||
|
||||
return (function() {
|
||||
checkTimeout();
|
||||
return ${safeCode};
|
||||
})();
|
||||
`
|
||||
) as ShaderFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if shader code contains only static expressions (no variables)
|
||||
*/
|
||||
private static isStaticExpression(code: string): boolean {
|
||||
// Check if code contains any variables using regex for better accuracy
|
||||
const variablePattern = /\b(x|y|t|i|r|a|u|v|c|f|d|n|b|bn|bs|be|bw|m|l|k|s|e|w|h|p|z|j|o|g|bpm|bx|by|sx|sy|qx|qy|mouse[XY]|mousePressed|mouseV[XY]|mouseClickTime|touchCount|touch[01][XY]|pinchScale|pinchRotation|accel[XYZ]|gyro[XYZ]|audioLevel|bassLevel|midLevel|trebleLevel)\b/;
|
||||
|
||||
return !variablePattern.test(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates static expressions safely
|
||||
*/
|
||||
private static evaluateStaticExpression(code: string): number {
|
||||
try {
|
||||
// Safely evaluate numeric expression
|
||||
const result = new Function(`return ${code}`)();
|
||||
return isFinite(result) ? result : 0;
|
||||
} catch (error) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes shader code by auto-prefixing Math functions and constants
|
||||
*/
|
||||
private static sanitizeCode(code: string): string {
|
||||
// Create a single regex pattern for all replacements
|
||||
const mathFunctions = [
|
||||
'abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp',
|
||||
'floor', 'log', 'max', 'min', 'pow', 'random', 'round', 'sin',
|
||||
'sqrt', 'tan', 'trunc', 'sign', 'cbrt', 'hypot', 'imul', 'fround',
|
||||
'clz32', 'acosh', 'asinh', 'atanh', 'cosh', 'sinh', 'tanh',
|
||||
'expm1', 'log1p', 'log10', 'log2'
|
||||
];
|
||||
|
||||
const mathConstants = {
|
||||
'PI': 'Math.PI',
|
||||
'E': 'Math.E',
|
||||
'LN2': 'Math.LN2',
|
||||
'LN10': 'Math.LN10',
|
||||
'LOG2E': 'Math.LOG2E',
|
||||
'LOG10E': 'Math.LOG10E',
|
||||
'SQRT1_2': 'Math.SQRT1_2',
|
||||
'SQRT2': 'Math.SQRT2'
|
||||
};
|
||||
|
||||
// Build combined regex pattern
|
||||
const functionPattern = mathFunctions.join('|');
|
||||
const constantPattern = Object.keys(mathConstants).join('|');
|
||||
const combinedPattern = new RegExp(
|
||||
`\\b(${functionPattern})\\(|\\b(${constantPattern})\\b|\\bt\\s*\\(`,
|
||||
'g'
|
||||
);
|
||||
|
||||
// Single pass replacement
|
||||
const processedCode = code.replace(combinedPattern, (match, func, constant) => {
|
||||
if (func) {
|
||||
return `Math.${func}(`;
|
||||
} else if (constant) {
|
||||
return mathConstants[constant as keyof typeof mathConstants];
|
||||
} else if (match.startsWith('t')) {
|
||||
return '_t(';
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
return processedCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash for shader code caching
|
||||
*/
|
||||
static hashCode(str: string): string {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32-bit integer
|
||||
}
|
||||
return hash.toString(36);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user