Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 72 additions & 24 deletions src/core/managers/WebGLManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand Down
9 changes: 8 additions & 1 deletion src/react/components/engine/ShaderEngine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
61 changes: 15 additions & 46 deletions src/react/hooks/useUniformUpdaters.ts
Original file line number Diff line number Diff line change
@@ -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, UniformParam>): 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<string, UniformParam>,
options?: { skipDefaultUniforms?: boolean }
) => {
const cacheRef = useRef<{
key: string;
result: Record<string, UniformUpdaterDef[]>;
} | 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]);
};
Loading