diff --git a/modules/v4d/third/virtual-webgl2.js b/modules/v4d/third/virtual-webgl2.js deleted file mode 100644 index 274f2590b..000000000 --- a/modules/v4d/third/virtual-webgl2.js +++ /dev/null @@ -1,1405 +0,0 @@ -/* - * Copyright 2018, Gregg Tavares. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * * Neither the name of Gregg Tavares. nor the names of his - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* eslint-env browser */ - -(function() { - const settings = { - disableWebGL1: false, - compositorCreator() { - }, - }; - const canvasToVirtualContextMap = new Map(); - const extensionInfo = {}; - const extensionSaveRestoreHelpersArray = []; - const extensionSaveRestoreHelpers = {}; - - let currentVirtualContext = null; - let someContextsNeedRendering; - - const sharedWebGLContext = document.createElement('canvas').getContext('webgl2'); - const numAttribs = sharedWebGLContext.getParameter(sharedWebGLContext.MAX_VERTEX_ATTRIBS); - const numTextureUnits = sharedWebGLContext.getParameter(sharedWebGLContext.MAX_COMBINED_TEXTURE_IMAGE_UNITS); - const numUniformBufferBindings = sharedWebGLContext.getParameter(sharedWebGLContext.MAX_UNIFORM_BUFFER_BINDINGS); - const baseState = makeDefaultState(sharedWebGLContext, 300, 150); - - const INT = 0x1404 - const UNSIGNED_INT = 0x1405; - const FLOAT = 0x1406; - - const vs = ` - attribute vec4 position; - varying vec2 v_texcoord; - void main() { - gl_Position = position; - v_texcoord = position.xy * .5 + .5; - } - `; - - const fs = ` - precision mediump float; - varying vec2 v_texcoord; - uniform sampler2D u_tex; - void main() { - gl_FragColor = texture2D(u_tex, v_texcoord); - } - `; - - const fs2 = ` - precision mediump float; - varying vec2 v_texcoord; - uniform sampler2D u_tex; - void main() { - gl_FragColor = texture2D(u_tex, v_texcoord); - gl_FragColor.rgb *= gl_FragColor.a; - } - `; - - const premultiplyAlphaTrueProgram = createProgram(sharedWebGLContext, [vs, fs]); - const premultiplyAlphaFalseProgram = createProgram(sharedWebGLContext, [vs, fs2]); - - { - const gl = sharedWebGLContext; - const positionLoc = 0; // hard coded in createProgram - - const buffer = gl.createBuffer(); - gl.bindBuffer(gl.ARRAY_BUFFER, buffer); - gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ - -1, -1, - 1, -1, - -1, 1, - -1, 1, - 1, -1, - 1, 1, - ]), gl.STATIC_DRAW); - - gl.enableVertexAttribArray(positionLoc); - gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); - - } - - saveAllState(baseState); - - HTMLCanvasElement.prototype.getContext = (function(origFn) { - return function(type, contextAttributes) { - if (type === 'webgl' || type === 'experimental-webgl') { - return createOrGetVirtualWebGLContext(this, type, contextAttributes); - } else if (type === 'webgl2') { - return createOrGetVirtualWebGLContext(this, type, contextAttributes); - } - return origFn.call(this, type, contextAttributes); - }; - - }(HTMLCanvasElement.prototype.getContext)); - - function valueOrDefault(value, defaultValue) { - return value === undefined ? defaultValue : value; - } - - function errorDisposedContext(fnName) { - return function() { - throw new Error(`tried to call ${fnName} on disposed context`); - }; - } - - class DefaultCompositor { - constructor(canvas) { - this._ctx = canvas.getContext('2d'); - } - composite(gl, texture, canvas, contextAttributes) { - // note: not entirely sure what to do here. We need this canvas to be at least as large - // as the canvas we're drawing to. Resizing a canvas is slow so I think just making - // sure we never get smaller than the largest canvas. At the moment though I'm too lazy - // to make it smaller. - const ctx = this._ctx; - const width = canvas.width; - const height = canvas.height; - const maxWidth = Math.max(gl.canvas.width, width); - const maxHeight = Math.max(gl.canvas.height, height); - if (gl.canvas.width !== maxWidth || gl.canvas.height !== maxHeight) { - gl.canvas.width = maxWidth; - gl.canvas.height = maxHeight; - } - - gl.viewport(0, 0, width, height); - - gl.useProgram(contextAttributes.premultipliedAlpha ? premultiplyAlphaTrueProgram : premultiplyAlphaFalseProgram); - - // draw the drawingbuffer's texture to the offscreen canvas - gl.bindTexture(gl.TEXTURE_2D, texture); - gl.drawArrays(gl.TRIANGLES, 0, 6); - - // copy it to target canvas - ctx.globalCompositeOperation = 'copy'; - ctx.drawImage( - gl.canvas, - 0, maxHeight - height, width, height, // src rect - 0, 0, width, height); // dest rect - } - dispose() { - } - } - - function virtualGLConstruct(canvas, contextAttributes = {}, compositor, disposeHelper) { - const gl = sharedWebGLContext; - this._canvas = canvas; - // Should use Symbols or something to hide these variables from the outside. - - this._compositor = compositor; - this._disposeHelper = disposeHelper; - this._extensions = {}; - // based on context attributes and canvas.width, canvas.height - // create a texture and framebuffer - this._drawingbufferTexture = gl.createTexture(); - this._drawingbufferFramebuffer = gl.createFramebuffer(); - this._contextAttributes = { - alpha: valueOrDefault(contextAttributes.alpha, true), - antialias: false, - depth: valueOrDefault(contextAttributes.depth, true), - failIfMajorPerformanceCaveat: false, - premultipliedAlpha: valueOrDefault(contextAttributes.premultipliedAlpha, true), - stencil: valueOrDefault(contextAttributes.stencil, false), - }; - this._preserveDrawingbuffer = valueOrDefault(contextAttributes.preserveDrawingBuffer, false); - - const oldTexture = gl.getParameter(gl.TEXTURE_BINDING_2D); - const oldFramebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING); - - gl.bindTexture(gl.TEXTURE_2D, this._drawingbufferTexture); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - // this._drawingbufferTexture.id = canvas.id; - // this._drawingbufferFramebuffer.id = canvas.id; - - gl.bindFramebuffer(gl.FRAMEBUFFER, this._drawingbufferFramebuffer); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._drawingbufferTexture, 0); - - if (this._contextAttributes.depth) { - const oldRenderbuffer = gl.getParameter(gl.RENDERBUFFER_BINDING); - this._depthRenderbuffer = gl.createRenderbuffer(); - gl.bindRenderbuffer(gl.RENDERBUFFER, this._depthRenderbuffer); - const attachmentPoint = this._contextAttributes.stencil ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT; - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachmentPoint, gl.RENDERBUFFER, this._depthRenderbuffer); - gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbuffer); - } - - gl.bindTexture(gl.TEXTURE_2D, oldTexture); - gl.bindFramebuffer(gl.FRAMEBUFFER, oldFramebuffer); - - // remember all WebGL state (default bindings, default texture units, - // default attributes and/or vertex shade object, default program, - // default blend, stencil, zBuffer, culling, viewport etc... state - this._state = makeDefaultState(gl, canvas.width, canvas.height); - this._state.readFramebuffer = this._drawingbufferFramebuffer; - this._state.drawFramebuffer = this._drawingbufferFramebuffer; - this._state.readBuffer = gl.COLOR_ATTACHMENT0; - - this._state.vertexArray = gl.createVertexArray(); - this._defaultVertexArray = this._state.vertexArray; - } - - function virtualGLDispose() { - this._disposeHelper(); - const gl = sharedWebGLContext; - gl.deleteFramebuffer(this._drawingbufferFramebuffer); - gl.deleteTexture(this._drawingbufferTexture); - if (this._depthRenderbuffer) { - gl.deleteRenderbuffer(this._depthRenderbuffer); - } - if (this._compositor.dispose) { - this._compositor.dispose(); - } - for (const [key, value] of Object.entries(this)) { - if (typeof value === 'function') { - this[key] = errorDisposedContext(key); - } - } - for (const [key, value] of Object.entries(this.prototype)) { - if (typeof value === 'function') { - this[key] = errorDisposedContext(key); - } - } - } - - function virtualGLComposite(gl) { - this._compositor.composite(gl, this._drawingbufferTexture, this.canvas, this._contextAttributes); - if (!this._preserveDrawingbuffer) { - this._needClear = true; - } - } - - // Base exists so VirtualWebGLContext has a base class we can replace - // because otherwise it's base is Object which we can't replace. - class Base {} - class VirtualWebGLContext extends Base { - constructor(canvas, contextAttributes = {}, compositor, disposeHelper) { - super(); - this.dispose = virtualGLDispose; - this.composite = virtualGLComposite; - virtualGLConstruct.call(this, canvas, contextAttributes, compositor, disposeHelper); - } - get canvas() { - return this._canvas; - } - get drawingBufferWidth() { - return this.canvas.width; - } - get drawingBufferHeight() { - return this.canvas.height; - } - } - class Base2 {} - class VirtualWebGL2Context extends Base2 { - constructor(canvas, contextAttributes = {}, compositor, disposeHelper) { - super(); - this.dispose = virtualGLDispose; - this.composite = virtualGLComposite; - virtualGLConstruct.call(this, canvas, contextAttributes, compositor, disposeHelper); - } - get canvas() { - return this._canvas; - } - get drawingBufferWidth() { - return this.canvas.width; - } - get drawingBufferHeight() { - return this.canvas.height; - } - } - - // Replace the prototype with WebGL2RenderingContext so that someCtx instanceof WebGL2RenderingContext returns true - Object.setPrototypeOf(Object.getPrototypeOf(VirtualWebGLContext.prototype), WebGLRenderingContext.prototype); - Object.setPrototypeOf(Object.getPrototypeOf(VirtualWebGL2Context.prototype), WebGL2RenderingContext.prototype); - - function makeDefaultState(gl, width, height) { - const vao = gl.createVertexArray(); - gl.bindVertexArray(vao); - - const state = { - arrayBuffer: null, - renderbuffer: null, - drawFramebuffer: null, - readFramebuffer: null, - copyReadBuffer: null, - copyWriteBuffer: null, - pixelPackBuffer: null, - pixelUnpackBuffer: null, - transformFeedbackBuffer: null, - uniformBuffer: null, - - readBuffer: gl.BACK, - - enable: new Map([ - [ gl.BLEND, false ], - [ gl.CULL_FACE, false ], - [ gl.DEPTH_TEST, false ], - [ gl.DITHER, false ], - [ gl.POLYGON_OFFSET_FILL, false ], - [ gl.RASTERIZER_DISCARD, false ], - [ gl.SAMPLE_ALPHA_TO_COVERAGE, false ], - [ gl.SAMPLE_COVERAGE, false ], - [ gl.SCISSOR_TEST, false ], - [ gl.STENCIL_TEST, false ], - ]), - - // This is a place the spec gets wrong! This data should have been part of a VertexArray - attribValues: new Array(numAttribs).fill(0).map(() => { - return { - type: gl.FLOAT, - value: [0, 0, 0, 1], - }; - }), - - vertexArray: vao, - activeTexture: gl.TEXTURE0, - transformFeedback: null, - - pack: new Map([ - [ gl.PACK_ALIGNMENT, 4], - [ gl.UNPACK_ALIGNMENT, 4], - [ gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.BROWSER_DEFAULT_WEBGL], - [ gl.UNPACK_FLIP_Y_WEBGL, 0], - [ gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0], - [ gl.UNPACK_ROW_LENGTH , 0], - [ gl.UNPACK_SKIP_ROWS , 0], - [ gl.UNPACK_SKIP_PIXELS , 0], - [ gl.UNPACK_SKIP_IMAGES , 0], - [ gl.UNPACK_IMAGE_HEIGHT , 0], - [ gl.PACK_ROW_LENGTH , 0], - [ gl.PACK_SKIP_ROWS , 0], - [ gl.PACK_SKIP_PIXELS , 0], - ]), - - currentProgram: null, - viewport: [0, 0, width, height], - scissor: [0, 0, width, height], - blendSrcRgb: gl.ONE, - blendDstRgb: gl.ZERO, - blendSrcAlpha: gl.ONE, - blendDstAlpha: gl.ZERO, - blendEquationRgb: gl.FUNC_ADD, - blendEquationAlpha: gl.FUNC_ADD, - blendColor: [0, 0, 0, 0], - clearColor: [0, 0, 0, 0], - colorMask: [true, true, true, true], - cullFaceMode: gl.BACK, - clearDepth: 1, - depthFunc: gl.LESS, - depthRange: [0, 1], - depthMask: true, - frontFace: gl.CCW, - generateMipmapHint: gl.DONT_CARE, - lineWidth: 1, - polygonOffsetFactor: 0, - polygonOffsetUnits: 0, - sampleCoverageValue: 1, - sampleCoverageUnits: false, - stencilFront: { - fail: gl.KEEP, - func: gl.ALWAYS, - depthFail: gl.KEEP, - depthPass: gl.KEEP, - ref: 0, - valueMask: 0xFFFFFFFF, - writeMask: 0xFFFFFFFF, - }, - stencilBack: { - fail: gl.KEEP, - func: gl.ALWAYS, - depthFail: gl.KEEP, - depthPass: gl.KEEP, - ref: 0, - valueMask: 0xFFFFFFFF, - writeMask: 0xFFFFFFFF, - }, - stencilClearValue: 0, - - textureUnits: new Array(numTextureUnits).fill(0).map(() => { - return { - texture2D: null, - textureCubemap: null, - texture2DArray: null, - texture3D: null, - sampler: null, - }; - }), - uniformBufferBindings: new Array(numUniformBufferBindings).fill(0).map(() => { - return { - buffer: null, - size: 0, - start: 0, - }; - }), - }; - - return state; - } - - function isFramebufferBindingNull(vCtx) { - return vCtx._state.drawFramebuffer === vCtx._drawingbufferFramebuffer; - } - - function createWrapper(origFn/*, name*/) { - // lots of optimization could happen here depending on specific functions - return function(...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - return origFn.call(sharedWebGLContext, ...args); - }; - } - - function clearIfNeeded(vCtx) { - if (vCtx._needClear) { - vCtx._needClear = false; - const gl = sharedWebGLContext; - gl.bindFramebuffer(gl.FRAMEBUFFER, vCtx._drawingbufferFramebuffer); - gl.disable(gl.SCISSOR_TEST); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - enableDisable(gl, gl.SCISSOR_TEST, vCtx._state.scissorTest); - gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, vCtx._state.drawFramebuffer); - } - } - - function beforeDraw(vCtx) { - makeCurrentContext(vCtx); - resizeCanvasIfChanged(vCtx); - clearIfNeeded(vCtx); - } - - function afterDraw(vCtx) { - if (isFramebufferBindingNull(vCtx)) { - vCtx._needComposite = true; - if (!someContextsNeedRendering) { - someContextsNeedRendering = true; - setTimeout(renderAllDirtyVirtualCanvases, 0); - } - } - } - - function createDrawWrapper(origFn) { - return function(...args) { - // a rendering function was called so we need to copy are drawingBuffer - // to the canvas for this context after the current event. - beforeDraw(this); - const result = origFn.call(sharedWebGLContext, ...args); - afterDraw(this); - return result; - }; - } - - function createStateArgsSaverFn(fnName) { - return function(...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl[fnName](...args); - const v = this._state[fnName]; - for (let i = 0; i < args.length; ++i) { - v[i] = args[i]; - } - }; - } - - function createSaveStateNamedArgs(fnName, argsToStateProps) { - return function(...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl[fnName](...args); - for (let i = 0; i < argsToStateProps.length; ++i) { - this._state[argsToStateProps[i]] = args[i]; - } - }; - } - - function createVertexAttribWrapper(origFn, fn) { - return function(loc, ...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - origFn.call(gl, loc, ...args); - const [type, value] = fn(args); - this._state.attribValues[loc] = {type, value}; - }; - } - - function saveStencilMaskImpl(st, mask) { - st.writeMask = mask; - } - - function saveStencilMask(state, face, mask) { - if (face === sharedWebGLContext.FRONT || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilMaskImpl(state.stencilFront, mask); - } - if (face === sharedWebGLContext.BACK || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilMaskImpl(state.stencilBack, mask); - } - } - - function saveStencilFuncImpl(st, func, ref, mask) { - st.func = func; - st.ref = ref; - st.valueMask = mask; - } - - function saveStencilFunc(state, face, func, ref, mask) { - if (face === sharedWebGLContext.FRONT || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilFuncImpl(state.stencilFront, func, ref, mask); - } - if (face === sharedWebGLContext.BACK || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilFuncImpl(state.stencilBack, func, ref, mask); - } - } - - function saveStencilOpImpl(st, fail, zfail, zpass) { - st.fail = fail; - st.depthFail = zfail; - st.depthPass = zpass; - } - - function saveStencilOp(state, face, fail, zfail, zpass) { - if (face === sharedWebGLContext.FRONT || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilOpImpl(state.stencilFront, fail, zfail, zpass); - } - if (face === sharedWebGLContext.BACK || face === sharedWebGLContext.FRONT_AND_BACK) { - saveStencilOpImpl(state.stencilBack, fail, zfail, zpass); - } - } - - const virtualFns = { - getExtension(name) { - // just like the real context each extension needs a virtual class because each use - // of the extension might be modified (as in people adding properties to it) - const existingExt = this._extensions[name]; - if (existingExt) { - return existingExt; - } - - const ext = sharedWebGLContext.getExtension(name); - if (!ext) { - return null; - } - const wrapperInfo = extensionInfo[name] || {}; - const wrapperFnMakerFn = wrapperInfo.wrapperFnMakerFn || (() => { - console.log('trying to get extension:', name); - }); - const saveRestoreHelper = extensionSaveRestoreHelpers[name]; - if (!saveRestoreHelper) { - const saveRestoreMakerFn = wrapperInfo.saveRestoreMakerFn; - if (saveRestoreMakerFn) { - const saveRestore = saveRestoreMakerFn(ext); - extensionSaveRestoreHelpers[name] = saveRestore; - extensionSaveRestoreHelpersArray.push(saveRestore); - } - } - - const wrapper = { - _context: this, - }; - for (const key in ext) { - let value = ext[key]; - if (typeof value === 'function') { - value = wrapperFnMakerFn(ext, value, name); - } - wrapper[key] = value; - } - - return wrapper; - }, - activeTexture(unit) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.activeTexture(unit); - this._state.activeTexture = unit; - }, - enable(pname) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.enable(pname); - this._state.enable.set(pname, true); - }, - disable(pname) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.disable(pname); - this._state.enable.set(pname, false); - }, - viewport: createStateArgsSaverFn('viewport'), - scissor: createStateArgsSaverFn('scissor'), - blendColor: createStateArgsSaverFn('blendColor'), - clearColor: createStateArgsSaverFn('clearColor'), - colorMask: createStateArgsSaverFn('colorMask'), - depthRange: createStateArgsSaverFn('depthRange'), - bindBuffer(target, buffer) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindBuffer(target, buffer); - switch (gl.target) { - case gl.ARRAY_BUFFER: - this._state.arrayBuffer = buffer; - break; - case gl.COPY_READ_BUFFER: - this._state.copyReadBuffer = buffer; - break; - case gl.COPY_WRITE_BUFFER: - this._state.copyWriteBuffer = buffer; - break; - case gl.PIXEL_PACK_BUFFER: - this._state.pixelPackBuffer = buffer; - break; - case gl.PIXEL_UNPACK_BUFFER: - this._state.pixelUnpackBuffer = buffer; - break; - case gl.TRANSFORM_FEEDBACK_BUFFER: - this._state.transformFeedbackBuffer = buffer; - break; - case gl.UNIFORM_BUFFER: - this._state.uniformBuffer = buffer; - } - }, - bindBufferBase(target, index, buffer) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindBufferBase(target, index, buffer); - switch (target) { - case gl.UNIFORM_BUFFER: { - const ub = this._state.uniformBufferBindings[index]; - ub.buffer = buffer; - ub.size = 0; - ub.start = 0; - break; - } - default: - break; - } - }, - bindBufferRange(target, index, buffer, offset, size) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindBufferRange(target, index, buffer, offset, size); - switch (target) { - case gl.UNIFORM_BUFFER: { - const ub = this._state.uniformBufferBindings[index]; - ub.buffer = buffer; - ub.size = size; - ub.start = offset; - break; - } - default: - break; - } - }, - bindTexture(target, texture) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindTexture(target, texture); - const unit = this._state.textureUnits[this._state.activeTexture - gl.TEXTURE0]; - switch (target) { - case gl.TEXTURE_2D: - unit.texture2D = texture; - break; - case gl.TEXTURE_CUBE_MAP: - unit.textureCubemap = texture; - break; - case gl.TEXTURE_2D_ARRAY: - unit.texture2DArray = texture; - break; - case gl.TEXTURE_3D: - unit.texture3D = texture; - break; - } - }, - bindRenderbuffer(target, renderbuffer) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindRenderbuffer(target, renderbuffer); - this._state.renderbuffer = renderbuffer; - }, - bindSampler(unit, sampler) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindSampler(unit, sampler); - this._state.textureUnits[unit].sampler = sampler; - }, - bindVertexArray(va) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - if (va === null) { - va = this._defaultVertexArray; - } - gl.bindVertexArray(va); - this._state.vertexArray = va; - }, - getContextAttributes() { - return this._contextAttributes; - }, - readPixels(...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - clearIfNeeded(this); - const gl = sharedWebGLContext; - return gl.readPixels(...args); - }, - getParameter(pname) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - const value = gl.getParameter(pname); - switch (pname) { - case gl.FRAMEBUFFER_BINDING: - if (value === this._drawingbufferFramebuffer) { - return null; - } - break; - case gl.DRAW_BUFFER0: - if (isFramebufferBindingNull(this)) { - if (value === gl.COLOR_ATTACHMENT0) { - return gl.BACK; - } - } - break; - case gl.READ_BUFFER: - if (isFramebufferBindingNull(this)) { - if (value === gl.COLOR_ATTACHMENT0) { - return gl.BACK; - } - } - break; - case gl.VERTEX_ARRAY_BINDING: - if (value === this._defaultVertexArray) { - return null; - } - break; - } - return value; - }, - blendFunc(sfactor, dfactor) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.blendFunc(sfactor, dfactor); - this._state.blendSrcRgb = sfactor; - this._state.blendSrcAlpha = sfactor; - this._state.blendDstRgb = dfactor; - this._state.blendDstAlpha = dfactor; - }, - blendEquation(mode) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.blendEquation(mode); - this._state.blendEquationRgb = mode; - this._state.blendEquationAlpha = mode; - }, - blendFuncSeparate: createSaveStateNamedArgs('blendFuncSeparate', ['blendSrcRgb', 'blendDstRgb', 'blendSrcAlpha', 'blendDstAlpha']), - blendEquationSeparate: createSaveStateNamedArgs('blendEquationSeparate', ['blendEquationRgb', 'blendEquationAlpha']), - cullFace: createSaveStateNamedArgs('cullFace', ['cullFaceMode']), - clearDepth: createSaveStateNamedArgs('clearDepth', ['clearDepth']), - depthFunc: createSaveStateNamedArgs('depthFunc', ['depthFunc']), - depthMask: createSaveStateNamedArgs('depthMask', ['depthMask']), - frontFace: createSaveStateNamedArgs('frontFace', ['frontFace']), - lineWidth: createSaveStateNamedArgs('lineWidth', ['lineWidth']), - polygonOffset: createSaveStateNamedArgs('polygonOffset', ['polygonOffsetFactor', 'polygonOffsetUnits']), - sampleCoverage: createSaveStateNamedArgs('sampleCoverage', ['sampleCoverageValue', 'sampleCoverageUnits']), - clearStencil: createSaveStateNamedArgs('clearStencil', ['clearStencilValue']), - hint(pname, value) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.hint(pname, value); - this._state.generateMipmapHint = value; - }, - bindFramebuffer(bindPoint, framebuffer) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - if (framebuffer === null) { - // bind our drawingBuffer - framebuffer = this._drawingbufferFramebuffer; - } - gl.bindFramebuffer(bindPoint, framebuffer); - switch (bindPoint) { - case gl.FRAMEBUFFER: - this._state.readFramebuffer = framebuffer; - this._state.drawFramebuffer = framebuffer; - break; - case gl.DRAW_FRAMEBUFFER: - this._state.drawFramebuffer = framebuffer; - break; - case gl.READ_FRAMEBUFFER: - this._state.readFramebuffer = framebuffer; - break; - } - }, - drawBuffers: (function() { - const gl = sharedWebGLContext; - const backBuffer = [gl.COLOR_ATTACHMENT0]; - - return function(drawingBuffers) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - // if the virtual context is bound to canvas then fake it - if (isFramebufferBindingNull(this)) { - // this really isn't checking everything - // for example if the user passed in array.length != 1 - // then we are supposed to generate an error - if (drawingBuffers[0] === gl.BACK) { - drawingBuffers = backBuffer; - } - } - - gl.drawBuffers(drawingBuffers); - }; - }()), - clear: createDrawWrapper(WebGL2RenderingContext.prototype.clear), - drawArrays: createDrawWrapper(WebGL2RenderingContext.prototype.drawArrays), - drawElements: createDrawWrapper(WebGL2RenderingContext.prototype.drawElements), - drawArraysInstanced: createDrawWrapper(WebGL2RenderingContext.prototype.drawArraysInstanced), - drawElementsInstanced: createDrawWrapper(WebGL2RenderingContext.prototype.drawElementsInstanced), - drawRangeElements: createDrawWrapper(WebGL2RenderingContext.prototype.drawRangeElements), - useProgram(program) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.useProgram(program); - this._state.currentProgram = program; - }, - bindTransformFeedback(target, tb) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.bindTransformFeedback(target, tb); - this._state.transformFeedback = tb; - }, - readBuffer(src) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - if (src === gl.BACK) { - src = gl.COLOR_ATTACHMENT0; - } - gl.readBuffer(src); - this._state.readBuffer = src; - }, - stencilFunc(func, ref, mask) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilFunc(func, ref, mask); - saveStencilFunc(this._state, gl.FRONT_AND_BACK, func, ref, mask); - }, - stencilFuncSeparate(face, func, ref, mask) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilFuncSeparate(face, func, ref, mask); - saveStencilFunc(this._state, face, func, ref, mask); - }, - stencilMask(mask) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilMask(mask); - saveStencilMask(this._state, gl.FRONT_AND_BACK, mask); - }, - stencilMaskSeparate(face, mask) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilMaskSeparate(face, mask); - saveStencilMask(this._state, face, mask); - }, - stencilOp(fail, zfail, zpass) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilOp(fail, zfail, zpass); - saveStencilOp(this._state, gl.FRONT_AND_BACK, fail, zfail, zpass); - }, - stencilOpSeparate(face, fail, zfail, zpass) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - gl.stencilOpSeparate(face, fail, zfail, zpass); - saveStencilOp(this._state, face, fail, zfail, zpass); - }, - vertexAttrib1f: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1f, ([x]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2f: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1f, ([x, y]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3f: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1f, ([x, y, z]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4f: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1f, ([x, y, z, w]) => [FLOAT, [x, y, z, w]]), - vertexAttrib1fv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1fv, ([[x]]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2fv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1fv, ([[x, y]]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3fv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1fv, ([[x, y, z]]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4fv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1fv, ([[x, y, z, w]]) => [FLOAT, [x, y, z, w]]), - vertexAttrib1i: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1i, ([x]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2i: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1i, ([x, y]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3i: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1i, ([x, y, z]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4i: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1i, ([x, y, z, w]) => [FLOAT, [x, y, z, w]]), - vertexAttrib1iv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1iv, ([[x]]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2iv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1iv, ([[x, y]]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3iv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1iv, ([[x, y, z]]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4iv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1iv, ([[x, y, z, w]]) => [FLOAT, [x, y, z, w]]), - vertexAttrib1ui: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1ui, ([x]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2ui: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1ui, ([x, y]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3ui: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1ui, ([x, y, z]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4ui: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1ui, ([x, y, z, w]) => [FLOAT, [x, y, z, w]]), - vertexAttrib1uiv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1uiv, ([[x]]) => [FLOAT, [x, 0, 0, 1]]), - vertexAttrib2uiv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1uiv, ([[x, y]]) => [FLOAT, [x, y, 0, 1]]), - vertexAttrib3uiv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1uiv, ([[x, y, z]]) => [FLOAT, [x, y, z, 1]]), - vertexAttrib4uiv: createVertexAttribWrapper(WebGL2RenderingContext.prototype.vertexAttrib1uiv, ([[x, y, z, w]]) => [FLOAT, [x, y, z, w]]), - }; - - const webgl1Extensions = { - OES_texture_float: { - fn() { - return {}; - }, - }, - OES_vertex_array_object: { - fn(vCtx) { - return { - VERTEX_ARRAY_BINDING_OES: sharedWebGLContext.VERTEX_ARRAY_BINDING, - createVertexArrayOES() { - return sharedWebGLContext.createVertexArray(); - }, - deleteVertexArrayOES(va) { - sharedWebGLContext.deleteVertexArray(va); - }, - bindVertexArrayOES(va) { - virtualFns.bindVertexArray.call(vCtx, va); - }, - isVertexArrayOES(va) { - return sharedWebGLContext.isVertexArray(va); - }, - }; - }, - }, - ANGLE_instanced_arrays: { - fn(vCtx) { - return { - VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0x88FE, - drawArraysInstancedANGLE(...args) { - virtualFns.drawArraysInstanced.call(vCtx, ...args); - }, - drawElementsInstancedANGLE(...args) { - virtualFns.drawElementsInstanced.call(vCtx, ...args); - }, - vertexAttribDivisorANGLE(...args) { - sharedWebGLContext.vertexAttribDivisor(...args); - }, - }; - }, - }, - // We can't easily support WebGL_draw_buffers because WebGL2 does not - // support gl_FragData. Instead, it requires you to write your shaders - // in GLSL ES 3.0 if you want to use multiple color attachments. To support - // that correctly would require writing a GLSL parser. We need to change - // 'attribute' -> 'in', 'varying' -> 'out' in vertex shaders, 'varying' to 'in' - // in fragment shader, 'texture2D' to 'texture' and declare outputs. That sounds - // simple but it quickly becomes complicated. - // - // * 'texture' is a valid identifier in GLSL ES 1.0 but a reserved word in - // GLSL ES 3.0 so we'd have to rename identifiers - // - // * Changing the fragment shader means it's on longer compatible with - // GLSL ES 1.0 vertex shaders. But, back in WebGL1, we could easily use - // the same vertex shader with and without WEBGL_draw_buffers. That means - // we now need 2 versions of every vertex shader (ES 1.0 and ES 3.0), OR - // we need to translate ALL shaders to GLSL ES 3.0 - // - // * GLSL 1.0 shaders support dynamically indexing an array of samplers. - // GLSL 3.0 does not. So we'd have to emit an emulation function. - // - // The point is it's not a trivial amount of work. - /* - WEBGL_draw_buffers: { - fn(vCtx) { - return { - COLOR_ATTACHMENT0_WEBGL : 0x8CE0, - COLOR_ATTACHMENT1_WEBGL : 0x8CE1, - COLOR_ATTACHMENT2_WEBGL : 0x8CE2, - COLOR_ATTACHMENT3_WEBGL : 0x8CE3, - COLOR_ATTACHMENT4_WEBGL : 0x8CE4, - COLOR_ATTACHMENT5_WEBGL : 0x8CE5, - COLOR_ATTACHMENT6_WEBGL : 0x8CE6, - COLOR_ATTACHMENT7_WEBGL : 0x8CE7, - COLOR_ATTACHMENT8_WEBGL : 0x8CE8, - COLOR_ATTACHMENT9_WEBGL : 0x8CE9, - COLOR_ATTACHMENT10_WEBGL : 0x8CEA, - COLOR_ATTACHMENT11_WEBGL : 0x8CEB, - COLOR_ATTACHMENT12_WEBGL : 0x8CEC, - COLOR_ATTACHMENT13_WEBGL : 0x8CED, - COLOR_ATTACHMENT14_WEBGL : 0x8CEE, - COLOR_ATTACHMENT15_WEBGL : 0x8CEF, - - DRAW_BUFFER0_WEBGL : 0x8825, - DRAW_BUFFER1_WEBGL : 0x8826, - DRAW_BUFFER2_WEBGL : 0x8827, - DRAW_BUFFER3_WEBGL : 0x8828, - DRAW_BUFFER4_WEBGL : 0x8829, - DRAW_BUFFER5_WEBGL : 0x882A, - DRAW_BUFFER6_WEBGL : 0x882B, - DRAW_BUFFER7_WEBGL : 0x882C, - DRAW_BUFFER8_WEBGL : 0x882D, - DRAW_BUFFER9_WEBGL : 0x882E, - DRAW_BUFFER10_WEBGL : 0x882F, - DRAW_BUFFER11_WEBGL : 0x8830, - DRAW_BUFFER12_WEBGL : 0x8831, - DRAW_BUFFER13_WEBGL : 0x8832, - DRAW_BUFFER14_WEBGL : 0x8833, - DRAW_BUFFER15_WEBGL : 0x8834, - - MAX_COLOR_ATTACHMENTS_WEBGL : 0x8CDF, - MAX_DRAW_BUFFERS_WEBGL : 0x8824, - - drawBuffersWEBGL(buffers) { - virtualFns.drawBuffers.call(vCtx, buffers); - }, - }; - }, - }, - */ - }; - - const texImage2DArgParersMap = new Map([ - [9, function([target, level, internalFormat, width, height, , format, type]) { - return {target, level, internalFormat, width, height, format, type}; - }, ], - [6, function([target, level, internalFormat, format, type, texImageSource]) { - return {target, level, internalFormat, width: texImageSource.width, height: texImageSource.height, format, type}; - }, ], - [10, function([target, level, internalFormat, width, height, , format, type]) { - return {target, level, internalFormat, width, height, format, type}; - }, ], - ]); - - const webgl1Fns = { - getExtension(name) { - const existingExt = this._extensions[name]; - if (existingExt) { - return existingExt; - } - - const info = webgl1Extensions[name]; - if (!info) { - return virtualFns.getExtension.call(this, name); - } - - return info.fn(this); - }, - texImage2D(...args) { - makeCurrentContext(this); - resizeCanvasIfChanged(this); - const gl = sharedWebGLContext; - const fn = texImage2DArgParersMap.get(args.length); - const {internalFormat, type} = fn(args); - if (type === sharedWebGLContext.FLOAT) { - switch (internalFormat) { - case gl.RGBA: args[2] = gl.RGBA32F; break; - case gl.RGB: args[2] = gl.RGB32F; break; - } - } - gl.texImage2D(...args); - }, - getSupportedExtensions: function() { - return [ - ...sharedWebGLContext.getSupportedExtensions(), - 'OES_texture_float', - 'WEBGL_depth_texture', - 'OES_vertex_array_object', - // "WEBGL_draw_buffers", // See other comment - ]; - }, - }; - - // copy all WebGL constants and functions to the prototype of - // VirtualWebGLContext - function copyProperties(keys, VirtualClass, overrideFns) { - for (const key of keys) { - const propDesc = Object.getOwnPropertyDescriptor(WebGL2RenderingContext.prototype, key); - if (propDesc.get) { - // it's a getter/setter ? - const virtualPropDesc = Object.getOwnPropertyDescriptor(VirtualClass.prototype, key); - if (!virtualPropDesc) { - console.warn(`WebGL2RenderingContext.${key} is not supported`); - } - continue; - } - switch (key) { - default: { - const value = WebGL2RenderingContext.prototype[key]; - let newValue = value; - const fn = overrideFns[key] || virtualFns[key]; - if (fn) { - newValue = fn; - } else { - if (typeof value === 'function') { - newValue = createWrapper(value, key); - } - } - VirtualClass.prototype[key] = newValue; - break; - } - } - } - } - copyProperties(Object.keys(WebGLRenderingContext.prototype), VirtualWebGLContext, webgl1Fns); - copyProperties(Object.keys(WebGL2RenderingContext.prototype), VirtualWebGL2Context, {}); - - function makeCurrentContext(vCtx) { - if (currentVirtualContext === vCtx) { - return; - } - - // save all current WebGL state on the previous current virtual context - if (currentVirtualContext) { - saveAllState(currentVirtualContext._state, currentVirtualContext); - } - - // restore all state for the new context - restoreAllState(vCtx._state, vCtx); - - // check if the current state is supposed to be rendering to the canvas. - // if so bind vCtx._drawingbuffer - - currentVirtualContext = vCtx; - } - - function resizeCanvasIfChanged(vCtx) { - const width = vCtx.canvas.width; - const height = vCtx.canvas.height; - - if (width !== vCtx._width || height !== vCtx._height) { - vCtx._width = width; - vCtx._height = height; - const gl = sharedWebGLContext; - const oldTexture = gl.getParameter(gl.TEXTURE_BINDING_2D); - const format = vCtx._contextAttributes.alpha ? gl.RGBA : gl.RGB; - gl.bindTexture(gl.TEXTURE_2D, vCtx._drawingbufferTexture); - gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, gl.UNSIGNED_BYTE, null); - gl.bindTexture(gl.TEXTURE_2D, oldTexture); - - if (vCtx._depthRenderbuffer) { - const oldRenderbuffer = gl.getParameter(gl.RENDERBUFFER_BINDING); - const internalFormat = vCtx._contextAttributes.stencil ? gl.DEPTH_STENCIL : gl.DEPTH_COMPONENT16; - gl.bindRenderbuffer(gl.RENDERBUFFER, vCtx._depthRenderbuffer); - gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); - gl.bindRenderbuffer(gl.RENDERBUFFER, oldRenderbuffer); - } - } - } - - function createOrGetVirtualWebGLContext(canvas, type, contextAttributes) { - // check if this canvas already has a context - const existingVirtualCtx = canvasToVirtualContextMap.get(canvas); - if (existingVirtualCtx) { - return existingVirtualCtx; - } - - const compositor = settings.compositorCreator(canvas, type, contextAttributes) || new DefaultCompositor(canvas, type, contextAttributes); - const newVirtualCtx = type === 'webgl2' - ? new VirtualWebGL2Context(canvas, contextAttributes, compositor, () => { - canvasToVirtualContextMap.delete(canvas); - }) - : new VirtualWebGLContext(canvas, contextAttributes, compositor, () => { - canvasToVirtualContextMap.delete(canvas); - }); - - canvasToVirtualContextMap.set(canvas, newVirtualCtx); - - return newVirtualCtx; - } - - function createProgram(gl, shaderSources) { - const program = gl.createProgram(); - [gl.VERTEX_SHADER, gl.FRAGMENT_SHADER].forEach((type, ndx) => { - const shader = gl.createShader(type); - gl.shaderSource(shader, shaderSources[ndx]); - gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - console.error(gl.getShaderInfoLog(shader)); // eslint-disable-line - } - gl.attachShader(program, shader); - }); - gl.bindAttribLocation(program, 0, 'position'); - gl.linkProgram(program); - if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { - console.error(gl.getProgramInfoLog(program)); // eslint-disable-line - } - - return program; - } - - function saveAllState(state, vCtx) { - // save all WebGL state (current bindings, current texture units, - // current attributes and/or vertex shade object, current program, - // current blend, stencil, zBuffer, culling, viewport etc... state - for (const fns of extensionSaveRestoreHelpersArray) { - fns.save(state, vCtx); - } - } - - function setStencil(gl, face, st) { - gl.stencilFuncSeparate(face, st.func, st.ref, st.valueMask); - gl.stencilOpSeparate(face, st.fail, st.depthFail, st.depthPass); - gl.stencilMaskSeparate(face, st.writeMask); - } - - function restoreAllState(state, vCtx) { - // restore all WebGL state (current bindings, current texture units, - // current attributes and/or vertex shade object, current program, - // current blend, stencil, zBuffer, culling, viewport etc... state - // save all WebGL state (current bindings, current texture units, - // current attributes and/or vertex shade object, current program, - // current blend, stencil, zBuffer, culling, viewport etc... state - const gl = sharedWebGLContext; - - gl.bindRenderbuffer(gl.RENDERBUFFER, state.renderbuffer); - gl.bindFramebuffer(gl.READ_FRAMEBUFFER, state.readFramebuffer); - gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, state.drawFramebuffer); - - // restore attributes - gl.bindVertexArray(state.vertexArray); - for (let i = 0; i < numAttribs; ++i) { - const attr = state.attribValues[i]; - switch (attr.type) { - case gl.FLOAT: - gl.vertexAttrib4fv(i, attr.value); - break; - case gl.INT: - gl.vertexAttribI4iv(i, attr.value); - break; - case gl.UNSIGNED_INT: - gl.vertexAttribI4uiv(i, attr.value); - break; - } - } - - gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, state.transformFeedback); - - // restore texture units - for (let i = 0; i < numTextureUnits; ++i) { - gl.activeTexture(gl.TEXTURE0 + i); - const unit = state.textureUnits[i]; - gl.bindTexture(gl.TEXTURE_2D, unit.texture2D); - gl.bindTexture(gl.TEXTURE_CUBE_MAP, unit.textureCubemap); - gl.bindTexture(gl.TEXTURE_2D_ARRAY, unit.texture2DArray); - gl.bindTexture(gl.TEXTURE_3D, unit.texture3D); - gl.bindSampler(i, unit.sampler); - } - gl.activeTexture(state.activeTexture); - - // uniform buffer bindings (must be restored before UNIFORM_BUFFER restore) - for (let i = 0; i < numUniformBufferBindings; ++i) { - const ub = state.uniformBufferBindings[i]; - if (ub.size || ub.start) { - gl.bindBufferRange(gl.UNIFORM_BUFFER, i, ub.buffer, ub.start, ub.size); - } else { - gl.bindBufferBase(gl.UNIFORM_BUFFER, i, ub.buffer); - } - } - - // bindings - gl.bindBuffer(gl.ARRAY_BUFFER, state.arrayBuffer); - gl.bindBuffer(gl.COPY_READ_BUFFER, state.copyReadBuffer); - gl.bindBuffer(gl.COPY_WRITE_BUFFER, state.copyWriteBuffer); - gl.bindBuffer(gl.PIXEL_PACK_BUFFER, state.pixelPackBuffer); - gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, state.pixelUnpackBuffer); - gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, state.transformFeedbackBuffer); - gl.bindBuffer(gl.UNIFORM_BUFFER, state.uniformBuffer); - - gl.readBuffer(state.readBuffer); - - state.enable.forEach((value, key) => { - enableDisable(gl, key, value); - }); - - state.pack.forEach((value, key) => { - gl.pixelStorei(key, value); - }); - - gl.useProgram(state.currentProgram); - - gl.viewport(...state.viewport); - gl.scissor(...state.scissor); - gl.blendFuncSeparate(state.blendSrcRgb, state.blendDstRgb, state.blendSrcAlpha, state.blendDstAlpha); - gl.blendEquationSeparate(state.blendEquationRgb, state.blendEquationAlpha); - gl.blendColor(...state.blendColor); - gl.clearColor(...state.clearColor); - gl.colorMask(...state.colorMask); - gl.cullFace(state.cullFaceMode); - gl.clearDepth(state.clearDepth); - gl.depthFunc(state.depthFunc); - gl.depthRange(...state.depthRange); - gl.depthMask(state.depthMask); - gl.frontFace(state.frontFace); - gl.hint(gl.GENERATE_MIPMAP_HINT, state.generateMipmapHint); - gl.lineWidth(state.lineWidth); - gl.polygonOffset(state.polygonOffsetFactor, state.polygonOffsetUnits); - gl.sampleCoverage(state.sampleCoverageValue, state.sampleCoverageUnits); - - setStencil(gl, gl.BACK, state.stencilBack); - setStencil(gl, gl.FRONT, state.stencilFront); - - gl.clearStencil(state.stencilClearValue); - - for (const fns of extensionSaveRestoreHelpersArray) { - fns.restore(state, vCtx); - } - } - - function enableDisable(gl, feature, enable) { - if (enable) { - gl.enable(feature); - } else { - gl.disable(feature); - } - } - - function renderAllDirtyVirtualCanvases() { - if (!someContextsNeedRendering) { - return; - } - someContextsNeedRendering = false; - - // save all current WebGL state on the previous current virtual context - if (currentVirtualContext) { - saveAllState(currentVirtualContext._state, currentVirtualContext); - currentVirtualContext = null; - } - - // set the state back to the one for drawing the canvas - restoreAllState(baseState); - - for (const vCtx of canvasToVirtualContextMap.values()) { - if (!vCtx._needComposite) { - continue; - } - - vCtx._needComposite = false; - vCtx.composite(sharedWebGLContext); - } - } - - window.requestAnimationFrame = (function(origFn) { - return function(callback) { - return origFn.call(window, (time) => { - const result = callback(time); - renderAllDirtyVirtualCanvases(); - return result; - }); - }; - - }(window.requestAnimationFrame)); - - function setup(options) { - Object.assign(settings, options); - } - - window.virtualWebGL = { - setup, - }; - -}()); -