// Use glsl-canvas to make managing webgl stuff easier. import importUMD from './import-umd.js'; import { UpdateLimiter } from './update-limiter.js'; const { Canvas } = await importUMD('./glsl-canvas.min.js'); export class GlCanvas extends Canvas { constructor(canvas_el, options) { super(new Proxy(canvas_el, { // because it's not visible, client dimensions are zero so we // need to substitute actual dimensions instead get: (target, name, receiver) => { if (name === 'getBoundingClientRect') { return () => new DOMRect(0, 0, target.width, target.height); } if (name === 'clientWidth') { return Math.ceil(target.width / this.devicePixelRatio); } if (name === 'clientHeight') { return Math.ceil(target.height / this.devicePixelRatio); } const r = target[name]; return typeof r === 'function' ? r.bind(target) : r; }, set: (target, name, value) => { if ((name !== 'width') && (name !== 'height')) { target[name] = value; } return true; } }), options); this.update_limiter = new UpdateLimiter(); this.destroyed = false; } // Allow rendering loop to be driven externally (e.g. by the audio encoder) // to avoid requestAnimationFrame (or indeed setInterval) throttling. onLoop() { if (this.update_limiter.check() && !this.destroyed) { const now = Date.now(); this.checkRender(); // Make sure we don't hog the main thread. Software rendering will take // a lot of time (100-200ms). Alternatively, We could use createImageBitmap, // post the ImageBitMap to a worker and use texImage2D (via glsl-canvas) // to write it to an OffScreenCanvas (use canvas_el.transferControlToOffscreen). // However, it'd still be using software rendering and the fps would be tiny. // Better to "support" only hardware rendering (or very fast CPUs!), where // time to render each frame is only 1ms max. this.update_limiter.threshold = (Date.now() - now) * 2; return true; } return false; } // Prevent errors after destruction destroy() { this.destroyed = true; if (this.gl) { super.destroy(); } this.uniforms = { createTexture() { return {}; }, create() {}, update() {} }; this.textures = { createOrUpdate() { return { then() {} }; }, values: {} }; this.buffers = { values: {} }; } }