| /** |
| * @license |
| * Copyright 2014 The Emscripten Authors |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| // WebGLWorker client code |
| |
| function assert(x) { |
| if (!x) throw 'failed assert'; |
| } |
| |
| function WebGLClient() { |
| var objects = {}; |
| |
| var ctx = null; |
| var buffer = null; |
| var i = 0; |
| var skippable = false; |
| var currFrameBuffer = null; |
| |
| function func0(name) { |
| ctx[name](); |
| } |
| function func1(name) { |
| ctx[name](buffer[i]); |
| i++; |
| } |
| function func2(name) { |
| ctx[name](buffer[i], buffer[i+1]); |
| i += 2; |
| } |
| function func3(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2]); |
| i += 3; |
| } |
| function func4(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]); |
| i += 4; |
| } |
| function func5(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4]); |
| i += 5; |
| } |
| function func6(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5]); |
| i += 6; |
| } |
| function func7(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6]); |
| i += 7; |
| } |
| function func9(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], buffer[i+8]); |
| i += 9; |
| } |
| |
| // lookuppers, convert integer ids to cached objects for some args |
| function func1L0(name) { |
| ctx[name](objects[buffer[i]]); |
| i++; |
| } |
| function func2L0(name) { |
| ctx[name](objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function func2L0L1(name) { |
| ctx[name](objects[buffer[i]], objects[buffer[i+1]]); |
| i += 2; |
| } |
| function func2L1_(name) { |
| ctx[name](buffer[i], buffer[i+1] ? objects[buffer[i+1]] : null); |
| i += 2; |
| } |
| function func3L0(name) { |
| ctx[name](objects[buffer[i]], buffer[i+1], buffer[i+2]); |
| i += 3; |
| } |
| function func4L3_(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null); |
| i += 4; |
| } |
| function func5L3_(name) { |
| ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null, buffer[i+4]); |
| i += 5; |
| } |
| |
| // constructors, last argument is the id to save as |
| function funcC0(name) { |
| var object = ctx[name](); |
| var id = buffer[i++]; |
| objects[id] = object; |
| } |
| function funcC1(name) { |
| var object = ctx[name](buffer[i++]); |
| var id = buffer[i++]; |
| objects[id] = object; |
| } |
| function funcC2(name) { |
| var object = ctx[name](buffer[i++], buffer[i++]); |
| var id = buffer[i++]; |
| objects[id] = object; |
| } |
| function funcC2L0(name) { |
| var object = ctx[name](objects[buffer[i++]], buffer[i++]); |
| var id = buffer[i++]; |
| objects[id] = object; |
| } |
| |
| // destructors, stop holding on to the object in the cache |
| function funcD0(name) { |
| var id = buffer[i++]; |
| var object = objects[id]; |
| objects[id] = null; |
| ctx[name](object); |
| } |
| |
| // special cases/optimizations |
| function bindFramebuffer() { |
| currFrameBuffer = buffer[i+1] ? objects[buffer[i+1]] : null; |
| ctx.bindFramebuffer(buffer[i], currFrameBuffer); |
| i += 2; |
| } |
| function drawArrays(name) { |
| if (!skippable || currFrameBuffer !== null) { |
| ctx.drawArrays(buffer[i], buffer[i+1], buffer[i+2]); |
| } |
| i += 3; |
| } |
| function drawElements(name) { |
| if (!skippable || currFrameBuffer !== null) { |
| ctx.drawElements(buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]); |
| } |
| i += 4; |
| } |
| function enable() { |
| ctx.enable(buffer[i++]); |
| } |
| function disable() { |
| ctx.disable(buffer[i++]); |
| } |
| function enableVertexAttribArray() { |
| ctx.enableVertexAttribArray(buffer[i++]); |
| } |
| function disableVertexAttribArray() { |
| ctx.disableVertexAttribArray(buffer[i++]); |
| } |
| function activeTexture() { |
| ctx.activeTexture(buffer[i++]); |
| } |
| function uniform1i() { |
| ctx.uniform1i(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform1f() { |
| ctx.uniform1f(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform1fv() { |
| ctx.uniform1fv(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform2fv() { |
| ctx.uniform2fv(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform1iv() { |
| ctx.uniform1iv(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform3f() { |
| ctx.uniform3f(objects[buffer[i]], buffer[i+1], buffer[i+2], buffer[i+3]); |
| i += 4; |
| } |
| function uniform3fv() { |
| ctx.uniform3fv(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function uniform4fv() { |
| ctx.uniform4fv(objects[buffer[i]], buffer[i+1]); |
| i += 2; |
| } |
| function vertexAttribPointer() { |
| ctx.vertexAttribPointer(buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5]); |
| i += 6; |
| } |
| |
| var calls = { |
| 0: { name: 'NULL', func: func0 }, |
| 1: { name: 'getExtension', func: func1 }, |
| 2: { name: 'enable', func: enable }, |
| 3: { name: 'disable', func: disable }, |
| 4: { name: 'clear', func: func1 }, |
| 5: { name: 'clearColor', func: func4 }, |
| 6: { name: 'createShader', func: funcC1 }, |
| 7: { name: 'deleteShader', func: funcD0 }, |
| 8: { name: 'shaderSource', func: func2L0 }, |
| 9: { name: 'compileShader', func: func1L0 }, |
| 10: { name: 'createProgram', func: funcC0 }, |
| 11: { name: 'deleteProgram', func: funcD0 }, |
| 12: { name: 'attachShader', func: func2L0L1 }, |
| 13: { name: 'bindAttribLocation', func: func3L0 }, |
| 14: { name: 'linkProgram', func: func1L0 }, |
| 15: { name: 'getProgramParameter', func: () => assert(ctx.getProgramParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL') }, |
| 16: { name: 'getUniformLocation', func: funcC2L0 }, |
| 17: { name: 'useProgram', func: func1L0 }, |
| 18: { name: 'uniform1i', func: uniform1i }, |
| 19: { name: 'uniform1f', func: uniform1f }, |
| 20: { name: 'uniform3fv', func: uniform3fv }, |
| 21: { name: 'uniform4fv', func: uniform4fv }, |
| 22: { name: 'uniformMatrix4fv', func: func3L0 }, |
| 23: { name: 'vertexAttrib4fv', func: func2 }, |
| 24: { name: 'createBuffer', func: funcC0 }, |
| 25: { name: 'deleteBuffer', func: funcD0 }, |
| 26: { name: 'bindBuffer', func: func2L1_ }, |
| 27: { name: 'bufferData', func: func3 }, |
| 28: { name: 'bufferSubData', func: func3 }, |
| 29: { name: 'viewport', func: func4 }, |
| 30: { name: 'vertexAttribPointer', func: vertexAttribPointer }, |
| 31: { name: 'enableVertexAttribArray', func: enableVertexAttribArray }, |
| 32: { name: 'disableVertexAttribArray', func: disableVertexAttribArray }, |
| 33: { name: 'drawArrays', func: drawArrays }, |
| 34: { name: 'drawElements', func: drawElements }, |
| 35: { name: 'getError', func: () => assert(ctx.getError() === ctx.NO_ERROR, 'we cannot handle errors, we are async proxied WebGL') }, |
| 36: { name: 'createTexture', func: funcC0 }, |
| 37: { name: 'deleteTexture', func: funcD0 }, |
| 38: { name: 'bindTexture', func: func2L1_ }, |
| 39: { name: 'texParameteri', func: func3 }, |
| 40: { name: 'texImage2D', func: func9 }, |
| 41: { name: 'compressedTexImage2D', func: func7 }, |
| 42: { name: 'activeTexture', func: activeTexture }, |
| 43: { name: 'getShaderParameter', func: () => assert(ctx.getShaderParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL') }, |
| 44: { name: 'clearDepth', func: func1 }, |
| 45: { name: 'depthFunc', func: func1 }, |
| 46: { name: 'frontFace', func: func1 }, |
| 47: { name: 'cullFace', func: func1 }, |
| 48: { name: 'pixelStorei', func: func2 }, |
| 49: { name: 'depthMask', func: func1 }, |
| 50: { name: 'depthRange', func: func2 }, |
| 51: { name: 'blendFunc', func: func2 }, |
| 52: { name: 'scissor', func: func4 }, |
| 53: { name: 'colorMask', func: func4 }, |
| 54: { name: 'lineWidth', func: func1 }, |
| 55: { name: 'createFramebuffer', func: funcC0 }, |
| 56: { name: 'deleteFramebuffer', func: funcD0 }, |
| 57: { name: 'bindFramebuffer', func: bindFramebuffer }, |
| 58: { name: 'framebufferTexture2D', func: func5L3_ }, |
| 59: { name: 'createRenderbuffer', func: funcC0 }, |
| 60: { name: 'deleteRenderbuffer', func: funcD0 }, |
| 61: { name: 'bindRenderbuffer', func: func2L1_ }, |
| 62: { name: 'renderbufferStorage', func: func4 }, |
| 63: { name: 'framebufferRenderbuffer', func: func4L3_ }, |
| 64: { name: 'debugPrint', func: func1 }, |
| 65: { name: 'hint', func: func2 }, |
| 66: { name: 'blendEquation', func: func1 }, |
| 67: { name: 'generateMipmap', func: func1 }, |
| 68: { name: 'uniformMatrix3fv', func: func3L0 }, |
| 69: { name: 'stencilMask', func: func1 }, |
| 70: { name: 'clearStencil', func: func1 }, |
| 71: { name: 'texSubImage2D', func: func9 }, |
| 72: { name: 'uniform3f', func: uniform3f }, |
| 73: { name: 'blendFuncSeparate', func: func4 }, |
| 74: { name: 'uniform2fv', func: uniform2fv }, |
| 75: { name: 'texParameterf', func: func3 }, |
| 76: { name: 'isContextLost', func: () => assert(!ctx.isContextLost(), 'context lost which we cannot handle, we are async proxied WebGL') }, |
| 77: { name: 'blendEquationSeparate', func: func2 }, |
| 78: { name: 'stencilFuncSeparate', func: func4 }, |
| 79: { name: 'stencilOpSeparate', func: func4 }, |
| 80: { name: 'drawBuffersWEBGL', func: func1 }, |
| 81: { name: 'uniform1iv', func: uniform1iv }, |
| 82: { name: 'uniform1fv', func: uniform1fv }, |
| }; |
| |
| function renderCommands(buf) { |
| ctx = Module['ctx']; |
| i = 0; |
| buffer = buf; |
| var len = buffer.length; |
| //dump('issuing commands, buffer len: ' + len + '\n'); |
| while (i < len) { |
| var info = calls[buffer[i++]]; |
| var name = info.name; |
| info.func(name); |
| //var err; |
| //while ((err = ctx.getError()) !== ctx.NO_ERROR) { |
| // dump('warning: GL error ' + err + ', after ' + [command, numArgs] + '\n'); |
| //} |
| assert(i <= len); |
| } |
| } |
| |
| var commandBuffers = []; |
| |
| function renderAllCommands() { |
| // we can skip parts of the frames before the last, as we just need their side effects |
| skippable = true; |
| for (var i = 0; i < commandBuffers.length-1; i++) { |
| renderCommands(commandBuffers[i]); |
| } |
| skippable = false; |
| renderCommands(commandBuffers[commandBuffers.length-1]); |
| commandBuffers.length = 0; |
| } |
| |
| this.onmessage = (msg) => { |
| //dump('client GL got ' + JSON.stringify(msg) + '\n'); |
| switch (msg.op) { |
| case 'render': { |
| if (commandBuffers.length === 0) { |
| // requestion a new frame, we will clear the buffers after rendering them |
| window.requestAnimationFrame(renderAllCommands); |
| } |
| commandBuffers.push(msg.commandBuffer); |
| break; |
| } |
| default: throw 'weird gl onmessage ' + JSON.stringify(msg); |
| } |
| }; |
| } |
| |
| WebGLClient.prefetch = () => { |
| // Create a fake temporary GL context |
| var canvas = document.createElement('canvas'); |
| var ctx = canvas.getContext('webgl-experimental') || canvas.getContext('webgl'); |
| if (!ctx) { |
| // If we have no webGL support, we still notify that prefetching is done, as the app blocks on that |
| worker.postMessage({ target: 'gl', op: 'setPrefetched', preMain: true }); |
| return; |
| } |
| |
| // Fetch the parameters and proxy them |
| var parameters = {}; |
| ['MAX_VERTEX_ATTRIBS', 'MAX_TEXTURE_IMAGE_UNITS', 'MAX_TEXTURE_SIZE', 'MAX_CUBE_MAP_TEXTURE_SIZE', 'MAX_VERTEX_UNIFORM_VECTORS', 'MAX_FRAGMENT_UNIFORM_VECTORS', |
| 'MAX_VARYING_VECTORS', 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', 'MAX_VERTEX_TEXTURE_IMAGE_UNITS', 'VENDOR', 'RENDERER', 'VERSION', 'SHADING_LANGUAGE_VERSION', |
| 'COMPRESSED_TEXTURE_FORMATS', 'RED_BITS', 'GREEN_BITS', 'BLUE_BITS', 'ALPHA_BITS', 'DEPTH_BITS', 'STENCIL_BITS', 'MAX_RENDERBUFFER_SIZE'].forEach(function(name) { |
| var id = ctx[name]; |
| parameters[id] = ctx.getParameter(id); |
| }); |
| // Try to enable some extensions, so we can access their parameters |
| [{ extName: 'EXT_texture_filter_anisotropic', paramName: 'MAX_TEXTURE_MAX_ANISOTROPY_EXT' }, |
| { extName: 'WEBGL_draw_buffers', paramName: 'MAX_COLOR_ATTACHMENTS_WEBGL' }].forEach(function(pair) { |
| var ext = ctx.getExtension(pair.extName); |
| if (ext) { |
| var id = ext[pair.paramName]; |
| parameters[id] = ctx.getParameter(id); |
| } |
| }); |
| // Fetch shader precisions |
| var precisions = {}; |
| ['FRAGMENT_SHADER', 'VERTEX_SHADER'].forEach(function(shaderType) { |
| shaderType = ctx[shaderType]; |
| precisions[shaderType] = {}; |
| ['LOW_FLOAT', 'MEDIUM_FLOAT', 'HIGH_FLOAT', 'LOW_INT', 'MEDIUM_INT', 'HIGH_INT'].forEach(function(precisionType) { |
| precisionType = ctx[precisionType]; |
| var info = ctx.getShaderPrecisionFormat(shaderType, precisionType); |
| precisions[shaderType][precisionType] = info ? { rangeMin: info.rangeMin, rangeMax: info.rangeMax, precision: info.precision } : info; |
| }); |
| }); |
| |
| worker.postMessage({ target: 'gl', op: 'setPrefetched', parameters, extensions: ctx.getSupportedExtensions(), precisions, preMain: true }); |
| }; |
| |