diff --git a/src/core/managers/WebGLManager.ts b/src/core/managers/WebGLManager.ts index 2c4a2ac..5ffd3f2 100644 --- a/src/core/managers/WebGLManager.ts +++ b/src/core/managers/WebGLManager.ts @@ -231,49 +231,97 @@ export class WebGLManager { }; break; - case 'vec2': + case 'vec2': { + const buffer = new Float32Array(2); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<2>; - gl.uniform2fv(location, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<2> | number[]; + if (Array.isArray(value)) { + buffer[0] = value[0]; + buffer[1] = value[1]; + gl.uniform2fv(location, buffer); + } else { + gl.uniform2fv(location, value); + } + return buffer as TypedFloat32Array<2>; }; break; - case 'vec3': + } + case 'vec3': { + const buffer = new Float32Array(3); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<3>; - gl.uniform3fv(location, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<3> | number[]; + if (Array.isArray(value)) { + buffer[0] = value[0]; + buffer[1] = value[1]; + buffer[2] = value[2]; + gl.uniform3fv(location, buffer); + } else { + gl.uniform3fv(location, value); + } + return buffer as TypedFloat32Array<3>; }; break; - case 'vec4': + } + case 'vec4': { + const buffer = new Float32Array(4); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<4>; - gl.uniform4fv(location, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<4> | number[]; + if (Array.isArray(value)) { + buffer[0] = value[0]; + buffer[1] = value[1]; + buffer[2] = value[2]; + buffer[3] = value[3]; + gl.uniform4fv(location, buffer); + } else { + gl.uniform4fv(location, value); + } + return buffer as TypedFloat32Array<4>; }; break; + } - case 'mat2': + case 'mat2': { + const buffer = new Float32Array(4); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<4>; - gl.uniformMatrix2fv(location, false, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<4> | number[]; + if (Array.isArray(value)) { + for (let i = 0; i < 4; i++) buffer[i] = value[i]; + gl.uniformMatrix2fv(location, false, buffer); + } else { + gl.uniformMatrix2fv(location, false, value); + } + return buffer as TypedFloat32Array<4>; }; break; - case 'mat3': + } + case 'mat3': { + const buffer = new Float32Array(9); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<9>; - gl.uniformMatrix3fv(location, false, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<9> | number[]; + if (Array.isArray(value)) { + for (let i = 0; i < 9; i++) buffer[i] = value[i]; + gl.uniformMatrix3fv(location, false, buffer); + } else { + gl.uniformMatrix3fv(location, false, value); + } + return buffer as TypedFloat32Array<9>; }; break; - case 'mat4': + } + case 'mat4': { + const buffer = new Float32Array(16); updateFunction = (time, width, height) => { - const value = updateFn(time, width, height) as TypedFloat32Array<16>; - gl.uniformMatrix4fv(location, false, value); - return value; + const value = updateFn(time, width, height) as TypedFloat32Array<16> | number[]; + if (Array.isArray(value)) { + for (let i = 0; i < 16; i++) buffer[i] = value[i]; + gl.uniformMatrix4fv(location, false, buffer); + } else { + gl.uniformMatrix4fv(location, false, value); + } + return buffer as TypedFloat32Array<16>; }; break; + } default: throw new Error(`Unsupported uniform type: ${type}`); diff --git a/src/react/components/engine/ShaderEngine.tsx b/src/react/components/engine/ShaderEngine.tsx index 3639934..84472c9 100644 --- a/src/react/components/engine/ShaderEngine.tsx +++ b/src/react/components/engine/ShaderEngine.tsx @@ -136,6 +136,13 @@ export const ShaderEngine = ({ }); activeProgram.current = pid; + const ups = uniformUpdaters[pid]; + if (ups) { + ups.forEach(u => { + manager.registerUniformUpdater(pid, u.name, u.type, u.updateFn); + }); + } + startTimeRef.current = performance.now(); animationFrameRef.current = requestAnimationFrame(renderLoopRef.current); window.addEventListener('resize', handleResize); @@ -145,7 +152,7 @@ export const ShaderEngine = ({ if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current); manager.destroyAll(); }; - }, [programConfigs, handleResize]); + }, [programConfigs, handleResize, uniformUpdaters]); useEffect(() => { const manager = managerRef.current; diff --git a/src/react/hooks/useUniformUpdaters.ts b/src/react/hooks/useUniformUpdaters.ts index 99989da..579ccd9 100644 --- a/src/react/hooks/useUniformUpdaters.ts +++ b/src/react/hooks/useUniformUpdaters.ts @@ -1,56 +1,25 @@ -import { useRef } from 'react'; +import { useMemo } from 'react'; import { createCommonUpdaters, createUniformUpdater } from '@/react/lib/createUniformUpdater'; import type { UniformParam, UniformUpdaterDef } from '@/types'; -function serializeUniforms(uniforms: Record): string { - const keys = Object.keys(uniforms).sort(); - return keys.map(k => { - const p = uniforms[k]; - const valStr = typeof p.value === 'function' ? p.value.toString() : JSON.stringify(p.value); - return `${k}:${p.type}:${valStr}`; - }).join('|'); -} - export const useUniformUpdaters = ( programId: string, uniforms: Record, options?: { skipDefaultUniforms?: boolean } ) => { - const cacheRef = useRef<{ - key: string; - result: Record; - } | null>(null); - - const skip = options?.skipDefaultUniforms ?? false; - const cacheKey = `${programId}|${skip}|${serializeUniforms(uniforms)}`; - - if (cacheRef.current && cacheRef.current.key === cacheKey) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (typeof window !== 'undefined' && (window as any).__micuglMetrics) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (window as any).__micuglMetrics.hookCacheHits++; - } - return cacheRef.current.result; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (typeof window !== 'undefined' && (window as any).__micuglMetrics) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - (window as any).__micuglMetrics.hookCacheMisses++; - } - - const updaters: UniformUpdaterDef[] = skip ? [] : createCommonUpdaters().filter(u => - (u.name === 'u_time' && !('u_time' in uniforms)) - || (u.name === 'u_resolution' && !('u_resolution' in uniforms)) - ); - - Object.entries(uniforms).forEach(([name, param]) => { - const uniformName = name.startsWith('u_') ? name : `u_${name}`; - updaters.push(createUniformUpdater(uniformName, param.type, param.value)); - }); - - const result = { [programId]: updaters }; - cacheRef.current = { key: cacheKey, result }; - return result; + return useMemo(() => { + const skip = options?.skipDefaultUniforms ?? false; + const updaters: UniformUpdaterDef[] = skip ? [] : createCommonUpdaters().filter(u => + (u.name === 'u_time' && !('u_time' in uniforms)) + || (u.name === 'u_resolution' && !('u_resolution' in uniforms)) + ); + + Object.entries(uniforms).forEach(([name, param]) => { + const uniformName = name.startsWith('u_') ? name : `u_${name}`; + updaters.push(createUniformUpdater(uniformName, param.type, param.value)); + }); + + return { [programId]: updaters }; + }, [programId, uniforms, options?.skipDefaultUniforms]); };