const canvas = document.querySelector('canvas') // hidpi const width = canvas.width, height = canvas.height canvas.style.width = width + 'px' canvas.style.height = height + 'px' canvas.width = width * window.devicePixelRatio canvas.height = height * window.devicePixelRatio const gl = canvas.getContext('webgl') const shaderMap = new Map() var currentShaderId = 0 function createShaderId() { const id = currentShaderId currentShaderId += 1 return id } const programMap = new Map() var currentProgramId = 0 function createProgramId() { const id = currentProgramId currentProgramId += 1 return id } const bufferMap = new Map() var currentBufferId = 0 function createBufferId() { const id = currentBufferId currentBufferId += 1 return id } const uniformLocMap = new Map() var currentUniformLocId = 0 function createUniformLocId() { const id = currentUniformLocId currentUniformLocId += 1 return id } var memory var decoder = new TextDecoder('utf-8') function getUint32(addr) { const view = new DataView(memory.buffer) return view.getUint32(addr, true) } function setUint32(addr, val) { const view = new DataView(memory.buffer) view.setUint32(addr, val, true) } function getString(addr) { const view = new DataView(memory.buffer) var len = 0 while (view.getUint8(addr + len) != 0) len++ return decoder.decode(memory.buffer.slice(addr, addr + len)) } const imports = { env: { glCreateShader: shaderType => { const id = createShaderId() const shader = gl.createShader(shaderType) shaderMap.set(id, shader) return id }, glShaderSource: (id, numSrcs, srcs, lens) => { const shader = shaderMap.get(id) var totalSource = '' for (let i = 0; i < numSrcs; i++) { const addr = srcs + i * 4 const src = getUint32(addr) const len = getUint32(lens + i * 4) totalSource += decoder.decode(memory.buffer.slice(src, src + len)) } gl.shaderSource(shader, totalSource) }, glCompileShader: id => gl.compileShader(shaderMap.get(id)), glCreateProgram: () => { const id = createProgramId() const prog = gl.createProgram() programMap.set(id, prog) return id }, glAttachShader: (progId, shaderId) => { gl.attachShader(programMap.get(progId), shaderMap.get(shaderId)) }, glLinkProgram: (progId) => gl.linkProgram(programMap.get(progId)), glUseProgram: (progId) => gl.useProgram(programMap.get(progId)), glGetAttribLocation: (progId, name) => gl.getAttribLocation(programMap.get(progId), getString(name)), glEnableVertexAttribArray: (loc) => gl.enableVertexAttribArray(loc), glVertexAttribPointer: (loc, size, type, normalized, stride, offset) => { gl.vertexAttribPointer(loc, size, type, normalized, stride, offset) }, glGenBuffers: (num, addr) => { for (let i = 0; i < num; i++) { const id = createBufferId() const buffer = gl.createBuffer() bufferMap.set(id, buffer) setUint32(addr + i * 4, id) } }, glBindBuffer: (target, bufferId) => gl.bindBuffer(target, bufferMap.get(bufferId)), glBufferData: (target, size, addr, usage) => { const data = new Float32Array(memory.buffer.slice(addr, addr + size * 4)) gl.bufferData(target, data, usage) }, glGetUniformLocation: (progId, name) => { const id = createUniformLocId() const loc = gl.getUniformLocation(programMap.get(progId), getString(name)) uniformLocMap.set(id, loc) return id }, glUniformMatrix4fv: (loc, count, transpose, addr) => { if (count != 1) throw Error('count must be 1') const vals = new Float32Array(memory.buffer.slice(addr, addr + 4 * 16)) gl.uniformMatrix4fv(uniformLocMap.get(loc), transpose, vals) } } } const sock = new WebSocket('ws://localhost:8889') sock.onmessage = function (e) { console.log(e.data) } var instance function draw(tick) { instance.exports.update(tick) gl.drawArrays(gl.TRIANGLES, 0, 3) requestAnimationFrame(draw); } function instantiate(bytes) { return WebAssembly.compile(bytes). then(m => { const inst = new WebAssembly.Instance(m, imports) memory = inst.exports.memory return inst }) } fetch('test.wasm') .then(response => response.arrayBuffer()) .then(bytes => instantiate(bytes)) .then(i => { instance = i instance.exports.setup() requestAnimationFrame(draw); })