From 61f02cee40cc594c6e671331085bed439623bcfc Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Wed, 8 Jan 2025 16:46:58 -0500 Subject: [PATCH] Initial support for creating WebGL 2 contexts. This support is very incomplete and only works for a bare minimum of functionality. It hasn't been tested against the WebGL 2 CTS. To create a WebGL 2 context, use the "createWebGL2Context" attribute in the creation options struct. Implementation notes: - in native code, we use a single native class for both context types - we assign enum constants dynamically, so we replace gl.ENUM with this.ENUM in the context helper functions - only adds VAOs from the set of new WebGL 2 object types This contribution is funded by https://higharc.com/ --- index.d.ts | 14 +- index.js | 1 + .../extensions/ext-color-buffer-float.js | 14 + src/javascript/node-index.js | 18 +- src/javascript/webgl-context-attributes.js | 4 +- src/javascript/webgl-rendering-context.js | 840 ++++++++------- src/javascript/webgl-texture-unit.js | 4 + src/javascript/webgl-vertex-array-object.js | 22 + src/native/bindings.cc | 377 ++++++- src/native/webgl.cc | 974 +++++++++++++++++- src/native/webgl.h | 104 +- 11 files changed, 1936 insertions(+), 436 deletions(-) create mode 100644 src/javascript/extensions/ext-color-buffer-float.js create mode 100644 src/javascript/webgl-vertex-array-object.js diff --git a/index.d.ts b/index.d.ts index a35b0831..6ca9d04a 100644 --- a/index.d.ts +++ b/index.d.ts @@ -26,7 +26,19 @@ declare namespace createContext { declare function createContext( width: number, height: number, - options?: WebGLContextAttributes, + options?: WebGLContextAttributes & { createWebGL2Context?: false }, ): WebGLRenderingContext & createContext.StackGLExtension; +declare function createContext( + width: number, + height: number, + options: WebGLContextAttributes & { createWebGL2Context: true } +): WebGL2RenderingContext & createContext.StackGLExtension; + +declare function createContext( + width: number, + height: number, + options?: WebGLContextAttributes & { createWebGL2Context?: boolean } +): (WebGLRenderingContext | WebGL2RenderingContext) & createContext.StackGLExtension; + export = createContext; diff --git a/index.js b/index.js index 0eef94e9..896d3eac 100644 --- a/index.js +++ b/index.js @@ -4,3 +4,4 @@ if (typeof WebGLRenderingContext !== 'undefined') { module.exports = require('./src/javascript/node-index') } module.exports.WebGLRenderingContext = require('./src/javascript/webgl-rendering-context').WebGLRenderingContext +module.exports.WebGL2RenderingContext = require('./src/javascript/webgl-rendering-context').WebGL2RenderingContext diff --git a/src/javascript/extensions/ext-color-buffer-float.js b/src/javascript/extensions/ext-color-buffer-float.js new file mode 100644 index 00000000..a2a2219d --- /dev/null +++ b/src/javascript/extensions/ext-color-buffer-float.js @@ -0,0 +1,14 @@ +class EXTColorBufferFloat {} + +function getEXTColorBufferFloat (context) { + let result = null + const exts = context.getSupportedExtensions() + + if (exts && exts.indexOf('EXT_color_buffer_float') >= 0) { + result = new EXTColorBufferFloat() + } + + return result +} + +module.exports = { getEXTColorBufferFloat, EXTColorBufferFloat } diff --git a/src/javascript/node-index.js b/src/javascript/node-index.js index 9f484eef..61f1833a 100644 --- a/src/javascript/node-index.js +++ b/src/javascript/node-index.js @@ -1,6 +1,6 @@ const bits = require('bit-twiddle') const { WebGLContextAttributes } = require('./webgl-context-attributes') -const { WebGLRenderingContext, wrapContext } = require('./webgl-rendering-context') +const { WebGLRenderingContext, WebGL2RenderingContext, wrapContext } = require('./webgl-rendering-context') const { WebGLTextureUnit } = require('./webgl-texture-unit') const { WebGLVertexArrayObjectState, WebGLVertexArrayGlobalState } = require('./webgl-vertex-attribute') @@ -28,15 +28,17 @@ function createContext (width, height, options) { flag(options, 'premultipliedAlpha', true), flag(options, 'preserveDrawingBuffer', false), flag(options, 'preferLowPowerToHighPerformance', false), - flag(options, 'failIfMajorPerformanceCaveat', false)) + flag(options, 'failIfMajorPerformanceCaveat', false), + flag(options, 'createWebGL2Context', false)) // Can only use premultipliedAlpha if alpha is set contextAttributes.premultipliedAlpha = contextAttributes.premultipliedAlpha && contextAttributes.alpha + const WebGLContext = contextAttributes.createWebGL2Context ? WebGL2RenderingContext : WebGLRenderingContext let ctx try { - ctx = new WebGLRenderingContext( + ctx = new WebGLContext( 1, 1, contextAttributes.alpha, @@ -46,7 +48,8 @@ function createContext (width, height, options) { contextAttributes.premultipliedAlpha, contextAttributes.preserveDrawingBuffer, contextAttributes.preferLowPowerToHighPerformance, - contextAttributes.failIfMajorPerformanceCaveat) + contextAttributes.failIfMajorPerformanceCaveat, + contextAttributes.createWebGL2Context) } catch (e) {} if (!ctx) { return null @@ -73,11 +76,16 @@ function createContext (width, height, options) { ctx._checkStencil = false ctx._stencilState = true + if (contextAttributes.createWebGL2Context) { + ctx._vaos = {} + ctx._activeVertexArrayObject = null + } + // Initialize texture units const numTextures = ctx.getParameter(ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS) ctx._textureUnits = new Array(numTextures) for (let i = 0; i < numTextures; ++i) { - ctx._textureUnits[i] = new WebGLTextureUnit(i) + ctx._textureUnits[i] = new WebGLTextureUnit(ctx, i) } ctx._activeTextureUnit = 0 ctx.activeTexture(ctx.TEXTURE0) diff --git a/src/javascript/webgl-context-attributes.js b/src/javascript/webgl-context-attributes.js index fa25500e..69bfc1cb 100644 --- a/src/javascript/webgl-context-attributes.js +++ b/src/javascript/webgl-context-attributes.js @@ -7,7 +7,8 @@ class WebGLContextAttributes { premultipliedAlpha, preserveDrawingBuffer, preferLowPowerToHighPerformance, - failIfMajorPerformanceCaveat) { + failIfMajorPerformanceCaveat, + createWebGL2Context) { this.alpha = alpha this.depth = depth this.stencil = stencil @@ -16,6 +17,7 @@ class WebGLContextAttributes { this.preserveDrawingBuffer = preserveDrawingBuffer this.preferLowPowerToHighPerformance = preferLowPowerToHighPerformance this.failIfMajorPerformanceCaveat = failIfMajorPerformanceCaveat + this.createWebGL2Context = createWebGL2Context } } diff --git a/src/javascript/webgl-rendering-context.js b/src/javascript/webgl-rendering-context.js index bf593961..9a0a0ba6 100644 --- a/src/javascript/webgl-rendering-context.js +++ b/src/javascript/webgl-rendering-context.js @@ -36,6 +36,8 @@ const { WebGLShader } = require('./webgl-shader') const { WebGLShaderPrecisionFormat } = require('./webgl-shader-precision-format') const { WebGLTexture } = require('./webgl-texture') const { WebGLUniformLocation } = require('./webgl-uniform-location') +const { WebGLVertexArrayObject } = require('./webgl-vertex-array-object') +const { getEXTColorBufferFloat } = require('./extensions/ext-color-buffer-float') // These are defined by the WebGL spec const MAX_UNIFORM_LENGTH = 256 @@ -62,20 +64,28 @@ const availableExtensions = { webgl_draw_buffers: getWebGLDrawBuffers, ext_blend_minmax: getEXTBlendMinMax, ext_texture_filter_anisotropic: getEXTTextureFilterAnisotropic, - ext_shader_texture_lod: getEXTShaderTextureLod + ext_shader_texture_lod: getEXTShaderTextureLod, + ext_color_buffer_float: getEXTColorBufferFloat } const privateMethods = [ + 'constructor', 'resize', 'destroy' ] function wrapContext (ctx) { - const wrapper = new WebGLRenderingContext() - bindPublics(Object.keys(ctx), wrapper, ctx, privateMethods) - bindPublics(Object.keys(ctx.constructor.prototype), wrapper, ctx, privateMethods) - bindPublics(Object.getOwnPropertyNames(ctx), wrapper, ctx, privateMethods) - bindPublics(Object.getOwnPropertyNames(ctx.constructor.prototype), wrapper, ctx, privateMethods) + const isWebGL2 = ctx.constructor.name === 'WebGL2RenderingContext' + const wrapper = isWebGL2 ? new WebGL2RenderingContext() : new WebGLRenderingContext() + + let proto = ctx + while (proto && proto !== Object.prototype) { + bindPublics(Object.keys(proto), wrapper, ctx, privateMethods) + bindPublics(Object.keys(proto.constructor.prototype), wrapper, ctx, privateMethods) + bindPublics(Object.getOwnPropertyNames(proto), wrapper, ctx, privateMethods) + bindPublics(Object.getOwnPropertyNames(proto.constructor.prototype), wrapper, ctx, privateMethods) + proto = Object.getPrototypeOf(proto) + } Object.defineProperties(wrapper, { drawingBufferWidth: { @@ -92,14 +102,14 @@ function wrapContext (ctx) { } // We need to wrap some of the native WebGL functions to handle certain error codes and check input values -class WebGLRenderingContext extends NativeWebGLRenderingContext { +class WebGLRenderingContextHelper extends NativeWebGLRenderingContext { _checkLocation (location) { if (!(location instanceof WebGLUniformLocation)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } else if (location._program._ctx !== this || location._linkCount !== location._program._linkCount) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } return true @@ -111,7 +121,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (!this._checkLocation(location)) { return false } else if (location._program !== this._activeProgram) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } return true @@ -129,16 +139,20 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _checkTextureTarget (target) { const unit = this._getActiveTextureUnit() let tex = null - if (target === gl.TEXTURE_2D) { + if (target === this.TEXTURE_2D) { tex = unit._bind2D - } else if (target === gl.TEXTURE_CUBE_MAP) { + } else if (target === this.TEXTURE_CUBE_MAP) { tex = unit._bindCube + } else if (this._isWebGL2() && target === this.TEXTURE_3D) { + tex = unit._bind3D + } else if (this._isWebGL2() && target === this.TEXTURE_2D_ARRAY) { + tex = unit._bind2DArray } else { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return false } if (!tex) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } return true @@ -146,10 +160,10 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _checkWrapper (object, Wrapper) { if (!this._checkValid(object, Wrapper)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } else if (!this._checkOwns(object)) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } return true @@ -161,20 +175,20 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _checkVertexIndex (index) { if (index < 0 || index >= this._vertexObjectState._attribs.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } return true } _fixupLink (program) { - if (!super.getProgramParameter(program._, gl.LINK_STATUS)) { + if (!super.getProgramParameter(program._, this.LINK_STATUS)) { program._linkInfoLog = super.getProgramInfoLog(program._) return false } // Record attribute attributeLocations - const numAttribs = this.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES) + const numAttribs = this.getProgramParameter(program, this.ACTIVE_ATTRIBUTES) const names = new Array(numAttribs) program._attributes.length = numAttribs for (let i = 0; i < numAttribs; ++i) { @@ -199,7 +213,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { super.linkProgram(program._ | 0) - const numUniforms = this.getProgramParameter(program, gl.ACTIVE_UNIFORMS) + const numUniforms = this.getProgramParameter(program, this.ACTIVE_UNIFORMS) program._uniforms.length = numUniforms for (let i = 0; i < numUniforms; ++i) { program._uniforms[i] = this.getActiveUniform(program, i) @@ -222,9 +236,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } _getActiveBuffer (target) { - if (target === gl.ARRAY_BUFFER) { + if (target === this.ARRAY_BUFFER) { return this._vertexGlobalState._arrayBufferBinding - } else if (target === gl.ELEMENT_ARRAY_BUFFER) { + } else if (target === this.ELEMENT_ARRAY_BUFFER) { return this._vertexObjectState._elementArrayBufferBinding } return null @@ -236,10 +250,14 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _getActiveTexture (target) { const activeUnit = this._getActiveTextureUnit() - if (target === gl.TEXTURE_2D) { + if (target === this.TEXTURE_2D) { return activeUnit._bind2D - } else if (target === gl.TEXTURE_CUBE_MAP) { + } else if (target === this.TEXTURE_CUBE_MAP) { return activeUnit._bindCube + } else if (this._isWebGL2() && target === this.TEXTURE_2D_ARRAY) { + return activeUnit._bind2DArray + } else if (this._isWebGL2() && target === this.TEXTURE_3D) { + return activeUnit._bind3D } return null } @@ -258,21 +276,21 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _getTexImage (target) { const unit = this._getActiveTextureUnit() - if (target === gl.TEXTURE_2D) { + if (target === this.TEXTURE_2D) { return unit._bind2D } else if (validCubeTarget(target)) { return unit._bindCube } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return null } _isConstantBlendFunc (factor) { return ( - factor === gl.CONSTANT_COLOR || - factor === gl.ONE_MINUS_CONSTANT_COLOR || - factor === gl.CONSTANT_ALPHA || - factor === gl.ONE_MINUS_CONSTANT_ALPHA) + factor === this.CONSTANT_COLOR || + factor === this.ONE_MINUS_CONSTANT_COLOR || + factor === this.CONSTANT_ALPHA || + factor === this.ONE_MINUS_CONSTANT_ALPHA) } _isObject (object, method, Wrapper) { @@ -288,43 +306,43 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { _resizeDrawingBuffer (width, height) { const prevFramebuffer = this._activeFramebuffer - const prevTexture = this._getActiveTexture(gl.TEXTURE_2D) + const prevTexture = this._getActiveTexture(this.TEXTURE_2D) const prevRenderbuffer = this._activeRenderbuffer const contextAttributes = this._contextAttributes const drawingBuffer = this._drawingBuffer - super.bindFramebuffer(gl.FRAMEBUFFER, drawingBuffer._framebuffer) + super.bindFramebuffer(this.FRAMEBUFFER, drawingBuffer._framebuffer) const attachments = this._getAttachments() // Clear all attachments for (let i = 0; i < attachments.length; ++i) { super.framebufferTexture2D( - gl.FRAMEBUFFER, + this.FRAMEBUFFER, attachments[i], - gl.TEXTURE_2D, + this.TEXTURE_2D, 0, 0) } // Update color attachment - super.bindTexture(gl.TEXTURE_2D, drawingBuffer._color) - const colorFormat = contextAttributes.alpha ? gl.RGBA : gl.RGB + super.bindTexture(this.TEXTURE_2D, drawingBuffer._color) + const colorFormat = contextAttributes.alpha ? this.RGBA : this.RGB super.texImage2D( - gl.TEXTURE_2D, + this.TEXTURE_2D, 0, colorFormat, width, height, 0, colorFormat, - gl.UNSIGNED_BYTE, + this.UNSIGNED_BYTE, null) - super.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - super.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + super.texParameteri(this.TEXTURE_2D, this.TEXTURE_MIN_FILTER, this.NEAREST) + super.texParameteri(this.TEXTURE_2D, this.TEXTURE_MAG_FILTER, this.NEAREST) super.framebufferTexture2D( - gl.FRAMEBUFFER, - gl.COLOR_ATTACHMENT0, - gl.TEXTURE_2D, + this.FRAMEBUFFER, + this.COLOR_ATTACHMENT0, + this.TEXTURE_2D, drawingBuffer._color, 0) @@ -332,41 +350,41 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { let storage = 0 let attachment = 0 if (contextAttributes.depth && contextAttributes.stencil) { - storage = gl.DEPTH_STENCIL - attachment = gl.DEPTH_STENCIL_ATTACHMENT + storage = this.DEPTH_STENCIL + attachment = this.DEPTH_STENCIL_ATTACHMENT } else if (contextAttributes.depth) { storage = 0x81A7 - attachment = gl.DEPTH_ATTACHMENT + attachment = this.DEPTH_ATTACHMENT } else if (contextAttributes.stencil) { - storage = gl.STENCIL_INDEX8 - attachment = gl.STENCIL_ATTACHMENT + storage = this.STENCIL_INDEX8 + attachment = this.STENCIL_ATTACHMENT } if (storage) { super.bindRenderbuffer( - gl.RENDERBUFFER, + this.RENDERBUFFER, drawingBuffer._depthStencil) super.renderbufferStorage( - gl.RENDERBUFFER, + this.RENDERBUFFER, storage, width, height) super.framebufferRenderbuffer( - gl.FRAMEBUFFER, + this.FRAMEBUFFER, attachment, - gl.RENDERBUFFER, + this.RENDERBUFFER, drawingBuffer._depthStencil) } // Restore previous binding state - this.bindFramebuffer(gl.FRAMEBUFFER, prevFramebuffer) - this.bindTexture(gl.TEXTURE_2D, prevTexture) - this.bindRenderbuffer(gl.RENDERBUFFER, prevRenderbuffer) + this.bindFramebuffer(this.FRAMEBUFFER, prevFramebuffer) + this.bindTexture(this.TEXTURE_2D, prevTexture) + this.bindRenderbuffer(this.RENDERBUFFER, prevRenderbuffer) } _restoreError (lastError) { const topError = this._errorStack.pop() - if (topError === gl.NO_ERROR) { + if (topError === this.NO_ERROR) { this.setError(lastError) } else { this.setError(topError) @@ -393,9 +411,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { for (let i = 0; i < framebufferAttachments.length; ++i) { if (framebuffer._attachments[attachments[i]] === renderbuffer) { this.framebufferTexture2D( - gl.FRAMEBUFFER, + this.FRAMEBUFFER, attachments[i] | 0, - gl.TEXTURE_2D, + this.TEXTURE_2D, null) } } @@ -403,47 +421,47 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } _validBlendFunc (factor) { - return factor === gl.ZERO || - factor === gl.ONE || - factor === gl.SRC_COLOR || - factor === gl.ONE_MINUS_SRC_COLOR || - factor === gl.DST_COLOR || - factor === gl.ONE_MINUS_DST_COLOR || - factor === gl.SRC_ALPHA || - factor === gl.ONE_MINUS_SRC_ALPHA || - factor === gl.DST_ALPHA || - factor === gl.ONE_MINUS_DST_ALPHA || - factor === gl.SRC_ALPHA_SATURATE || - factor === gl.CONSTANT_COLOR || - factor === gl.ONE_MINUS_CONSTANT_COLOR || - factor === gl.CONSTANT_ALPHA || - factor === gl.ONE_MINUS_CONSTANT_ALPHA + return factor === this.ZERO || + factor === this.ONE || + factor === this.SRC_COLOR || + factor === this.ONE_MINUS_SRC_COLOR || + factor === this.DST_COLOR || + factor === this.ONE_MINUS_DST_COLOR || + factor === this.SRC_ALPHA || + factor === this.ONE_MINUS_SRC_ALPHA || + factor === this.DST_ALPHA || + factor === this.ONE_MINUS_DST_ALPHA || + factor === this.SRC_ALPHA_SATURATE || + factor === this.CONSTANT_COLOR || + factor === this.ONE_MINUS_CONSTANT_COLOR || + factor === this.CONSTANT_ALPHA || + factor === this.ONE_MINUS_CONSTANT_ALPHA } _validBlendMode (mode) { - return mode === gl.FUNC_ADD || - mode === gl.FUNC_SUBTRACT || - mode === gl.FUNC_REVERSE_SUBTRACT || + return mode === this.FUNC_ADD || + mode === this.FUNC_SUBTRACT || + mode === this.FUNC_REVERSE_SUBTRACT || (this._extensions.ext_blend_minmax && ( mode === this._extensions.ext_blend_minmax.MIN_EXT || mode === this._extensions.ext_blend_minmax.MAX_EXT)) } _validCubeTarget (target) { - return target === gl.TEXTURE_CUBE_MAP_POSITIVE_X || - target === gl.TEXTURE_CUBE_MAP_NEGATIVE_X || - target === gl.TEXTURE_CUBE_MAP_POSITIVE_Y || - target === gl.TEXTURE_CUBE_MAP_NEGATIVE_Y || - target === gl.TEXTURE_CUBE_MAP_POSITIVE_Z || - target === gl.TEXTURE_CUBE_MAP_NEGATIVE_Z + return target === this.TEXTURE_CUBE_MAP_POSITIVE_X || + target === this.TEXTURE_CUBE_MAP_NEGATIVE_X || + target === this.TEXTURE_CUBE_MAP_POSITIVE_Y || + target === this.TEXTURE_CUBE_MAP_NEGATIVE_Y || + target === this.TEXTURE_CUBE_MAP_POSITIVE_Z || + target === this.TEXTURE_CUBE_MAP_NEGATIVE_Z } _validFramebufferAttachment (attachment) { switch (attachment) { - case gl.DEPTH_ATTACHMENT: - case gl.STENCIL_ATTACHMENT: - case gl.DEPTH_STENCIL_ATTACHMENT: - case gl.COLOR_ATTACHMENT0: + case this.DEPTH_ATTACHMENT: + case this.STENCIL_ATTACHMENT: + case this.DEPTH_STENCIL_ATTACHMENT: + case this.COLOR_ATTACHMENT0: return true } @@ -462,21 +480,21 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } _validTextureTarget (target) { - return target === gl.TEXTURE_2D || - target === gl.TEXTURE_CUBE_MAP + return target === this.TEXTURE_2D || + target === this.TEXTURE_CUBE_MAP } _verifyTextureCompleteness (target, pname, param) { const unit = this._getActiveTextureUnit() let texture = null - if (target === gl.TEXTURE_2D) { + if (target === this.TEXTURE_2D) { texture = unit._bind2D } else if (this._validCubeTarget(target)) { texture = unit._bindCube } // oes_texture_float but not oes_texture_float_linear - if (this._extensions.oes_texture_float && !this._extensions.oes_texture_float_linear && texture && texture._type === gl.FLOAT && (pname === gl.TEXTURE_MAG_FILTER || pname === gl.TEXTURE_MIN_FILTER) && (param === gl.LINEAR || param === gl.LINEAR_MIPMAP_NEAREST || param === gl.NEAREST_MIPMAP_LINEAR || param === gl.LINEAR_MIPMAP_LINEAR)) { + if (this._extensions.oes_texture_float && !this._extensions.oes_texture_float_linear && texture && texture._type === this.FLOAT && (pname === this.TEXTURE_MAG_FILTER || pname === this.TEXTURE_MIN_FILTER) && (param === this.LINEAR || param === this.LINEAR_MIPMAP_NEAREST || param === this.NEAREST_MIPMAP_LINEAR || param === this.LINEAR_MIPMAP_LINEAR)) { texture._complete = false this.bindTexture(target, texture) return @@ -494,13 +512,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { activeTexture (texture) { texture |= 0 - const texNum = texture - gl.TEXTURE0 + const texNum = texture - this.TEXTURE0 if (texNum >= 0 && texNum < this._textureUnits.length) { this._activeTextureUnit = texNum return super.activeTexture(texture) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } attachShader (program, shader) { @@ -509,7 +527,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('attachShader(WebGLProgram, WebGLShader)') } if (!program || !shader) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } else if (program instanceof WebGLProgram && shader instanceof WebGLShader && @@ -522,13 +540,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { shader._ | 0) const error = this.getError() this._restoreError(error) - if (error === gl.NO_ERROR) { + if (error === this.NO_ERROR) { program._link(shader) } return } } - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) } bindAttribLocation (program, index, name) { @@ -538,9 +556,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } name += '' if (!isValidString(name) || name.length > MAX_ATTRIBUTE_LENGTH) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (/^_?webgl_a/.test(name)) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) } else if (this._checkWrapper(program, WebGLProgram)) { return super.bindAttribLocation( program._ | 0, @@ -593,9 +611,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!checkObject(buffer)) { throw new TypeError('bindBuffer(GLenum, WebGLBuffer)') } - if (target !== gl.ARRAY_BUFFER && - target !== gl.ELEMENT_ARRAY_BUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.ARRAY_BUFFER && + target !== this.ELEMENT_ARRAY_BUFFER) { + this.setError(this.INVALID_ENUM) return } @@ -606,7 +624,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } else if (this._checkWrapper(buffer, WebGLBuffer)) { if (buffer._binding && buffer._binding !== target) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } buffer._binding = target | 0 @@ -616,7 +634,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } - if (target === gl.ARRAY_BUFFER) { + if (target === this.ARRAY_BUFFER) { // Buffers of type ARRAY_BUFFER are bound to the global vertex state. this._vertexGlobalState.setArrayBuffer(buffer) } else { @@ -630,8 +648,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('bindRenderbuffer(GLenum, WebGLRenderbuffer)') } - if (target !== gl.RENDERBUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.RENDERBUFFER) { + this.setError(this.INVALID_ENUM) return } @@ -690,7 +708,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const error = this.getError() this._restoreError(error) - if (error !== gl.NO_ERROR) { + if (error !== this.NO_ERROR) { return } @@ -708,13 +726,55 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } } - if (target === gl.TEXTURE_2D) { + if (target === this.TEXTURE_2D) { activeUnit._bind2D = texture - } else if (target === gl.TEXTURE_CUBE_MAP) { + } else if (target === this.TEXTURE_CUBE_MAP) { activeUnit._bindCube = texture + } else if (this._isWebGL2() && target === this.TEXTURE_2D_ARRAY) { + activeUnit._bind2DArray = texture + } else if (this._isWebGL2() && target === this.TEXTURE_3D) { + activeUnit._bind3D = texture } } + bindVertexArray (array) { + if (!checkObject(array)) { + throw new TypeError('bindVertexArray(WebGLVertexArrayObject)') + } + + if (!array) { + array = null + super.bindVertexArray(null) + } else if (array instanceof WebGLVertexArrayObject && + array._pendingDelete) { + this.setError(gl.INVALID_OPERATION) + return + } else if (this._checkWrapper(array, WebGLVertexArrayObject)) { + super.bindVertexArray(array._) + } else { + return + } + + if (this._activeVertexArrayObject !== array) { + if (this._activeVertexArrayObject) { + this._activeVertexArrayObject._refCount -= 1 + this._activeVertexArrayObject._checkDelete() + } + if (array) { + array._refCount += 1 + } + } + + if (array === null) { + this._vertexObjectState = this._defaultVertexObjectState + } else { + this._vertexObjectState = array._vertexState + } + + // Update the active vertex array object. + this._activeVertexArrayObject = array + } + blendColor (red, green, blue, alpha) { return super.blendColor(+red, +green, +blue, +alpha) } @@ -724,7 +784,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (this._validBlendMode(mode)) { return super.blendEquation(mode) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } blendEquationSeparate (modeRGB, modeAlpha) { @@ -733,7 +793,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (this._validBlendMode(modeRGB) && this._validBlendMode(modeAlpha)) { return super.blendEquationSeparate(modeRGB, modeAlpha) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } createBuffer () { @@ -776,6 +836,15 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return webGlTexture } + createVertexArray () { + // TODO: do not expose VAO methods in WebGL 1 + const arrayId = super.createVertexArray() + if (arrayId <= 0) return null + const array = new WebGLVertexArrayObject(arrayId, this) + this._vaos[arrayId] = array + return array + } + getContextAttributes () { return this._contextAttributes } @@ -797,51 +866,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } getSupportedExtensions () { - const exts = [ - 'ANGLE_instanced_arrays', - 'STACKGL_resize_drawingbuffer', - 'STACKGL_destroy_context' - ] - - const supportedExts = super.getSupportedExtensions() - - if (supportedExts.indexOf('GL_OES_element_index_uint') >= 0) { - exts.push('OES_element_index_uint') - } - - if (supportedExts.indexOf('GL_OES_standard_derivatives') >= 0) { - exts.push('OES_standard_derivatives') - } - - if (supportedExts.indexOf('GL_OES_texture_float') >= 0) { - exts.push('OES_texture_float') - } - - if (supportedExts.indexOf('GL_OES_texture_float_linear') >= 0) { - exts.push('OES_texture_float_linear') - } - - if (supportedExts.indexOf('GL_EXT_draw_buffers') >= 0) { - exts.push('WEBGL_draw_buffers') - } - - if (supportedExts.indexOf('GL_EXT_blend_minmax') >= 0) { - exts.push('EXT_blend_minmax') - } - - if (supportedExts.indexOf('GL_EXT_texture_filter_anisotropic') >= 0) { - exts.push('EXT_texture_filter_anisotropic') - } - - if (supportedExts.indexOf('GL_OES_vertex_array_object') >= 0) { - exts.push('OES_vertex_array_object') - } - - if (supportedExts.indexOf('GL_EXT_shader_texture_lod') >= 0) { - exts.push('EXT_shader_texture_lod') - } - - return exts + const ret = super.getSupportedExtensions() + return ret.split(' ') } setError (error) { @@ -853,11 +879,11 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { dfactor |= 0 if (!this._validBlendFunc(sfactor) || !this._validBlendFunc(dfactor)) { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return } if (this._isConstantBlendFunc(sfactor) && this._isConstantBlendFunc(dfactor)) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } super.blendFunc(sfactor, dfactor) @@ -877,13 +903,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { this._validBlendFunc(dstRGB) && this._validBlendFunc(srcAlpha) && this._validBlendFunc(dstAlpha))) { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return } if ((this._isConstantBlendFunc(srcRGB) && this._isConstantBlendFunc(dstRGB)) || (this._isConstantBlendFunc(srcAlpha) && this._isConstantBlendFunc(dstAlpha))) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -897,22 +923,22 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { bufferData (target, data, usage) { target |= 0 usage |= 0 - if (usage !== gl.STREAM_DRAW && - usage !== gl.STATIC_DRAW && - usage !== gl.DYNAMIC_DRAW) { - this.setError(gl.INVALID_ENUM) + if (usage !== this.STREAM_DRAW && + usage !== this.STATIC_DRAW && + usage !== this.DYNAMIC_DRAW) { + this.setError(this.INVALID_ENUM) return } - if (target !== gl.ARRAY_BUFFER && - target !== gl.ELEMENT_ARRAY_BUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.ARRAY_BUFFER && + target !== this.ELEMENT_ARRAY_BUFFER) { + this.setError(this.INVALID_ENUM) return } const active = this._getActiveBuffer(target) if (!active) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -923,7 +949,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (data instanceof ArrayBuffer) { u8Data = new Uint8Array(data) } else { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -934,18 +960,18 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { usage) const error = this.getError() this._restoreError(error) - if (error !== gl.NO_ERROR) { + if (error !== this.NO_ERROR) { return } active._size = u8Data.length - if (target === gl.ELEMENT_ARRAY_BUFFER) { + if (target === this.ELEMENT_ARRAY_BUFFER) { active._elements = new Uint8Array(u8Data) } } else if (typeof data === 'number') { const size = data | 0 if (size < 0) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -956,16 +982,16 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { usage) const error = this.getError() this._restoreError(error) - if (error !== gl.NO_ERROR) { + if (error !== this.NO_ERROR) { return } active._size = size - if (target === gl.ELEMENT_ARRAY_BUFFER) { + if (target === this.ELEMENT_ARRAY_BUFFER) { active._elements = new Uint8Array(size) } } else { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } } @@ -973,9 +999,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { target |= 0 offset |= 0 - if (target !== gl.ARRAY_BUFFER && - target !== gl.ELEMENT_ARRAY_BUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.ARRAY_BUFFER && + target !== this.ELEMENT_ARRAY_BUFFER) { + this.setError(this.INVALID_ENUM) return } @@ -984,18 +1010,18 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } if (!data || typeof data !== 'object') { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } const active = this._getActiveBuffer(target) if (!active) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } if (offset < 0 || offset >= active._size) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -1005,16 +1031,16 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (data instanceof ArrayBuffer) { u8Data = new Uint8Array(data) } else { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } if (offset + u8Data.length > active._size) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } - if (target === gl.ELEMENT_ARRAY_BUFFER) { + if (target === this.ELEMENT_ARRAY_BUFFER) { active._elements.set(u8Data, offset) } @@ -1063,7 +1089,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const error = this.getError() shader._compileStatus = !!super.getShaderParameter( shader._ | 0, - gl.COMPILE_STATUS) + this.COMPILE_STATUS) shader._compileInfo = super.getShaderInfoLog(shader._ | 0) this.getError() this.setError(prevError || error) @@ -1098,7 +1124,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const error = this.getError() this._restoreError(error) - if (error === gl.NO_ERROR) { + if (error === this.NO_ERROR) { const texture = this._getTexImage(target) texture._format = gl.RGBA texture._type = gl.UNSIGNED_BYTE @@ -1136,9 +1162,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { createShader (type) { type |= 0 - if (type !== gl.FRAGMENT_SHADER && - type !== gl.VERTEX_SHADER) { - this.setError(gl.INVALID_ENUM) + if (type !== this.FRAGMENT_SHADER && + type !== this.VERTEX_SHADER) { + this.setError(this.INVALID_ENUM) return null } const id = super.createShader(type) @@ -1169,7 +1195,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } if (object !== null) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) } } @@ -1181,15 +1207,15 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!(buffer instanceof WebGLBuffer && this._checkOwns(buffer))) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } if (this._vertexGlobalState._arrayBufferBinding === buffer) { - this.bindBuffer(gl.ARRAY_BUFFER, null) + this.bindBuffer(this.ARRAY_BUFFER, null) } if (this._vertexObjectState._elementArrayBufferBinding === buffer) { - this.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null) + this.bindBuffer(this.ELEMENT_ARRAY_BUFFER, null) } if (this._vertexObjectState === this._defaultVertexObjectState) { @@ -1209,12 +1235,12 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!(framebuffer instanceof WebGLFramebuffer && this._checkOwns(framebuffer))) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } if (this._activeFramebuffer === framebuffer) { - this.bindFramebuffer(gl.FRAMEBUFFER, null) + this.bindFramebuffer(this.FRAMEBUFFER, null) } framebuffer._pendingDelete = true @@ -1239,12 +1265,12 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!(renderbuffer instanceof WebGLRenderbuffer && this._checkOwns(renderbuffer))) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } if (this._activeRenderbuffer === renderbuffer) { - this.bindRenderbuffer(gl.RENDERBUFFER, null) + this.bindRenderbuffer(this.RENDERBUFFER, null) } const activeFramebuffer = this._activeFramebuffer @@ -1262,7 +1288,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (texture instanceof WebGLTexture) { if (!this._checkOwns(texture)) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } } else { @@ -1275,14 +1301,20 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { for (let i = 0; i < this._textureUnits.length; ++i) { const unit = this._textureUnits[i] if (unit._bind2D === texture) { - this.activeTexture(gl.TEXTURE0 + i) - this.bindTexture(gl.TEXTURE_2D, null) + this.activeTexture(this.TEXTURE0 + i) + this.bindTexture(this.TEXTURE_2D, null) } else if (unit._bindCube === texture) { - this.activeTexture(gl.TEXTURE0 + i) - this.bindTexture(gl.TEXTURE_CUBE_MAP, null) + this.activeTexture(this.TEXTURE0 + i) + this.bindTexture(this.TEXTURE_CUBE_MAP, null) + } else if (this._isWebGL2() && unit._bind2DArray === texture) { + this.activeTexture(this.TEXTURE_2D_ARRAY) + this.bindTexture(this.TEXTURE_2D_ARRAY, null) + } else if (this._isWebGL2() && unit._bind3D === texture) { + this.activeTexture(this.TEXTURE_3D) + this.bindTexture(this.TEXTURE_3D, null) } } - this.activeTexture(gl.TEXTURE0 + curActive) + this.activeTexture(this.TEXTURE0 + curActive) // FIXME: Does the texture get unbound from *all* framebuffers, or just the // active FBO? @@ -1295,9 +1327,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const attachment = attachments[i] if (framebuffer._attachments[attachment] === texture) { ctx.framebufferTexture2D( - gl.FRAMEBUFFER, + this.FRAMEBUFFER, attachment, - gl.TEXTURE_2D, + this.TEXTURE_2D, null) } } @@ -1311,20 +1343,43 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { texture._checkDelete() } + deleteVertexArray (array) { + if (!checkObject(array)) { + throw new TypeError('deleteVertexArray(WebGLVertexArrayObject)') + } + + if (!(array instanceof WebGLVertexArrayObject && + this._checkOwns(array))) { + this.setError(gl.INVALID_OPERATION) + return + } + + if (array._pendingDelete) { + return + } + + if (this._activeVertexArrayObject === array) { + this.bindVertexArray(null) + } + + array._pendingDelete = true + array._checkDelete() + } + depthFunc (func) { func |= 0 switch (func) { - case gl.NEVER: - case gl.LESS: - case gl.EQUAL: - case gl.LEQUAL: - case gl.GREATER: - case gl.NOTEQUAL: - case gl.GEQUAL: - case gl.ALWAYS: + case this.NEVER: + case this.LESS: + case this.EQUAL: + case this.LEQUAL: + case this.GREATER: + case this.NOTEQUAL: + case this.GEQUAL: + case this.ALWAYS: return super.depthFunc(func) default: - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } } @@ -1338,7 +1393,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (zNear <= zFar) { return super.depthRange(zNear, zFar) } - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) } destroy () { @@ -1356,7 +1411,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { super.detachShader(program._, shader._) program._unlink(shader) } else { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) } } } @@ -1364,8 +1419,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { disable (cap) { cap |= 0 super.disable(cap) - if (cap === gl.TEXTURE_2D || - cap === gl.TEXTURE_CUBE_MAP) { + if (cap === this.TEXTURE_2D || + cap === this.TEXTURE_CUBE_MAP) { const active = this._getActiveTextureUnit() if (active._mode === cap) { active._mode = 0 @@ -1376,7 +1431,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { disableVertexAttribArray (index) { index |= 0 if (index < 0 || index >= this._vertexObjectState._attribs.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } super.disableVertexAttribArray(index) @@ -1408,7 +1463,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { enableVertexAttribArray (index) { index |= 0 if (index < 0 || index >= this._vertexObjectState._attribs.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -1439,14 +1494,14 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } if (!this._validFramebufferAttachment(attachment) || - renderbufferTarget !== gl.RENDERBUFFER) { - this.setError(gl.INVALID_ENUM) + renderbufferTarget !== this.RENDERBUFFER) { + this.setError(this.INVALID_ENUM) return } const framebuffer = this._activeFramebuffer if (!framebuffer) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -1473,12 +1528,12 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { // Check parameters are ok if (!this._validFramebufferAttachment(attachment)) { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return } if (level !== 0) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -1488,25 +1543,25 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } // Check texture target is ok - if (textarget === gl.TEXTURE_2D) { - if (texture && texture._binding !== gl.TEXTURE_2D) { - this.setError(gl.INVALID_OPERATION) + if (textarget === this.TEXTURE_2D) { + if (texture && texture._binding !== this.TEXTURE_2D) { + this.setError(this.INVALID_OPERATION) return } } else if (this._validCubeTarget(textarget)) { - if (texture && texture._binding !== gl.TEXTURE_CUBE_MAP) { - this.setError(gl.INVALID_OPERATION) + if (texture && texture._binding !== this.TEXTURE_CUBE_MAP) { + this.setError(this.INVALID_OPERATION) return } } else { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return } // Check a framebuffer is actually bound const framebuffer = this._activeFramebuffer if (!framebuffer) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -1525,7 +1580,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!checkObject(program)) { throw new TypeError('getActiveAttrib(WebGLProgram)') } else if (!program) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (this._checkWrapper(program, WebGLProgram)) { const info = super.getActiveAttrib(program._ | 0, index | 0) if (info) { @@ -1539,7 +1594,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!checkObject(program)) { throw new TypeError('getActiveUniform(WebGLProgram, GLint)') } else if (!program) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (this._checkWrapper(program, WebGLProgram)) { const info = super.getActiveUniform(program._ | 0, index | 0) if (info) { @@ -1557,7 +1612,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('getAttachedShaders(WebGLProgram)') } if (!program) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (this._checkWrapper(program, WebGLProgram)) { const shaderArray = super.getAttachedShaders(program._ | 0) if (!shaderArray) { @@ -1578,7 +1633,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } name += '' if (!isValidString(name) || name.length > MAX_ATTRIBUTE_LENGTH) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (this._checkWrapper(program, WebGLProgram)) { return super.getAttribLocation(program._ | 0, name + '') } @@ -1587,29 +1642,29 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { getParameter (pname) { switch (pname) { - case gl.COMPRESSED_TEXTURE_FORMATS: + case this.COMPRESSED_TEXTURE_FORMATS: return new Uint32Array(0) - case gl.ARRAY_BUFFER_BINDING: + case this.ARRAY_BUFFER_BINDING: return this._vertexGlobalState._arrayBufferBinding - case gl.ELEMENT_ARRAY_BUFFER_BINDING: + case this.ELEMENT_ARRAY_BUFFER_BINDING: return this._vertexObjectState._elementArrayBufferBinding - case gl.CURRENT_PROGRAM: + case this.CURRENT_PROGRAM: return this._activeProgram - case gl.FRAMEBUFFER_BINDING: + case this.FRAMEBUFFER_BINDING: return this._activeFramebuffer - case gl.RENDERBUFFER_BINDING: + case this.RENDERBUFFER_BINDING: return this._activeRenderbuffer - case gl.TEXTURE_BINDING_2D: + case this.TEXTURE_BINDING_2D: return this._getActiveTextureUnit()._bind2D - case gl.TEXTURE_BINDING_CUBE_MAP: + case this.TEXTURE_BINDING_CUBE_MAP: return this._getActiveTextureUnit()._bindCube - case gl.VERSION: + case this.VERSION: return 'WebGL 1.0 stack-gl ' + HEADLESS_VERSION - case gl.VENDOR: + case this.VENDOR: return 'stack-gl' - case gl.RENDERER: + case this.RENDERER: return 'ANGLE' - case gl.SHADING_LANGUAGE_VERSION: + case this.SHADING_LANGUAGE_VERSION: return 'WebGL GLSL ES 1.0 stack-gl' default: @@ -1628,15 +1683,15 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { shaderType |= 0 precisionType |= 0 - if (!(shaderType === gl.FRAGMENT_SHADER || - shaderType === gl.VERTEX_SHADER) || - !(precisionType === gl.LOW_FLOAT || - precisionType === gl.MEDIUM_FLOAT || - precisionType === gl.HIGH_FLOAT || - precisionType === gl.LOW_INT || - precisionType === gl.MEDIUM_INT || - precisionType === gl.HIGH_INT)) { - this.setError(gl.INVALID_ENUM) + if (!(shaderType === this.FRAGMENT_SHADER || + shaderType === this.VERTEX_SHADER) || + !(precisionType === this.LOW_FLOAT || + precisionType === this.MEDIUM_FLOAT || + precisionType === this.HIGH_FLOAT || + precisionType === this.LOW_INT || + precisionType === this.MEDIUM_INT || + precisionType === this.HIGH_INT)) { + this.setError(this.INVALID_ENUM) return } @@ -1651,18 +1706,18 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { getBufferParameter (target, pname) { target |= 0 pname |= 0 - if (target !== gl.ARRAY_BUFFER && - target !== gl.ELEMENT_ARRAY_BUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.ARRAY_BUFFER && + target !== this.ELEMENT_ARRAY_BUFFER) { + this.setError(this.INVALID_ENUM) return null } switch (pname) { - case gl.BUFFER_SIZE: - case gl.BUFFER_USAGE: + case this.BUFFER_SIZE: + case this.BUFFER_USAGE: return super.getBufferParameter(target | 0, pname | 0) default: - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return null } } @@ -1678,7 +1733,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { // Note: there's an ANGLE bug where it doesn't check for the default framebuffer in WebGL compat. if (!this._activeFramebuffer) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return null } @@ -1691,9 +1746,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return null } - if (error === gl.NO_ERROR && pname === gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { - const type = super.getFramebufferAttachmentParameter(target, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) - if (type === gl.RENDERBUFFER) { + if (error === this.NO_ERROR && pname === this.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME) { + const type = super.getFramebufferAttachmentParameter(target, attachment, this.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) + if (type === this.RENDERBUFFER) { return this._renderbuffers[result] } else { return this._textures[result] @@ -1709,21 +1764,21 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('getProgramParameter(WebGLProgram, GLenum)') } else if (this._checkWrapper(program, WebGLProgram)) { switch (pname) { - case gl.DELETE_STATUS: + case this.DELETE_STATUS: return program._pendingDelete - case gl.LINK_STATUS: + case this.LINK_STATUS: return program._linkStatus - case gl.VALIDATE_STATUS: + case this.VALIDATE_STATUS: return !!super.getProgramParameter(program._, pname) - case gl.ATTACHED_SHADERS: - case gl.ACTIVE_ATTRIBUTES: - case gl.ACTIVE_UNIFORMS: + case this.ATTACHED_SHADERS: + case this.ACTIVE_ATTRIBUTES: + case this.ACTIVE_UNIFORMS: return super.getProgramParameter(program._, pname) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } return null } @@ -1740,32 +1795,32 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { getRenderbufferParameter (target, pname) { target |= 0 pname |= 0 - if (target !== gl.RENDERBUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.RENDERBUFFER) { + this.setError(this.INVALID_ENUM) return null } const renderbuffer = this._activeRenderbuffer if (!renderbuffer) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return null } switch (pname) { - case gl.RENDERBUFFER_INTERNAL_FORMAT: + case this.RENDERBUFFER_INTERNAL_FORMAT: return renderbuffer._format - case gl.RENDERBUFFER_WIDTH: + case this.RENDERBUFFER_WIDTH: return renderbuffer._width - case gl.RENDERBUFFER_HEIGHT: + case this.RENDERBUFFER_HEIGHT: return renderbuffer._height - case gl.RENDERBUFFER_SIZE: - case gl.RENDERBUFFER_RED_SIZE: - case gl.RENDERBUFFER_GREEN_SIZE: - case gl.RENDERBUFFER_BLUE_SIZE: - case gl.RENDERBUFFER_ALPHA_SIZE: - case gl.RENDERBUFFER_DEPTH_SIZE: - case gl.RENDERBUFFER_STENCIL_SIZE: + case this.RENDERBUFFER_SIZE: + case this.RENDERBUFFER_RED_SIZE: + case this.RENDERBUFFER_GREEN_SIZE: + case this.RENDERBUFFER_BLUE_SIZE: + case this.RENDERBUFFER_ALPHA_SIZE: + case this.RENDERBUFFER_DEPTH_SIZE: + case this.RENDERBUFFER_STENCIL_SIZE: return super.getRenderbufferParameter(target, pname) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return null } @@ -1775,14 +1830,14 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('getShaderParameter(WebGLShader, GLenum)') } else if (this._checkWrapper(shader, WebGLShader)) { switch (pname) { - case gl.DELETE_STATUS: + case this.DELETE_STATUS: return shader._pendingDelete - case gl.COMPILE_STATUS: + case this.COMPILE_STATUS: return shader._compileStatus - case gl.SHADER_TYPE: + case this.SHADER_TYPE: return shader._type } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } return null } @@ -1814,17 +1869,17 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } const unit = this._getActiveTextureUnit() - if ((target === gl.TEXTURE_2D && !unit._bind2D) || - (target === gl.TEXTURE_CUBE_MAP && !unit._bindCube)) { - this.setError(gl.INVALID_OPERATION) + if ((target === this.TEXTURE_2D && !unit._bind2D) || + (target === this.TEXTURE_CUBE_MAP && !unit._bindCube)) { + this.setError(this.INVALID_OPERATION) return null } switch (pname) { - case gl.TEXTURE_MAG_FILTER: - case gl.TEXTURE_MIN_FILTER: - case gl.TEXTURE_WRAP_S: - case gl.TEXTURE_WRAP_T: + case this.TEXTURE_MAG_FILTER: + case this.TEXTURE_MIN_FILTER: + case this.TEXTURE_WRAP_S: + case this.TEXTURE_WRAP_T: return super.getTexParameter(target, pname) } @@ -1832,7 +1887,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return super.getTexParameter(target, pname) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return null } @@ -1841,13 +1896,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { !checkObject(location)) { throw new TypeError('getUniform(WebGLProgram, WebGLUniformLocation)') } else if (!program) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return null } else if (!location) { return null } else if (this._checkWrapper(program, WebGLProgram)) { if (!checkUniform(program, location)) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return null } const data = super.getUniform(program._ | 0, location._ | 0) @@ -1855,38 +1910,38 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return null } switch (location._activeInfo.type) { - case gl.FLOAT: + case this.FLOAT: return data[0] - case gl.FLOAT_VEC2: + case this.FLOAT_VEC2: return new Float32Array(data.slice(0, 2)) - case gl.FLOAT_VEC3: + case this.FLOAT_VEC3: return new Float32Array(data.slice(0, 3)) - case gl.FLOAT_VEC4: + case this.FLOAT_VEC4: return new Float32Array(data.slice(0, 4)) - case gl.INT: + case this.INT: return data[0] | 0 - case gl.INT_VEC2: + case this.INT_VEC2: return new Int32Array(data.slice(0, 2)) - case gl.INT_VEC3: + case this.INT_VEC3: return new Int32Array(data.slice(0, 3)) - case gl.INT_VEC4: + case this.INT_VEC4: return new Int32Array(data.slice(0, 4)) - case gl.BOOL: + case this.BOOL: return !!data[0] - case gl.BOOL_VEC2: + case this.BOOL_VEC2: return [!!data[0], !!data[1]] - case gl.BOOL_VEC3: + case this.BOOL_VEC3: return [!!data[0], !!data[1], !!data[2]] - case gl.BOOL_VEC4: + case this.BOOL_VEC4: return [!!data[0], !!data[1], !!data[2], !!data[3]] - case gl.FLOAT_MAT2: + case this.FLOAT_MAT2: return new Float32Array(data.slice(0, 4)) - case gl.FLOAT_MAT3: + case this.FLOAT_MAT3: return new Float32Array(data.slice(0, 9)) - case gl.FLOAT_MAT4: + case this.FLOAT_MAT4: return new Float32Array(data.slice(0, 16)) - case gl.SAMPLER_2D: - case gl.SAMPLER_CUBE: + case this.SAMPLER_2D: + case this.SAMPLER_CUBE: return data[0] | 0 default: return null @@ -1902,7 +1957,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { name += '' if (!isValidString(name)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -1944,16 +1999,16 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { // } this._saveError() - for (let i = 0; this.getError() === gl.NO_ERROR; ++i) { + for (let i = 0; this.getError() === this.NO_ERROR; ++i) { const xloc = super.getUniformLocation( program._ | 0, baseName + '[' + i + ']') - if (this.getError() !== gl.NO_ERROR || xloc < 0) { + if (this.getError() !== this.NO_ERROR || xloc < 0) { break } arrayLocs.push(xloc) } - this._restoreError(gl.NO_ERROR) + this._restoreError(this.NO_ERROR) result._array = arrayLocs } else if (/\[(\d+)\]$/.test(name)) { @@ -1972,26 +2027,26 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { index |= 0 pname |= 0 if (index < 0 || index >= this._vertexObjectState._attribs.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return null } const attrib = this._vertexObjectState._attribs[index] const vertexAttribValue = this._vertexGlobalState._attribs[index]._data switch (pname) { - case gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + case this.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return attrib._pointerBuffer - case gl.VERTEX_ATTRIB_ARRAY_ENABLED: + case this.VERTEX_ATTRIB_ARRAY_ENABLED: return attrib._isPointer - case gl.VERTEX_ATTRIB_ARRAY_SIZE: + case this.VERTEX_ATTRIB_ARRAY_SIZE: return attrib._inputSize - case gl.VERTEX_ATTRIB_ARRAY_STRIDE: + case this.VERTEX_ATTRIB_ARRAY_STRIDE: return attrib._inputStride - case gl.VERTEX_ATTRIB_ARRAY_TYPE: + case this.VERTEX_ATTRIB_ARRAY_TYPE: return attrib._pointerType - case gl.VERTEX_ATTRIB_ARRAY_NORMALIZED: + case this.VERTEX_ATTRIB_ARRAY_NORMALIZED: return attrib._pointerNormal - case gl.CURRENT_VERTEX_ATTRIB: + case this.CURRENT_VERTEX_ATTRIB: return new Float32Array(vertexAttribValue) default: return super.getVertexAttrib(index, pname) @@ -2002,13 +2057,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { index |= 0 pname |= 0 if (index < 0 || index >= this._vertexObjectState._attribs.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return null } - if (pname === gl.VERTEX_ATTRIB_ARRAY_POINTER) { + if (pname === this.VERTEX_ATTRIB_ARRAY_POINTER) { return this._vertexObjectState._attribs[index]._pointerOffset } else { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return null } } @@ -2018,19 +2073,19 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { mode |= 0 if (!( - target === gl.GENERATE_MIPMAP_HINT || + target === this.GENERATE_MIPMAP_HINT || ( this._extensions.oes_standard_derivatives && target === this._extensions.oes_standard_derivatives.FRAGMENT_SHADER_DERIVATIVE_HINT_OES ) )) { - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) return } - if (mode !== gl.FASTEST && - mode !== gl.NICEST && - mode !== gl.DONT_CARE) { - this.setError(gl.INVALID_ENUM) + if (mode !== this.FASTEST && + mode !== this.NICEST && + mode !== this.DONT_CARE) { + this.setError(this.INVALID_ENUM) return } @@ -2067,13 +2122,18 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return super.isTexture(object._ | 0) } + isVertexArray (object) { + if (!this._isObject(object, 'isVertexArray', WebGLVertexArrayObject)) return false + return super.isVertexArray(object._ | 0) + } + isEnabled (cap) { return super.isEnabled(cap | 0) } lineWidth (width) { if (isNaN(width)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } return super.lineWidth(+width) @@ -2089,7 +2149,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const prevError = this.getError() super.linkProgram(program._ | 0) const error = this.getError() - if (error === gl.NO_ERROR) { + if (error === this.NO_ERROR) { program._linkStatus = this._fixupLink(program) } this.getError() @@ -2100,29 +2160,29 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { pixelStorei (pname, param) { pname |= 0 param |= 0 - if (pname === gl.UNPACK_ALIGNMENT) { + if (pname === this.UNPACK_ALIGNMENT) { if (param === 1 || param === 2 || param === 4 || param === 8) { this._unpackAlignment = param } else { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } - } else if (pname === gl.PACK_ALIGNMENT) { + } else if (pname === this.PACK_ALIGNMENT) { if (param === 1 || param === 2 || param === 4 || param === 8) { this._packAlignment = param } else { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } - } else if (pname === gl.UNPACK_COLORSPACE_CONVERSION_WEBGL) { - if (!(param === gl.NONE || param === gl.BROWSER_DEFAULT_WEBGL)) { - this.setError(gl.INVALID_VALUE) + } else if (pname === this.UNPACK_COLORSPACE_CONVERSION_WEBGL) { + if (!(param === this.NONE || param === this.BROWSER_DEFAULT_WEBGL)) { + this.setError(this.INVALID_VALUE) return } } @@ -2159,14 +2219,14 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { width |= 0 height |= 0 - if (target !== gl.RENDERBUFFER) { - this.setError(gl.INVALID_ENUM) + if (target !== this.RENDERBUFFER) { + this.setError(this.INVALID_ENUM) return } const renderbuffer = this._activeRenderbuffer if (!renderbuffer) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -2178,7 +2238,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { height) const error = this.getError() this._restoreError(error) - if (error !== gl.NO_ERROR) { + if (error !== this.NO_ERROR) { return } @@ -2213,12 +2273,12 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('shaderSource(WebGLShader, String)') } if (!shader || (!source && typeof source !== 'string')) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } source += '' if (!isValidString(source)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) } else if (this._checkWrapper(shader, WebGLShader)) { super.shaderSource(shader._ | 0, this._wrapShader(shader._type, source)) // eslint-disable-line shader._source = source @@ -2296,8 +2356,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { // Note: there's an ANGLE bug where it doesn't check for setting texImage2D on texture zero in WebGL compat. if (this._getActiveTexture(target) === null) { - if (target === gl.TEXTURE_2D || target === gl.TEXTURE_CUBE_MAP) { - this.setError(gl.INVALID_OPERATION) + if (target === this.TEXTURE_2D || target === this.TEXTURE_CUBE_MAP) { + this.setError(this.INVALID_OPERATION) return } } @@ -2318,7 +2378,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { data) const error = this.getError() this._restoreError(error) - if (error === gl.NO_ERROR) { + if (error === this.NO_ERROR) { const texture = this._getTexImage(target) texture._format = format texture._type = type @@ -2377,10 +2437,10 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (this._checkTextureTarget(target)) { this._verifyTextureCompleteness(target, pname, param) switch (pname) { - case gl.TEXTURE_MIN_FILTER: - case gl.TEXTURE_MAG_FILTER: - case gl.TEXTURE_WRAP_S: - case gl.TEXTURE_WRAP_T: + case this.TEXTURE_MIN_FILTER: + case this.TEXTURE_MAG_FILTER: + case this.TEXTURE_WRAP_S: + case this.TEXTURE_WRAP_T: return super.texParameterf(target, pname, param) } @@ -2388,7 +2448,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return super.texParameterf(target, pname, param) } - this.setError(gl.INVALID_ENUM) + this.setError(this.INVALID_ENUM) } } @@ -2425,7 +2485,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (this._checkWrapper(program, WebGLProgram)) { super.validateProgram(program._ | 0) const error = this.getError() - if (error === gl.NO_ERROR) { + if (error === this.NO_ERROR) { program._linkInfoLog = super.getProgramInfoLog(program._ | 0) } this.getError() @@ -2441,7 +2501,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { stride, offset) { if (stride < 0 || offset < 0) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } @@ -2456,33 +2516,33 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { offset < 0 || index < 0 || index >= this._vertexObjectState._attribs.length || !(size === 1 || size === 2 || size === 3 || size === 4)) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } if (this._vertexGlobalState._arrayBufferBinding === null) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } // fixed, int and unsigned int aren't allowed in WebGL const byteSize = typeSize(type) if (byteSize === 0 || - type === gl.INT || - type === gl.UNSIGNED_INT) { - this.setError(gl.INVALID_ENUM) + type === this.INT || + type === this.UNSIGNED_INT) { + this.setError(this.INVALID_ENUM) return } if (stride > 255 || stride < 0) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } // stride and offset must be multiples of size if ((stride % byteSize) !== 0 || (offset % byteSize) !== 0) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } @@ -2535,22 +2595,22 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return false } else if (this._checkLocationActive(location)) { const utype = location._activeInfo.type - if (utype === gl.SAMPLER_2D || utype === gl.SAMPLER_CUBE) { + if (utype === this.SAMPLER_2D || utype === this.SAMPLER_CUBE) { if (count !== 1) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return } if (type !== 'i') { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } if (v0 < 0 || v0 >= this._textureUnits.length) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } } if (uniformTypeSize(utype) > count) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } return true @@ -2569,7 +2629,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (typeof value !== 'object' || !value || typeof value.length !== 'number') { throw new TypeError(`Second argument to ${name} must be array`) } else if (uniformTypeSize(location._activeInfo.type) > count) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } else if (value.length >= count && value.length % count === 0) { if (location._array) { @@ -2577,11 +2637,11 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (value.length === count) { return true } else { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return false } } - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } @@ -2738,7 +2798,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { value === null || !value.length || value.length % count * count !== 0) { - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } if (!location) { @@ -2753,7 +2813,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } else if (location._array) { return true } - this.setError(gl.INVALID_VALUE) + this.setError(this.INVALID_VALUE) return false } @@ -2829,7 +2889,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { vertexAttrib1fv (index, value) { if (typeof value !== 'object' || value === null || value.length < 1) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } const data = this._vertexGlobalState._attribs[index]._data @@ -2842,7 +2902,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { vertexAttrib2fv (index, value) { if (typeof value !== 'object' || value === null || value.length < 2) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } const data = this._vertexGlobalState._attribs[index]._data @@ -2855,7 +2915,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { vertexAttrib3fv (index, value) { if (typeof value !== 'object' || value === null || value.length < 3) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } const data = this._vertexGlobalState._attribs[index]._data @@ -2868,7 +2928,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { vertexAttrib4fv (index, value) { if (typeof value !== 'object' || value === null || value.length < 4) { - this.setError(gl.INVALID_OPERATION) + this.setError(this.INVALID_OPERATION) return } const data = this._vertexGlobalState._attribs[index]._data @@ -2878,6 +2938,10 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { data[0] = value[0] return super.vertexAttrib4f(index | 0, +value[0], +value[1], +value[2], +value[3]) } + + _isWebGL2 () { + return this.TEXTURE_2D_ARRAY !== undefined + } } // Make the gl consts available as static properties @@ -2885,7 +2949,11 @@ for (const [key, value] of Object.entries(gl)) { if (typeof value !== 'number') { continue } - Object.assign(WebGLRenderingContext, { [key]: value }) + Object.assign(WebGLRenderingContextHelper, { [key]: value }) } -module.exports = { WebGLRenderingContext, wrapContext } +class WebGLRenderingContext extends WebGLRenderingContextHelper {} + +class WebGL2RenderingContext extends WebGLRenderingContextHelper {} + +module.exports = { WebGLRenderingContext, WebGL2RenderingContext, wrapContext } diff --git a/src/javascript/webgl-texture-unit.js b/src/javascript/webgl-texture-unit.js index 0f94effb..7db26574 100644 --- a/src/javascript/webgl-texture-unit.js +++ b/src/javascript/webgl-texture-unit.js @@ -5,6 +5,10 @@ class WebGLTextureUnit { this._mode = 0 this._bind2D = null this._bindCube = null + if (ctx._isWebGL2()) { + this._bind2DArray = null + this._bind3D = null + } } } diff --git a/src/javascript/webgl-vertex-array-object.js b/src/javascript/webgl-vertex-array-object.js new file mode 100644 index 00000000..a1471ddd --- /dev/null +++ b/src/javascript/webgl-vertex-array-object.js @@ -0,0 +1,22 @@ +const { Linkable } = require('./linkable') +const { gl } = require('./native-gl') +const { WebGLVertexArrayObjectState } = require('./webgl-vertex-attribute') + +class WebGLVertexArrayObject extends Linkable { + constructor (_, ctx) { + super(_) + this._ctx = ctx + this._vertexState = new WebGLVertexArrayObjectState(ctx) + } + + _performDelete () { + // Clean up the vertex state to release references to buffers. + this._vertexState.cleanUp() + + delete this._vertexState + delete this._ctx._vaos[this._] + gl.deleteVertexArray.call(this._ctx, this._ | 0) + } +} + +module.exports = { WebGLVertexArrayObject } diff --git a/src/native/bindings.cc b/src/native/bindings.cc index edf30109..b5491821 100644 --- a/src/native/bindings.cc +++ b/src/native/bindings.cc @@ -18,6 +18,11 @@ Nan::Persistent WEBGL_TEMPLATE; #define JS_GL_CONSTANT(name) JS_CONSTANT(name, GL_##name) +#define JS_SET_CONSTANT(name, v) \ + Nan::Set(info.This(), Nan::New(#name).ToLocalChecked(), Nan::New(v)) + +#define JS_SET_GL_CONSTANT(name) JS_SET_CONSTANT(name, GL_##name) + NAN_MODULE_INIT(Init) { v8::Local webgl_template = Nan::New(WebGLRenderingContext::New); @@ -158,6 +163,96 @@ NAN_MODULE_INIT(Init) { JS_GL_METHOD("isVertexArrayOES", IsVertexArrayOES); JS_GL_METHOD("bindVertexArrayOES", BindVertexArrayOES); + // WebGL 2.0 functions: + JS_GL_METHOD("copyBufferSubData", CopyBufferSubData); + JS_GL_METHOD("getBufferSubData", GetBufferSubData); + JS_GL_METHOD("blitFramebuffer", BlitFramebuffer); + JS_GL_METHOD("framebufferTextureLayer", FramebufferTextureLayer); + JS_GL_METHOD("invalidateFramebuffer", InvalidateFramebuffer); + JS_GL_METHOD("invalidateSubFramebuffer", InvalidateSubFramebuffer); + JS_GL_METHOD("readBuffer", ReadBuffer); + JS_GL_METHOD("getInternalformatParameter", GetInternalformatParameter); + JS_GL_METHOD("renderbufferStorageMultisample", RenderbufferStorageMultisample); + JS_GL_METHOD("texStorage2D", TexStorage2D); + JS_GL_METHOD("texStorage3D", TexStorage3D); + JS_GL_METHOD("texImage3D", TexImage3D); + JS_GL_METHOD("texSubImage3D", TexSubImage3D); + JS_GL_METHOD("copyTexSubImage3D", CopyTexSubImage3D); + JS_GL_METHOD("compressedTexImage3D", CompressedTexImage3D); + JS_GL_METHOD("compressedTexSubImage3D", CompressedTexSubImage3D); + JS_GL_METHOD("getFragDataLocation", GetFragDataLocation); + JS_GL_METHOD("uniform1ui", Uniform1ui); + JS_GL_METHOD("uniform2ui", Uniform2ui); + JS_GL_METHOD("uniform3ui", Uniform3ui); + JS_GL_METHOD("uniform4ui", Uniform4ui); + JS_GL_METHOD("uniform1uiv", Uniform1uiv); + JS_GL_METHOD("uniform2uiv", Uniform2uiv); + JS_GL_METHOD("uniform3uiv", Uniform3uiv); + JS_GL_METHOD("uniform4uiv", Uniform4uiv); + JS_GL_METHOD("uniformMatrix3x2fv", UniformMatrix3x2fv); + JS_GL_METHOD("uniformMatrix4x2fv", UniformMatrix4x2fv); + JS_GL_METHOD("uniformMatrix2x3fv", UniformMatrix2x3fv); + JS_GL_METHOD("uniformMatrix4x3fv", UniformMatrix4x3fv); + JS_GL_METHOD("uniformMatrix2x4fv", UniformMatrix2x4fv); + JS_GL_METHOD("uniformMatrix3x4fv", UniformMatrix3x4fv); + JS_GL_METHOD("vertexAttribI4i", VertexAttribI4i); + JS_GL_METHOD("vertexAttribI4iv", VertexAttribI4iv); + JS_GL_METHOD("vertexAttribI4ui", VertexAttribI4ui); + JS_GL_METHOD("vertexAttribI4uiv", VertexAttribI4uiv); + JS_GL_METHOD("vertexAttribIPointer", VertexAttribIPointer); + JS_GL_METHOD("vertexAttribDivisor", VertexAttribDivisor); + JS_GL_METHOD("drawArraysInstanced", DrawArraysInstanced); + JS_GL_METHOD("drawElementsInstanced", DrawElementsInstanced); + JS_GL_METHOD("drawRangeElements", DrawRangeElements); + JS_GL_METHOD("drawBuffers", DrawBuffers); + JS_GL_METHOD("clearBufferfv", ClearBufferfv); + JS_GL_METHOD("clearBufferiv", ClearBufferiv); + JS_GL_METHOD("clearBufferuiv", ClearBufferuiv); + JS_GL_METHOD("clearBufferfi", ClearBufferfi); + JS_GL_METHOD("createQuery", CreateQuery); + JS_GL_METHOD("deleteQuery", DeleteQuery); + JS_GL_METHOD("isQuery", IsQuery); + JS_GL_METHOD("beginQuery", BeginQuery); + JS_GL_METHOD("endQuery", EndQuery); + JS_GL_METHOD("getQuery", GetQuery); + JS_GL_METHOD("getQueryParameter", GetQueryParameter); + JS_GL_METHOD("createSampler", CreateSampler); + JS_GL_METHOD("deleteSampler", DeleteSampler); + JS_GL_METHOD("isSampler", IsSampler); + JS_GL_METHOD("bindSampler", BindSampler); + JS_GL_METHOD("samplerParameteri", SamplerParameteri); + JS_GL_METHOD("samplerParameterf", SamplerParameterf); + JS_GL_METHOD("getSamplerParameter", GetSamplerParameter); + JS_GL_METHOD("fenceSync", FenceSync); + JS_GL_METHOD("isSync", IsSync); + JS_GL_METHOD("deleteSync", DeleteSync); + JS_GL_METHOD("clientWaitSync", ClientWaitSync); + JS_GL_METHOD("waitSync", WaitSync); + JS_GL_METHOD("getSyncParameter", GetSyncParameter); + JS_GL_METHOD("createTransformFeedback", CreateTransformFeedback); + JS_GL_METHOD("deleteTransformFeedback", DeleteTransformFeedback); + JS_GL_METHOD("isTransformFeedback", IsTransformFeedback); + JS_GL_METHOD("bindTransformFeedback", BindTransformFeedback); + JS_GL_METHOD("beginTransformFeedback", BeginTransformFeedback); + JS_GL_METHOD("endTransformFeedback", EndTransformFeedback); + JS_GL_METHOD("transformFeedbackVaryings", TransformFeedbackVaryings); + JS_GL_METHOD("getTransformFeedbackVarying", GetTransformFeedbackVarying); + JS_GL_METHOD("pauseTransformFeedback", PauseTransformFeedback); + JS_GL_METHOD("resumeTransformFeedback", ResumeTransformFeedback); + JS_GL_METHOD("bindBufferBase", BindBufferBase); + JS_GL_METHOD("bindBufferRange", BindBufferRange); + JS_GL_METHOD("getIndexedParameter", GetIndexedParameter); + JS_GL_METHOD("getUniformIndices", GetUniformIndices); + JS_GL_METHOD("getActiveUniforms", GetActiveUniforms); + JS_GL_METHOD("getUniformBlockIndex", GetUniformBlockIndex); + JS_GL_METHOD("getActiveUniformBlockParameter", GetActiveUniformBlockParameter); + JS_GL_METHOD("getActiveUniformBlockName", GetActiveUniformBlockName); + JS_GL_METHOD("uniformBlockBinding", UniformBlockBinding); + JS_GL_METHOD("createVertexArray", CreateVertexArray); + JS_GL_METHOD("deleteVertexArray", DeleteVertexArray); + JS_GL_METHOD("isVertexArray", IsVertexArray); + JS_GL_METHOD("bindVertexArray", BindVertexArray); + // Windows defines a macro called NO_ERROR which messes this up Nan::SetPrototypeTemplate(webgl_template, "NO_ERROR", Nan::New(GL_NO_ERROR)); JS_GL_CONSTANT(INVALID_ENUM); @@ -165,13 +260,9 @@ NAN_MODULE_INIT(Init) { JS_GL_CONSTANT(INVALID_OPERATION); JS_GL_CONSTANT(OUT_OF_MEMORY); - // OpenGL ES 2.1 constants - Nan::SetPrototypeTemplate(webgl_template, "DEPTH_STENCIL", - Nan::New(GL_DEPTH_STENCIL_OES)); - - Nan::SetPrototypeTemplate(webgl_template, "DEPTH_STENCIL_ATTACHMENT", - Nan::New(0x821A)); - + // OpenGL ES 2.0 constants + JS_GL_CONSTANT(DEPTH_STENCIL); + JS_GL_CONSTANT(DEPTH_STENCIL_ATTACHMENT); JS_GL_CONSTANT(MAX_VERTEX_UNIFORM_VECTORS); JS_GL_CONSTANT(MAX_VARYING_VECTORS); JS_GL_CONSTANT(MAX_FRAGMENT_UNIFORM_VECTORS); @@ -475,4 +566,276 @@ NAN_MODULE_INIT(Init) { Nan::Export(target, "setError", WebGLRenderingContext::SetError); } +void BindWebGL2(const Nan::FunctionCallbackInfo &info) { + /* ES3 enums */ + JS_SET_GL_CONSTANT(READ_BUFFER); + JS_SET_GL_CONSTANT(UNPACK_ROW_LENGTH); + JS_SET_GL_CONSTANT(UNPACK_SKIP_ROWS); + JS_SET_GL_CONSTANT(UNPACK_SKIP_PIXELS); + JS_SET_GL_CONSTANT(PACK_ROW_LENGTH); + JS_SET_GL_CONSTANT(PACK_SKIP_ROWS); + JS_SET_GL_CONSTANT(PACK_SKIP_PIXELS); + JS_SET_GL_CONSTANT(COLOR); + JS_SET_GL_CONSTANT(DEPTH); + JS_SET_GL_CONSTANT(STENCIL); + JS_SET_GL_CONSTANT(RED); + JS_SET_GL_CONSTANT(RGB8); + JS_SET_GL_CONSTANT(RGB10_A2); + JS_SET_GL_CONSTANT(TEXTURE_BINDING_3D); + JS_SET_GL_CONSTANT(UNPACK_SKIP_IMAGES); + JS_SET_GL_CONSTANT(UNPACK_IMAGE_HEIGHT); + JS_SET_GL_CONSTANT(TEXTURE_3D); + JS_SET_GL_CONSTANT(TEXTURE_WRAP_R); + JS_SET_GL_CONSTANT(MAX_3D_TEXTURE_SIZE); + JS_SET_GL_CONSTANT(UNSIGNED_INT_2_10_10_10_REV); + JS_SET_GL_CONSTANT(MAX_ELEMENTS_VERTICES); + JS_SET_GL_CONSTANT(MAX_ELEMENTS_INDICES); + JS_SET_GL_CONSTANT(TEXTURE_MIN_LOD); + JS_SET_GL_CONSTANT(TEXTURE_MAX_LOD); + JS_SET_GL_CONSTANT(TEXTURE_BASE_LEVEL); + JS_SET_GL_CONSTANT(TEXTURE_MAX_LEVEL); + JS_SET_GL_CONSTANT(MIN); + JS_SET_GL_CONSTANT(MAX); + JS_SET_GL_CONSTANT(DEPTH_COMPONENT24); + JS_SET_GL_CONSTANT(MAX_TEXTURE_LOD_BIAS); + JS_SET_GL_CONSTANT(TEXTURE_COMPARE_MODE); + JS_SET_GL_CONSTANT(TEXTURE_COMPARE_FUNC); + JS_SET_GL_CONSTANT(CURRENT_QUERY); + JS_SET_GL_CONSTANT(QUERY_RESULT); + JS_SET_GL_CONSTANT(QUERY_RESULT_AVAILABLE); + JS_SET_GL_CONSTANT(STREAM_READ); + JS_SET_GL_CONSTANT(STREAM_COPY); + JS_SET_GL_CONSTANT(STATIC_READ); + JS_SET_GL_CONSTANT(STATIC_COPY); + JS_SET_GL_CONSTANT(DYNAMIC_READ); + JS_SET_GL_CONSTANT(DYNAMIC_COPY); + JS_SET_GL_CONSTANT(MAX_DRAW_BUFFERS); + JS_SET_GL_CONSTANT(DRAW_BUFFER0); + JS_SET_GL_CONSTANT(DRAW_BUFFER1); + JS_SET_GL_CONSTANT(DRAW_BUFFER2); + JS_SET_GL_CONSTANT(DRAW_BUFFER3); + JS_SET_GL_CONSTANT(DRAW_BUFFER4); + JS_SET_GL_CONSTANT(DRAW_BUFFER5); + JS_SET_GL_CONSTANT(DRAW_BUFFER6); + JS_SET_GL_CONSTANT(DRAW_BUFFER7); + JS_SET_GL_CONSTANT(DRAW_BUFFER8); + JS_SET_GL_CONSTANT(DRAW_BUFFER9); + JS_SET_GL_CONSTANT(DRAW_BUFFER10); + JS_SET_GL_CONSTANT(DRAW_BUFFER11); + JS_SET_GL_CONSTANT(DRAW_BUFFER12); + JS_SET_GL_CONSTANT(DRAW_BUFFER13); + JS_SET_GL_CONSTANT(DRAW_BUFFER14); + JS_SET_GL_CONSTANT(DRAW_BUFFER15); + JS_SET_GL_CONSTANT(MAX_FRAGMENT_UNIFORM_COMPONENTS); + JS_SET_GL_CONSTANT(MAX_VERTEX_UNIFORM_COMPONENTS); + JS_SET_GL_CONSTANT(SAMPLER_3D); + JS_SET_GL_CONSTANT(SAMPLER_2D_SHADOW); + JS_SET_GL_CONSTANT(FRAGMENT_SHADER_DERIVATIVE_HINT); + JS_SET_GL_CONSTANT(PIXEL_PACK_BUFFER); + JS_SET_GL_CONSTANT(PIXEL_UNPACK_BUFFER); + JS_SET_GL_CONSTANT(PIXEL_PACK_BUFFER_BINDING); + JS_SET_GL_CONSTANT(PIXEL_UNPACK_BUFFER_BINDING); + JS_SET_GL_CONSTANT(FLOAT_MAT2x3); + JS_SET_GL_CONSTANT(FLOAT_MAT2x4); + JS_SET_GL_CONSTANT(FLOAT_MAT3x2); + JS_SET_GL_CONSTANT(FLOAT_MAT3x4); + JS_SET_GL_CONSTANT(FLOAT_MAT4x2); + JS_SET_GL_CONSTANT(FLOAT_MAT4x3); + JS_SET_GL_CONSTANT(SRGB); + JS_SET_GL_CONSTANT(SRGB8); + JS_SET_GL_CONSTANT(SRGB8_ALPHA8); + JS_SET_GL_CONSTANT(COMPARE_REF_TO_TEXTURE); + JS_SET_GL_CONSTANT(RGBA32F); + JS_SET_GL_CONSTANT(RGB32F); + JS_SET_GL_CONSTANT(RGBA16F); + JS_SET_GL_CONSTANT(RGB16F); + JS_SET_GL_CONSTANT(VERTEX_ATTRIB_ARRAY_INTEGER); + JS_SET_GL_CONSTANT(MAX_ARRAY_TEXTURE_LAYERS); + JS_SET_GL_CONSTANT(MIN_PROGRAM_TEXEL_OFFSET); + JS_SET_GL_CONSTANT(MAX_PROGRAM_TEXEL_OFFSET); + JS_SET_GL_CONSTANT(MAX_VARYING_COMPONENTS); + JS_SET_GL_CONSTANT(TEXTURE_2D_ARRAY); + JS_SET_GL_CONSTANT(TEXTURE_BINDING_2D_ARRAY); + JS_SET_GL_CONSTANT(R11F_G11F_B10F); + JS_SET_GL_CONSTANT(UNSIGNED_INT_10F_11F_11F_REV); + JS_SET_GL_CONSTANT(RGB9_E5); + JS_SET_GL_CONSTANT(UNSIGNED_INT_5_9_9_9_REV); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BUFFER_MODE); + JS_SET_GL_CONSTANT(MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_VARYINGS); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BUFFER_START); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BUFFER_SIZE); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + JS_SET_GL_CONSTANT(RASTERIZER_DISCARD); + JS_SET_GL_CONSTANT(MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS); + JS_SET_GL_CONSTANT(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS); + JS_SET_GL_CONSTANT(INTERLEAVED_ATTRIBS); + JS_SET_GL_CONSTANT(SEPARATE_ATTRIBS); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BUFFER); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BUFFER_BINDING); + JS_SET_GL_CONSTANT(RGBA32UI); + JS_SET_GL_CONSTANT(RGB32UI); + JS_SET_GL_CONSTANT(RGBA16UI); + JS_SET_GL_CONSTANT(RGB16UI); + JS_SET_GL_CONSTANT(RGBA8UI); + JS_SET_GL_CONSTANT(RGB8UI); + JS_SET_GL_CONSTANT(RGBA32I); + JS_SET_GL_CONSTANT(RGB32I); + JS_SET_GL_CONSTANT(RGBA16I); + JS_SET_GL_CONSTANT(RGB16I); + JS_SET_GL_CONSTANT(RGBA8I); + JS_SET_GL_CONSTANT(RGB8I); + JS_SET_GL_CONSTANT(RED_INTEGER); + JS_SET_GL_CONSTANT(RGB_INTEGER); + JS_SET_GL_CONSTANT(RGBA_INTEGER); + JS_SET_GL_CONSTANT(SAMPLER_2D_ARRAY); + JS_SET_GL_CONSTANT(SAMPLER_2D_ARRAY_SHADOW); + JS_SET_GL_CONSTANT(SAMPLER_CUBE_SHADOW); + JS_SET_GL_CONSTANT(UNSIGNED_INT_VEC2); + JS_SET_GL_CONSTANT(UNSIGNED_INT_VEC3); + JS_SET_GL_CONSTANT(UNSIGNED_INT_VEC4); + JS_SET_GL_CONSTANT(INT_SAMPLER_2D); + JS_SET_GL_CONSTANT(INT_SAMPLER_3D); + JS_SET_GL_CONSTANT(INT_SAMPLER_CUBE); + JS_SET_GL_CONSTANT(INT_SAMPLER_2D_ARRAY); + JS_SET_GL_CONSTANT(UNSIGNED_INT_SAMPLER_2D); + JS_SET_GL_CONSTANT(UNSIGNED_INT_SAMPLER_3D); + JS_SET_GL_CONSTANT(UNSIGNED_INT_SAMPLER_CUBE); + JS_SET_GL_CONSTANT(UNSIGNED_INT_SAMPLER_2D_ARRAY); + JS_SET_GL_CONSTANT(DEPTH_COMPONENT32F); + JS_SET_GL_CONSTANT(DEPTH32F_STENCIL8); + JS_SET_GL_CONSTANT(FLOAT_32_UNSIGNED_INT_24_8_REV); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_RED_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_GREEN_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_BLUE_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE); + JS_SET_GL_CONSTANT(FRAMEBUFFER_DEFAULT); + JS_SET_GL_CONSTANT(UNSIGNED_INT_24_8); + JS_SET_GL_CONSTANT(DEPTH24_STENCIL8); + JS_SET_GL_CONSTANT(UNSIGNED_NORMALIZED); + JS_SET_GL_CONSTANT(DRAW_FRAMEBUFFER_BINDING); + JS_SET_GL_CONSTANT(READ_FRAMEBUFFER); + JS_SET_GL_CONSTANT(DRAW_FRAMEBUFFER); + JS_SET_GL_CONSTANT(READ_FRAMEBUFFER_BINDING); + JS_SET_GL_CONSTANT(RENDERBUFFER_SAMPLES); + JS_SET_GL_CONSTANT(FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER); + JS_SET_GL_CONSTANT(MAX_COLOR_ATTACHMENTS); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT1); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT2); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT3); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT4); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT5); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT6); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT7); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT8); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT9); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT10); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT11); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT12); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT13); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT14); + JS_SET_GL_CONSTANT(COLOR_ATTACHMENT15); + JS_SET_GL_CONSTANT(FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); + JS_SET_GL_CONSTANT(MAX_SAMPLES); + JS_SET_GL_CONSTANT(HALF_FLOAT); + JS_SET_GL_CONSTANT(RG); + JS_SET_GL_CONSTANT(RG_INTEGER); + JS_SET_GL_CONSTANT(R8); + JS_SET_GL_CONSTANT(RG8); + JS_SET_GL_CONSTANT(R16F); + JS_SET_GL_CONSTANT(R32F); + JS_SET_GL_CONSTANT(RG16F); + JS_SET_GL_CONSTANT(RG32F); + JS_SET_GL_CONSTANT(R8I); + JS_SET_GL_CONSTANT(R8UI); + JS_SET_GL_CONSTANT(R16I); + JS_SET_GL_CONSTANT(R16UI); + JS_SET_GL_CONSTANT(R32I); + JS_SET_GL_CONSTANT(R32UI); + JS_SET_GL_CONSTANT(RG8I); + JS_SET_GL_CONSTANT(RG8UI); + JS_SET_GL_CONSTANT(RG16I); + JS_SET_GL_CONSTANT(RG16UI); + JS_SET_GL_CONSTANT(RG32I); + JS_SET_GL_CONSTANT(RG32UI); + JS_SET_GL_CONSTANT(VERTEX_ARRAY_BINDING); + JS_SET_GL_CONSTANT(R8_SNORM); + JS_SET_GL_CONSTANT(RG8_SNORM); + JS_SET_GL_CONSTANT(RGB8_SNORM); + JS_SET_GL_CONSTANT(RGBA8_SNORM); + JS_SET_GL_CONSTANT(SIGNED_NORMALIZED); + JS_SET_GL_CONSTANT(COPY_READ_BUFFER); + JS_SET_GL_CONSTANT(COPY_WRITE_BUFFER); + JS_SET_GL_CONSTANT(COPY_READ_BUFFER_BINDING); + JS_SET_GL_CONSTANT(COPY_WRITE_BUFFER_BINDING); + JS_SET_GL_CONSTANT(UNIFORM_BUFFER); + JS_SET_GL_CONSTANT(UNIFORM_BUFFER_BINDING); + JS_SET_GL_CONSTANT(UNIFORM_BUFFER_START); + JS_SET_GL_CONSTANT(UNIFORM_BUFFER_SIZE); + JS_SET_GL_CONSTANT(MAX_VERTEX_UNIFORM_BLOCKS); + JS_SET_GL_CONSTANT(MAX_FRAGMENT_UNIFORM_BLOCKS); + JS_SET_GL_CONSTANT(MAX_COMBINED_UNIFORM_BLOCKS); + JS_SET_GL_CONSTANT(MAX_UNIFORM_BUFFER_BINDINGS); + JS_SET_GL_CONSTANT(MAX_UNIFORM_BLOCK_SIZE); + JS_SET_GL_CONSTANT(MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS); + JS_SET_GL_CONSTANT(MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS); + JS_SET_GL_CONSTANT(UNIFORM_BUFFER_OFFSET_ALIGNMENT); + JS_SET_GL_CONSTANT(ACTIVE_UNIFORM_BLOCKS); + JS_SET_GL_CONSTANT(UNIFORM_TYPE); + JS_SET_GL_CONSTANT(UNIFORM_SIZE); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_INDEX); + JS_SET_GL_CONSTANT(UNIFORM_OFFSET); + JS_SET_GL_CONSTANT(UNIFORM_ARRAY_STRIDE); + JS_SET_GL_CONSTANT(UNIFORM_MATRIX_STRIDE); + JS_SET_GL_CONSTANT(UNIFORM_IS_ROW_MAJOR); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_BINDING); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_DATA_SIZE); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_ACTIVE_UNIFORMS); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER); + JS_SET_GL_CONSTANT(UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER); + JS_SET_GL_CONSTANT(INVALID_INDEX); + JS_SET_GL_CONSTANT(MAX_VERTEX_OUTPUT_COMPONENTS); + JS_SET_GL_CONSTANT(MAX_FRAGMENT_INPUT_COMPONENTS); + JS_SET_GL_CONSTANT(MAX_SERVER_WAIT_TIMEOUT); + JS_SET_GL_CONSTANT(OBJECT_TYPE); + JS_SET_GL_CONSTANT(SYNC_CONDITION); + JS_SET_GL_CONSTANT(SYNC_STATUS); + JS_SET_GL_CONSTANT(SYNC_FLAGS); + JS_SET_GL_CONSTANT(SYNC_FENCE); + JS_SET_GL_CONSTANT(SYNC_GPU_COMMANDS_COMPLETE); + JS_SET_GL_CONSTANT(UNSIGNALED); + JS_SET_GL_CONSTANT(SIGNALED); + JS_SET_GL_CONSTANT(ALREADY_SIGNALED); + JS_SET_GL_CONSTANT(TIMEOUT_EXPIRED); + JS_SET_GL_CONSTANT(CONDITION_SATISFIED); + JS_SET_GL_CONSTANT(WAIT_FAILED); + JS_SET_GL_CONSTANT(SYNC_FLUSH_COMMANDS_BIT); + JS_SET_GL_CONSTANT(VERTEX_ATTRIB_ARRAY_DIVISOR); + JS_SET_GL_CONSTANT(ANY_SAMPLES_PASSED); + JS_SET_GL_CONSTANT(ANY_SAMPLES_PASSED_CONSERVATIVE); + JS_SET_GL_CONSTANT(SAMPLER_BINDING); + JS_SET_GL_CONSTANT(RGB10_A2UI); + JS_SET_GL_CONSTANT(INT_2_10_10_10_REV); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_PAUSED); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_ACTIVE); + JS_SET_GL_CONSTANT(TRANSFORM_FEEDBACK_BINDING); + JS_SET_GL_CONSTANT(TEXTURE_IMMUTABLE_FORMAT); + JS_SET_GL_CONSTANT(MAX_ELEMENT_INDEX); + JS_SET_GL_CONSTANT(TEXTURE_IMMUTABLE_LEVELS); + + JS_SET_CONSTANT(TIMEOUT_IGNORED, -1); + + /* WebGL 2-specific enums */ + JS_SET_CONSTANT(MAX_CLIENT_WAIT_TIMEOUT_WEBGL, 0x9247); + + /* WebGL 2 constant shared with an extension */ + JS_SET_GL_CONSTANT(RGBA8); +} + NODE_MODULE(webgl, Init) diff --git a/src/native/webgl.cc b/src/native/webgl.cc index 3713e822..11a5fd24 100644 --- a/src/native/webgl.cc +++ b/src/native/webgl.cc @@ -160,13 +160,37 @@ WebGLRenderingContext *WebGLRenderingContext::CONTEXT_LIST_HEAD = NULL; return Nan::ThrowError("Invalid GL context"); \ } +bool ContextSupportsExtensions(WebGLRenderingContext *inst, + const std::vector &extensions) { + for (const std::string &extension : extensions) { + if (inst->enabledExtensions.count(extension) == 0 && + inst->requestableExtensions.count(extension) == 0) { + return false; + } + } + return true; +} + +bool CaseInsensitiveCompare(const std::string &a, const std::string &b) { + std::string aLower = a; + std::string bLower = b; + std::transform(aLower.begin(), aLower.end(), aLower.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::transform(bLower.begin(), bLower.end(), bLower.begin(), + [](unsigned char c) { return std::tolower(c); }); + return aLower < bLower; +}; + WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, bool depth, bool stencil, bool antialias, bool premultipliedAlpha, bool preserveDrawingBuffer, bool preferLowPowerToHighPerformance, - bool failIfMajorPerformanceCaveat) + bool failIfMajorPerformanceCaveat, + bool createWebGL2Context) : state(GLCONTEXT_STATE_INIT), unpack_flip_y(false), unpack_premultiply_alpha(false), - unpack_colorspace_conversion(0x9244), unpack_alignment(4), next(NULL), prev(NULL) { + unpack_colorspace_conversion(0x9244), unpack_alignment(4), + webGLToANGLEExtensions(&CaseInsensitiveCompare), next(NULL), prev(NULL) { + if (!eglGetProcAddress) { if (!eglLibrary.open("libEGL")) { errorMessage = "Error opening ANGLE shared library."; @@ -199,6 +223,7 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, } // Set up configuration + EGLint renderableTypeBit = createWebGL2Context ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES3_BIT; EGLint attrib_list[] = {EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RED_SIZE, @@ -214,7 +239,7 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, EGL_STENCIL_SIZE, 8, EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, + renderableTypeBit, EGL_NONE}; EGLint num_config; if (!eglChooseConfig(DISPLAY, attrib_list, &config, 1, &num_config) || num_config != 1) { @@ -225,7 +250,7 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, // Create context EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, - 2, + createWebGL2Context ? 3 : 2, EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE, EGL_TRUE, EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, @@ -290,22 +315,33 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, } // Each WebGL extension maps to one or more required ANGLE extensions. - // Note: WebGL extension names are lowercase. - webGLToANGLEExtensions = { - {"stackgl_destroy_context", {}}, - {"stackgl_resize_drawingbuffer", {}}, - {"angle_instanced_arrays", {"GL_ANGLE_instanced_arrays"}}, - {"oes_element_index_uint", {"GL_OES_element_index_uint"}}, - {"ext_blend_minmax", {"GL_EXT_blend_minmax"}}, - {"oes_standard_derivatives", {"GL_OES_standard_derivatives"}}, - {"oes_texture_float", - {"GL_OES_texture_float", "GL_CHROMIUM_color_buffer_float_rgba", - "GL_CHROMIUM_color_buffer_float_rgb"}}, - {"oes_texture_float_linear", {"GL_OES_texture_float_linear"}}, - {"ext_texture_filter_anisotropic", {"GL_EXT_texture_filter_anisotropic"}}, - {"webgl_draw_buffers", {"GL_EXT_draw_buffers"}}, - {"oes_vertex_array_object", {"GL_OES_vertex_array_object"}}, - {"ext_shader_texture_lod", {"GL_EXT_shader_texture_lod"}}}; + webGLToANGLEExtensions.insert({"STACKGL_destroy_context", {}}); + webGLToANGLEExtensions.insert({"STACKGL_resize_drawingbuffer", {}}); + webGLToANGLEExtensions.insert( + {"EXT_texture_filter_anisotropic", {"GL_EXT_texture_filter_anisotropic"}}); + webGLToANGLEExtensions.insert({"OES_texture_float_linear", {"GL_OES_texture_float_linear"}}); + if (createWebGL2Context) { + webGLToANGLEExtensions.insert({"EXT_color_buffer_float", {"GL_EXT_color_buffer_float"}}); + } else { + webGLToANGLEExtensions.insert({"ANGLE_instanced_arrays", {"GL_ANGLE_instanced_arrays"}}); + webGLToANGLEExtensions.insert({"OES_element_index_uint", {"GL_OES_element_index_uint"}}); + webGLToANGLEExtensions.insert({"EXT_blend_minmax", {"GL_EXT_blend_minmax"}}); + webGLToANGLEExtensions.insert({"OES_standard_derivatives", {"GL_OES_standard_derivatives"}}); + webGLToANGLEExtensions.insert({"OES_texture_float", + {"GL_OES_texture_float", "GL_CHROMIUM_color_buffer_float_rgba", + "GL_CHROMIUM_color_buffer_float_rgb"}}); + webGLToANGLEExtensions.insert({"WEBGL_draw_buffers", {"GL_EXT_draw_buffers"}}); + webGLToANGLEExtensions.insert({"OES_vertex_array_object", {"GL_OES_vertex_array_object"}}); + webGLToANGLEExtensions.insert({"EXT_shader_texture_lod", {"GL_EXT_shader_texture_lod"}}); + } + + for (const auto &iter : webGLToANGLEExtensions) { + const std::string &webGLExtension = iter.first; + const std::vector &angleExtensions = iter.second; + if (ContextSupportsExtensions(this, angleExtensions)) { + supportedWebGLExtensions.insert(webGLExtension); + } + } } bool WebGLRenderingContext::setActive() { @@ -409,17 +445,20 @@ GL_METHOD(DisposeAll) { GL_METHOD(New) { Nan::HandleScope(); + bool createWebGL2Context = Nan::To(info[10]).ToChecked(); + WebGLRenderingContext *instance = new WebGLRenderingContext(Nan::To(info[0]).ToChecked(), // Width Nan::To(info[1]).ToChecked(), // Height - (Nan::To(info[2]).ToChecked()), // Alpha - (Nan::To(info[3]).ToChecked()), // Depth - (Nan::To(info[4]).ToChecked()), // Stencil - (Nan::To(info[5]).ToChecked()), // antialias - (Nan::To(info[6]).ToChecked()), // premultipliedAlpha - (Nan::To(info[7]).ToChecked()), // preserve drawing buffer - (Nan::To(info[8]).ToChecked()), // low power - (Nan::To(info[9]).ToChecked())); // fail if crap + Nan::To(info[2]).ToChecked(), // Alpha + Nan::To(info[3]).ToChecked(), // Depth + Nan::To(info[4]).ToChecked(), // Stencil + Nan::To(info[5]).ToChecked(), // antialias + Nan::To(info[6]).ToChecked(), // premultipliedAlpha + Nan::To(info[7]).ToChecked(), // preserve drawing buffer + Nan::To(info[8]).ToChecked(), // low power + Nan::To(info[9]).ToChecked(), // fail if crap + createWebGL2Context); if (instance->state != GLCONTEXT_STATE_OK) { if (!instance->errorMessage.empty()) { @@ -432,6 +471,10 @@ GL_METHOD(New) { instance->Wrap(info.This()); + if (createWebGL2Context) { + BindWebGL2(info); + } + info.GetReturnValue().Set(info.This()); } @@ -857,7 +900,7 @@ GL_METHOD(Enable) { GL_METHOD(CreateTexture) { GL_BOILERPLATE; - GLuint texture; + GLuint texture = 0; glGenTextures(1, &texture); inst->registerGLObj(GLOBJECT_TYPE_TEXTURE, texture); @@ -2070,10 +2113,7 @@ GL_METHOD(GetVertexAttrib) { GL_METHOD(GetSupportedExtensions) { GL_BOILERPLATE; - std::set extensionsSet = inst->enabledExtensions; - extensionsSet.insert(inst->requestableExtensions.begin(), inst->requestableExtensions.end()); - - std::string extensions = JoinStringSet(extensionsSet); + std::string extensions = JoinStringSet(inst->supportedWebGLExtensions); v8::Local exts = Nan::New(extensions).ToLocalChecked(); @@ -2238,3 +2278,871 @@ GL_METHOD(IsVertexArrayOES) { info.GetReturnValue().Set( Nan::New(glIsVertexArrayOES(Nan::To(info[0]).ToChecked()) != 0)); } + +GL_METHOD(CopyBufferSubData) { + GL_BOILERPLATE; + GLenum readTarget = Nan::To(info[0]).ToChecked(); + GLenum writeTarget = Nan::To(info[1]).ToChecked(); + GLintptr readOffset = Nan::To(info[2]).ToChecked(); + GLintptr writeOffset = Nan::To(info[3]).ToChecked(); + GLsizeiptr size = Nan::To(info[4]).ToChecked(); + glCopyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); +} + +GL_METHOD(GetBufferSubData) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLintptr srcByteOffset = Nan::To(info[1]).ToChecked(); + auto buffer = info[2].As(); + void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data(); + GLsizeiptr bufferSize = buffer->ByteLength(); + // TODO: glGetBufferSubData(target, srcByteOffset, bufferSize, bufferPtr); +} + +GL_METHOD(BlitFramebuffer) { + GL_BOILERPLATE; + GLint srcX0 = Nan::To(info[0]).ToChecked(); + GLint srcY0 = Nan::To(info[1]).ToChecked(); + GLint srcX1 = Nan::To(info[2]).ToChecked(); + GLint srcY1 = Nan::To(info[3]).ToChecked(); + GLint dstX0 = Nan::To(info[4]).ToChecked(); + GLint dstY0 = Nan::To(info[5]).ToChecked(); + GLint dstX1 = Nan::To(info[6]).ToChecked(); + GLint dstY1 = Nan::To(info[7]).ToChecked(); + GLbitfield mask = Nan::To(info[8]).ToChecked(); + GLenum filter = Nan::To(info[9]).ToChecked(); + glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} + +GL_METHOD(FramebufferTextureLayer) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLenum attachment = Nan::To(info[1]).ToChecked(); + GLuint texture = Nan::To(info[2]).ToChecked(); + GLint level = Nan::To(info[3]).ToChecked(); + GLint layer = Nan::To(info[4]).ToChecked(); + glFramebufferTextureLayer(target, attachment, texture, level, layer); +} + +GL_METHOD(InvalidateFramebuffer) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + auto attachments = info[1].As(); + GLsizei count = attachments->Length(); + GLenum *attachmentList = new GLenum[count]; + for (GLsizei i = 0; i < count; i++) + attachmentList[i] = Nan::To(Nan::Get(attachments, i).ToLocalChecked()).ToChecked(); + glInvalidateFramebuffer(target, count, attachmentList); + delete[] attachmentList; +} + +GL_METHOD(InvalidateSubFramebuffer) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + auto attachments = info[1].As(); + GLsizei count = attachments->Length(); + GLenum *attachmentList = new GLenum[count]; + for (GLsizei i = 0; i < count; i++) + attachmentList[i] = Nan::To(Nan::Get(attachments, i).ToLocalChecked()).ToChecked(); + GLint x = Nan::To(info[2]).ToChecked(); + GLint y = Nan::To(info[3]).ToChecked(); + GLsizei width = Nan::To(info[4]).ToChecked(); + GLsizei height = Nan::To(info[5]).ToChecked(); + glInvalidateSubFramebuffer(target, count, attachmentList, x, y, width, height); + delete[] attachmentList; +} + +GL_METHOD(ReadBuffer) { + GL_BOILERPLATE; + GLenum src = OverrideDrawBufferEnum(Nan::To(info[0]).ToChecked()); + glReadBuffer(src); +} + +GL_METHOD(GetInternalformatParameter) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLenum internalformat = Nan::To(info[1]).ToChecked(); + GLenum pname = Nan::To(info[2]).ToChecked(); + GLint result; + glGetInternalformativ(target, internalformat, pname, 1, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(RenderbufferStorageMultisample) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLsizei samples = Nan::To(info[1]).ToChecked(); + GLenum internalformat = Nan::To(info[2]).ToChecked(); + GLsizei width = Nan::To(info[3]).ToChecked(); + GLsizei height = Nan::To(info[4]).ToChecked(); + glRenderbufferStorageMultisample(target, samples, internalformat, width, height); +} + +GL_METHOD(TexStorage2D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLsizei levels = Nan::To(info[1]).ToChecked(); + GLenum internalformat = Nan::To(info[2]).ToChecked(); + GLsizei width = Nan::To(info[3]).ToChecked(); + GLsizei height = Nan::To(info[4]).ToChecked(); + glTexStorage2D(target, levels, internalformat, width, height); +} + +GL_METHOD(TexStorage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLsizei levels = Nan::To(info[1]).ToChecked(); + GLenum internalformat = Nan::To(info[2]).ToChecked(); + GLsizei width = Nan::To(info[3]).ToChecked(); + GLsizei height = Nan::To(info[4]).ToChecked(); + GLsizei depth = Nan::To(info[5]).ToChecked(); + glTexStorage3D(target, levels, internalformat, width, height, depth); +} + +GL_METHOD(TexImage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLint level = Nan::To(info[1]).ToChecked(); + GLint internalformat = Nan::To(info[2]).ToChecked(); + GLsizei width = Nan::To(info[3]).ToChecked(); + GLsizei height = Nan::To(info[4]).ToChecked(); + GLsizei depth = Nan::To(info[5]).ToChecked(); + GLint border = Nan::To(info[6]).ToChecked(); + GLenum format = Nan::To(info[7]).ToChecked(); + GLenum type = Nan::To(info[8]).ToChecked(); + if (info[9]->IsUndefined()) { + glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, + nullptr); + } else if (info[9]->IsArrayBufferView()) { + auto buffer = info[9].As(); + void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data(); + glTexImage3D(target, level, internalformat, width, height, depth, border, format, type, + bufferPtr); + } else { + Nan::ThrowTypeError("Invalid data type for TexImage3D"); + } +} + +GL_METHOD(TexSubImage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLint level = Nan::To(info[1]).ToChecked(); + GLint xoffset = Nan::To(info[2]).ToChecked(); + GLint yoffset = Nan::To(info[3]).ToChecked(); + GLint zoffset = Nan::To(info[4]).ToChecked(); + GLsizei width = Nan::To(info[5]).ToChecked(); + GLsizei height = Nan::To(info[6]).ToChecked(); + GLsizei depth = Nan::To(info[7]).ToChecked(); + GLenum format = Nan::To(info[8]).ToChecked(); + GLenum type = Nan::To(info[9]).ToChecked(); + if (info[10]->IsUndefined()) { + glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, + nullptr); + } else if (info[10]->IsArrayBufferView()) { + auto buffer = info[10].As(); + void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data(); + glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, + bufferPtr); + } else { + Nan::ThrowTypeError("Invalid data type for TexSubImage3D"); + } +} + +GL_METHOD(CopyTexSubImage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLint level = Nan::To(info[1]).ToChecked(); + GLint xoffset = Nan::To(info[2]).ToChecked(); + GLint yoffset = Nan::To(info[3]).ToChecked(); + GLint zoffset = Nan::To(info[4]).ToChecked(); + GLint x = Nan::To(info[5]).ToChecked(); + GLint y = Nan::To(info[6]).ToChecked(); + GLsizei width = Nan::To(info[7]).ToChecked(); + GLsizei height = Nan::To(info[8]).ToChecked(); + glCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); +} + +GL_METHOD(CompressedTexImage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLint level = Nan::To(info[1]).ToChecked(); + GLenum internalformat = Nan::To(info[2]).ToChecked(); + GLsizei width = Nan::To(info[3]).ToChecked(); + GLsizei height = Nan::To(info[4]).ToChecked(); + GLsizei depth = Nan::To(info[5]).ToChecked(); + GLint border = Nan::To(info[6]).ToChecked(); + GLsizei imageSize = Nan::To(info[7]).ToChecked(); + if (info[8]->IsUndefined()) { + glCompressedTexImage3D(target, level, internalformat, width, height, depth, border, imageSize, + nullptr); + } else if (info[8]->IsArrayBufferView()) { + auto buffer = info[8].As(); + void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data(); + glCompressedTexImage3D(target, level, internalformat, width, height, depth, border, imageSize, + bufferPtr); + } else { + Nan::ThrowTypeError("Invalid data type for CompressedTexImage3D"); + } +} + +GL_METHOD(CompressedTexSubImage3D) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLint level = Nan::To(info[1]).ToChecked(); + GLint xoffset = Nan::To(info[2]).ToChecked(); + GLint yoffset = Nan::To(info[3]).ToChecked(); + GLint zoffset = Nan::To(info[4]).ToChecked(); + GLsizei width = Nan::To(info[5]).ToChecked(); + GLsizei height = Nan::To(info[6]).ToChecked(); + GLsizei depth = Nan::To(info[7]).ToChecked(); + GLenum format = Nan::To(info[8]).ToChecked(); + GLsizei imageSize = Nan::To(info[9]).ToChecked(); + if (info[10]->IsUndefined()) { + glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, + format, imageSize, nullptr); + } else if (info[10]->IsArrayBufferView()) { + auto buffer = info[10].As(); + void *bufferPtr = buffer->Buffer()->GetBackingStore()->Data(); + glCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, + format, imageSize, bufferPtr); + } else { + Nan::ThrowTypeError("Invalid data type for CompressedTexSubImage3D"); + } +} + +GL_METHOD(GetFragDataLocation) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + Nan::Utf8String name(info[1]); + GLint location = glGetFragDataLocation(program, *name); + info.GetReturnValue().Set(Nan::New(location)); +} + +GL_METHOD(Uniform1ui) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLuint v0 = Nan::To(info[1]).ToChecked(); + glUniform1ui(location, v0); +} + +GL_METHOD(Uniform2ui) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLuint v0 = Nan::To(info[1]).ToChecked(); + GLuint v1 = Nan::To(info[2]).ToChecked(); + glUniform2ui(location, v0, v1); +} + +GL_METHOD(Uniform3ui) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLuint v0 = Nan::To(info[1]).ToChecked(); + GLuint v1 = Nan::To(info[2]).ToChecked(); + GLuint v2 = Nan::To(info[3]).ToChecked(); + glUniform3ui(location, v0, v1, v2); +} + +GL_METHOD(Uniform4ui) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLuint v0 = Nan::To(info[1]).ToChecked(); + GLuint v1 = Nan::To(info[2]).ToChecked(); + GLuint v2 = Nan::To(info[3]).ToChecked(); + GLuint v3 = Nan::To(info[4]).ToChecked(); + glUniform4ui(location, v0, v1, v2, v3); +} + +GL_METHOD(Uniform1uiv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + auto data = info[1].As(); + GLuint *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / sizeof(GLuint); + glUniform1uiv(location, count, bufferPtr); +} + +GL_METHOD(Uniform2uiv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + auto data = info[1].As(); + GLuint *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (2 * sizeof(GLuint)); + glUniform2uiv(location, count, bufferPtr); +} + +GL_METHOD(Uniform3uiv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + auto data = info[1].As(); + GLuint *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (3 * sizeof(GLuint)); + glUniform3uiv(location, count, bufferPtr); +} + +GL_METHOD(Uniform4uiv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + auto data = info[1].As(); + GLuint *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (4 * sizeof(GLuint)); + glUniform4uiv(location, count, bufferPtr); +} + +GL_METHOD(UniformMatrix3x2fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (6 * sizeof(GLfloat)); + glUniformMatrix3x2fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(UniformMatrix4x2fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (8 * sizeof(GLfloat)); + glUniformMatrix4x2fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(UniformMatrix2x3fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (6 * sizeof(GLfloat)); + glUniformMatrix2x3fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(UniformMatrix4x3fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (12 * sizeof(GLfloat)); + glUniformMatrix4x3fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(UniformMatrix2x4fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (8 * sizeof(GLfloat)); + glUniformMatrix2x4fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(UniformMatrix3x4fv) { + GL_BOILERPLATE; + GLuint location = Nan::To(info[0]).ToChecked(); + GLboolean transpose = Nan::To(info[1]).ToChecked(); + auto data = info[2].As(); + GLfloat *bufferPtr = static_cast(data->Buffer()->GetBackingStore()->Data()); + GLsizei count = data->ByteLength() / (12 * sizeof(GLfloat)); + glUniformMatrix3x4fv(location, count, transpose, bufferPtr); +} + +GL_METHOD(VertexAttribI4i) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + GLint x = Nan::To(info[1]).ToChecked(); + GLint y = Nan::To(info[2]).ToChecked(); + GLint z = Nan::To(info[3]).ToChecked(); + GLint w = Nan::To(info[4]).ToChecked(); + glVertexAttribI4i(index, x, y, z, w); +} + +GL_METHOD(VertexAttribI4iv) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + auto values = info[1].As(); + GLint *bufferPtr = static_cast(values->Buffer()->GetBackingStore()->Data()); + glVertexAttribI4iv(index, bufferPtr); +} + +GL_METHOD(VertexAttribI4ui) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + GLuint x = Nan::To(info[1]).ToChecked(); + GLuint y = Nan::To(info[2]).ToChecked(); + GLuint z = Nan::To(info[3]).ToChecked(); + GLuint w = Nan::To(info[4]).ToChecked(); + glVertexAttribI4ui(index, x, y, z, w); +} + +GL_METHOD(VertexAttribI4uiv) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + auto values = info[1].As(); + GLuint *bufferPtr = static_cast(values->Buffer()->GetBackingStore()->Data()); + glVertexAttribI4uiv(index, bufferPtr); +} + +GL_METHOD(VertexAttribIPointer) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + GLint size = Nan::To(info[1]).ToChecked(); + GLenum type = Nan::To(info[2]).ToChecked(); + GLsizei stride = Nan::To(info[3]).ToChecked(); + GLintptr offset = Nan::To(info[4]).ToChecked(); + glVertexAttribIPointer(index, size, type, stride, reinterpret_cast(offset)); +} + +GL_METHOD(VertexAttribDivisor) { + GL_BOILERPLATE; + GLuint index = Nan::To(info[0]).ToChecked(); + GLuint divisor = Nan::To(info[1]).ToChecked(); + glVertexAttribDivisor(index, divisor); +} + +GL_METHOD(DrawArraysInstanced) { + GL_BOILERPLATE; + GLenum mode = Nan::To(info[0]).ToChecked(); + GLint first = Nan::To(info[1]).ToChecked(); + GLsizei count = Nan::To(info[2]).ToChecked(); + GLsizei instanceCount = Nan::To(info[3]).ToChecked(); + glDrawArraysInstanced(mode, first, count, instanceCount); +} + +GL_METHOD(DrawElementsInstanced) { + GL_BOILERPLATE; + GLenum mode = Nan::To(info[0]).ToChecked(); + GLsizei count = Nan::To(info[1]).ToChecked(); + GLenum type = Nan::To(info[2]).ToChecked(); + GLintptr offset = Nan::To(info[3]).ToChecked(); + GLsizei instanceCount = Nan::To(info[4]).ToChecked(); + glDrawElementsInstanced(mode, count, type, reinterpret_cast(offset), instanceCount); +} + +GL_METHOD(DrawRangeElements) { + GL_BOILERPLATE; + GLenum mode = Nan::To(info[0]).ToChecked(); + GLuint start = Nan::To(info[1]).ToChecked(); + GLuint end = Nan::To(info[2]).ToChecked(); + GLsizei count = Nan::To(info[3]).ToChecked(); + GLenum type = Nan::To(info[4]).ToChecked(); + GLintptr offset = Nan::To(info[5]).ToChecked(); + glDrawRangeElements(mode, start, end, count, type, reinterpret_cast(offset)); +} + +GL_METHOD(DrawBuffers) { + GL_BOILERPLATE; + auto buffers = info[0].As(); + GLsizei count = buffers->Length(); + GLenum *bufferList = new GLenum[count]; + for (GLsizei i = 0; i < count; i++) { + GLenum buffer = Nan::To(Nan::Get(buffers, i).ToLocalChecked()).ToChecked(); + bufferList[i] = OverrideDrawBufferEnum(buffer); + } + glDrawBuffers(count, bufferList); + delete[] bufferList; +} + +GL_METHOD(ClearBufferfv) { + GL_BOILERPLATE; + GLenum buffer = Nan::To(info[0]).ToChecked(); + GLint drawbuffer = Nan::To(info[1]).ToChecked(); + auto values = info[2].As(); + GLfloat *bufferPtr = static_cast(values->Buffer()->GetBackingStore()->Data()); + glClearBufferfv(buffer, drawbuffer, bufferPtr); +} + +GL_METHOD(ClearBufferiv) { + GL_BOILERPLATE; + GLenum buffer = Nan::To(info[0]).ToChecked(); + GLint drawbuffer = Nan::To(info[1]).ToChecked(); + auto values = info[2].As(); + GLint *bufferPtr = static_cast(values->Buffer()->GetBackingStore()->Data()); + glClearBufferiv(buffer, drawbuffer, bufferPtr); +} + +GL_METHOD(ClearBufferuiv) { + GL_BOILERPLATE; + GLenum buffer = Nan::To(info[0]).ToChecked(); + GLint drawbuffer = Nan::To(info[1]).ToChecked(); + auto values = info[2].As(); + GLuint *bufferPtr = static_cast(values->Buffer()->GetBackingStore()->Data()); + glClearBufferuiv(buffer, drawbuffer, bufferPtr); +} + +GL_METHOD(ClearBufferfi) { + GL_BOILERPLATE; + GLenum buffer = Nan::To(info[0]).ToChecked(); + GLint drawbuffer = Nan::To(info[1]).ToChecked(); + GLfloat depth = Nan::To(info[2]).ToChecked(); + GLint stencil = Nan::To(info[3]).ToChecked(); + glClearBufferfi(buffer, drawbuffer, depth, stencil); +} + +GL_METHOD(CreateQuery) { + GL_BOILERPLATE; + GLuint query; + glGenQueries(1, &query); + info.GetReturnValue().Set(Nan::New(query)); +} + +GL_METHOD(DeleteQuery) { + GL_BOILERPLATE; + GLuint query = Nan::To(info[0]).ToChecked(); + glDeleteQueries(1, &query); +} + +GL_METHOD(IsQuery) { + GL_BOILERPLATE; + GLuint query = Nan::To(info[0]).ToChecked(); + GLboolean result = glIsQuery(query); + info.GetReturnValue().Set(Nan::New(result != GL_FALSE)); +} + +GL_METHOD(BeginQuery) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLuint query = Nan::To(info[1]).ToChecked(); + glBeginQuery(target, query); +} + +GL_METHOD(EndQuery) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + glEndQuery(target); +} + +GL_METHOD(GetQuery) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLuint result; + glGetQueryiv(target, pname, reinterpret_cast(&result)); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(GetQueryParameter) { + GL_BOILERPLATE; + GLuint query = Nan::To(info[0]).ToChecked(); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLuint result; + glGetQueryObjectuiv(query, pname, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(CreateSampler) { + GL_BOILERPLATE; + GLuint sampler; + glGenSamplers(1, &sampler); + info.GetReturnValue().Set(Nan::New(sampler)); +} + +GL_METHOD(DeleteSampler) { + GL_BOILERPLATE; + GLuint sampler = Nan::To(info[0]).ToChecked(); + glDeleteSamplers(1, &sampler); +} + +GL_METHOD(IsSampler) { + GL_BOILERPLATE; + GLuint sampler = Nan::To(info[0]).ToChecked(); + GLboolean result = glIsSampler(sampler); + info.GetReturnValue().Set(Nan::New(result != GL_FALSE)); +} + +GL_METHOD(BindSampler) { + GL_BOILERPLATE; + GLuint unit = Nan::To(info[0]).ToChecked(); + GLuint sampler = Nan::To(info[1]).ToChecked(); + glBindSampler(unit, sampler); +} + +GL_METHOD(SamplerParameteri) { + GL_BOILERPLATE; + GLuint sampler = Nan::To(info[0]).ToChecked(); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLint param = Nan::To(info[2]).ToChecked(); + glSamplerParameteri(sampler, pname, param); +} + +GL_METHOD(SamplerParameterf) { + GL_BOILERPLATE; + GLuint sampler = Nan::To(info[0]).ToChecked(); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLfloat param = Nan::To(info[2]).ToChecked(); + glSamplerParameterf(sampler, pname, param); +} + +GL_METHOD(GetSamplerParameter) { + GL_BOILERPLATE; + GLuint sampler = Nan::To(info[0]).ToChecked(); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLint result; + glGetSamplerParameteriv(sampler, pname, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +// ANGLE internally stores GLsync values as integer handles, so this is safe on ANGLE. +GLsync IntToSync(uint32_t intValue) { + return reinterpret_cast(static_cast(intValue)); +} + +uint32_t SyncToInt(GLsync sync) { return static_cast(reinterpret_cast(sync)); } + +GL_METHOD(FenceSync) { + GL_BOILERPLATE; + GLenum condition = Nan::To(info[0]).ToChecked(); + GLbitfield flags = Nan::To(info[1]).ToChecked(); + GLsync sync = glFenceSync(condition, flags); + info.GetReturnValue().Set(Nan::New(SyncToInt(sync))); +} + +GL_METHOD(IsSync) { + GL_BOILERPLATE; + GLsync sync = IntToSync(Nan::To(info[0]).ToChecked()); + GLboolean result = glIsSync(sync); + info.GetReturnValue().Set(Nan::New(result != GL_FALSE)); +} + +GL_METHOD(DeleteSync) { + GL_BOILERPLATE; + GLsync sync = IntToSync(Nan::To(info[0]).ToChecked()); + glDeleteSync(sync); +} + +GL_METHOD(ClientWaitSync) { + GL_BOILERPLATE; + GLsync sync = IntToSync(Nan::To(info[0]).ToChecked()); + GLbitfield flags = Nan::To(info[1]).ToChecked(); + GLuint64 timeout = Nan::To(info[2]).ToChecked(); + GLenum result = glClientWaitSync(sync, flags, timeout); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(WaitSync) { + GL_BOILERPLATE; + GLsync sync = IntToSync(Nan::To(info[0]).ToChecked()); + GLbitfield flags = Nan::To(info[1]).ToChecked(); + GLint64 timeout = Nan::To(info[2]).ToChecked(); + glWaitSync(sync, flags, timeout); +} + +GL_METHOD(GetSyncParameter) { + GL_BOILERPLATE; + GLsync sync = IntToSync(Nan::To(info[0]).ToChecked()); + GLenum pname = Nan::To(info[1]).ToChecked(); + GLint result; + glGetSynciv(sync, pname, 1, nullptr, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(CreateTransformFeedback) { + GL_BOILERPLATE; + GLuint tf; + glGenTransformFeedbacks(1, &tf); + info.GetReturnValue().Set(Nan::New(tf)); +} + +GL_METHOD(DeleteTransformFeedback) { + GL_BOILERPLATE; + GLuint tf = Nan::To(info[0]).ToChecked(); + glDeleteTransformFeedbacks(1, &tf); +} + +GL_METHOD(IsTransformFeedback) { + GL_BOILERPLATE; + GLuint tf = Nan::To(info[0]).ToChecked(); + GLboolean result = glIsTransformFeedback(tf); + info.GetReturnValue().Set(Nan::New(result != GL_FALSE)); +} + +GL_METHOD(BindTransformFeedback) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLuint tf = Nan::To(info[1]).ToChecked(); + glBindTransformFeedback(target, tf); +} + +GL_METHOD(BeginTransformFeedback) { + GL_BOILERPLATE; + GLenum primitiveMode = Nan::To(info[0]).ToChecked(); + glBeginTransformFeedback(primitiveMode); +} + +GL_METHOD(EndTransformFeedback) { + GL_BOILERPLATE; + glEndTransformFeedback(); +} + +GL_METHOD(TransformFeedbackVaryings) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + auto varyings = info[1].As(); + GLenum bufferMode = Nan::To(info[2]).ToChecked(); + GLsizei count = varyings->Length(); + const char **varyingStrings = new const char *[count]; + for (GLsizei i = 0; i < count; i++) { + Nan::Utf8String str(Nan::Get(varyings, i).ToLocalChecked()); + varyingStrings[i] = *str; + } + glTransformFeedbackVaryings(program, count, varyingStrings, bufferMode); + delete[] varyingStrings; +} + +GL_METHOD(GetTransformFeedbackVarying) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + GLuint index = Nan::To(info[1]).ToChecked(); + char name[256]; + GLsizei length; + GLsizei size; + GLenum type; + glGetTransformFeedbackVarying(program, index, 256, &length, &size, &type, name); + v8::Local result = Nan::New(); + Nan::Set(result, Nan::New("name").ToLocalChecked(), Nan::New(name).ToLocalChecked()); + Nan::Set(result, Nan::New("size").ToLocalChecked(), Nan::New(size)); + Nan::Set(result, Nan::New("type").ToLocalChecked(), Nan::New(type)); + info.GetReturnValue().Set(result); +} + +GL_METHOD(PauseTransformFeedback) { + GL_BOILERPLATE; + glPauseTransformFeedback(); +} + +GL_METHOD(ResumeTransformFeedback) { + GL_BOILERPLATE; + glResumeTransformFeedback(); +} + +GL_METHOD(BindBufferBase) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLuint index = Nan::To(info[1]).ToChecked(); + GLuint buffer = Nan::To(info[2]).ToChecked(); + glBindBufferBase(target, index, buffer); +} + +GL_METHOD(BindBufferRange) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLuint index = Nan::To(info[1]).ToChecked(); + GLuint buffer = Nan::To(info[2]).ToChecked(); + GLintptr offset = Nan::To(info[3]).ToChecked(); + GLsizeiptr size = Nan::To(info[4]).ToChecked(); + glBindBufferRange(target, index, buffer, offset, size); +} + +GL_METHOD(GetIndexedParameter) { + GL_BOILERPLATE; + GLenum target = Nan::To(info[0]).ToChecked(); + GLuint index = Nan::To(info[1]).ToChecked(); + GLint result; + glGetIntegeri_v(target, index, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(GetUniformIndices) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + auto uniformNames = info[1].As(); + GLsizei count = uniformNames->Length(); + const char **names = new const char *[count]; + for (GLsizei i = 0; i < count; i++) { + Nan::Utf8String name(Nan::Get(uniformNames, i).ToLocalChecked()); + names[i] = *name; + } + GLuint *indices = new GLuint[count]; + glGetUniformIndices(program, count, names, indices); + v8::Local result = Nan::New(count); + for (GLsizei i = 0; i < count; i++) { + Nan::Set(result, i, Nan::New(indices[i])); + } + info.GetReturnValue().Set(result); + delete[] names; + delete[] indices; +} + +GL_METHOD(GetActiveUniforms) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + auto uniformIndices = info[1].As(); + GLsizei count = uniformIndices->Length(); + GLuint *indices = new GLuint[count]; + for (GLsizei i = 0; i < count; i++) { + indices[i] = Nan::To(Nan::Get(uniformIndices, i).ToLocalChecked()).ToChecked(); + } + GLenum pname = Nan::To(info[2]).ToChecked(); + GLint *params = new GLint[count]; + glGetActiveUniformsiv(program, count, indices, pname, params); + v8::Local result = Nan::New(count); + for (GLsizei i = 0; i < count; i++) { + Nan::Set(result, i, Nan::New(params[i])); + } + info.GetReturnValue().Set(result); + delete[] indices; + delete[] params; +} + +GL_METHOD(GetUniformBlockIndex) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + Nan::Utf8String blockName(info[1]); + GLuint index = glGetUniformBlockIndex(program, *blockName); + info.GetReturnValue().Set(Nan::New(index)); +} + +GL_METHOD(GetActiveUniformBlockParameter) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + GLuint uniformBlockIndex = Nan::To(info[1]).ToChecked(); + GLenum pname = Nan::To(info[2]).ToChecked(); + GLint result; + glGetActiveUniformBlockiv(program, uniformBlockIndex, pname, &result); + info.GetReturnValue().Set(Nan::New(result)); +} + +GL_METHOD(GetActiveUniformBlockName) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + GLuint uniformBlockIndex = Nan::To(info[1]).ToChecked(); + char name[256]; + GLsizei length; + glGetActiveUniformBlockName(program, uniformBlockIndex, 256, &length, name); + info.GetReturnValue().Set(Nan::New(name).ToLocalChecked()); +} + +GL_METHOD(UniformBlockBinding) { + GL_BOILERPLATE; + GLuint program = Nan::To(info[0]).ToChecked(); + GLuint uniformBlockIndex = Nan::To(info[1]).ToChecked(); + GLuint uniformBlockBinding = Nan::To(info[2]).ToChecked(); + glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); +} + +GL_METHOD(CreateVertexArray) { + GL_BOILERPLATE; + GLuint vao; + glGenVertexArrays(1, &vao); + info.GetReturnValue().Set(Nan::New(vao)); +} + +GL_METHOD(DeleteVertexArray) { + GL_BOILERPLATE; + GLuint vao = Nan::To(info[0]).ToChecked(); + glDeleteVertexArrays(1, &vao); +} + +GL_METHOD(IsVertexArray) { + GL_BOILERPLATE; + GLuint vao = Nan::To(info[0]).ToChecked(); + GLboolean result = glIsVertexArray(vao); + info.GetReturnValue().Set(Nan::New(result != GL_FALSE)); +} + +GL_METHOD(BindVertexArray) { + GL_BOILERPLATE; + GLuint vao = Nan::To(info[0]).ToChecked(); + glBindVertexArray(vao); +} diff --git a/src/native/webgl.h b/src/native/webgl.h index 70610450..3ee2c8bd 100644 --- a/src/native/webgl.h +++ b/src/native/webgl.h @@ -35,7 +35,11 @@ enum GLContextState { GLCONTEXT_STATE_ERROR }; -typedef std::pair GLObjectReference; +bool CaseInsensitiveCompare(const std::string &a, const std::string &b); + +using GLObjectReference = std::pair; +using WebGLToANGLEExtensionsMap = + std::map, decltype(&CaseInsensitiveCompare)>; struct WebGLRenderingContext : public node::ObjectWrap { @@ -58,7 +62,8 @@ struct WebGLRenderingContext : public node::ObjectWrap { std::set requestableExtensions; std::set enabledExtensions; - std::map> webGLToANGLEExtensions; + std::set supportedWebGLExtensions; + WebGLToANGLEExtensionsMap webGLToANGLEExtensions; // A list of object references, need do destroy them at program exit std::map, bool> objects; @@ -92,7 +97,8 @@ struct WebGLRenderingContext : public node::ObjectWrap { // Constructor WebGLRenderingContext(int width, int height, bool alpha, bool depth, bool stencil, bool antialias, bool premultipliedAlpha, bool preserveDrawingBuffer, - bool preferLowPowerToHighPerformance, bool failIfMajorPerformanceCaveat); + bool preferLowPowerToHighPerformance, bool failIfMajorPerformanceCaveat, + bool createWebGL2Context); virtual ~WebGLRenderingContext(); // Context validation @@ -265,6 +271,98 @@ struct WebGLRenderingContext : public node::ObjectWrap { static NAN_METHOD(CreateVertexArrayOES); static NAN_METHOD(DeleteVertexArrayOES); static NAN_METHOD(IsVertexArrayOES); + + // WebGL 2 methods + static NAN_METHOD(CopyBufferSubData); + static NAN_METHOD(GetBufferSubData); + static NAN_METHOD(BlitFramebuffer); + static NAN_METHOD(FramebufferTextureLayer); + static NAN_METHOD(InvalidateFramebuffer); + static NAN_METHOD(InvalidateSubFramebuffer); + static NAN_METHOD(ReadBuffer); + static NAN_METHOD(GetInternalformatParameter); + static NAN_METHOD(RenderbufferStorageMultisample); + static NAN_METHOD(TexStorage2D); + static NAN_METHOD(TexStorage3D); + static NAN_METHOD(TexImage3D); + static NAN_METHOD(TexSubImage3D); + static NAN_METHOD(CopyTexSubImage3D); + static NAN_METHOD(CompressedTexImage3D); + static NAN_METHOD(CompressedTexSubImage3D); + static NAN_METHOD(GetFragDataLocation); + static NAN_METHOD(Uniform1ui); + static NAN_METHOD(Uniform2ui); + static NAN_METHOD(Uniform3ui); + static NAN_METHOD(Uniform4ui); + static NAN_METHOD(Uniform1uiv); + static NAN_METHOD(Uniform2uiv); + static NAN_METHOD(Uniform3uiv); + static NAN_METHOD(Uniform4uiv); + static NAN_METHOD(UniformMatrix3x2fv); + static NAN_METHOD(UniformMatrix4x2fv); + static NAN_METHOD(UniformMatrix2x3fv); + static NAN_METHOD(UniformMatrix4x3fv); + static NAN_METHOD(UniformMatrix2x4fv); + static NAN_METHOD(UniformMatrix3x4fv); + static NAN_METHOD(VertexAttribI4i); + static NAN_METHOD(VertexAttribI4iv); + static NAN_METHOD(VertexAttribI4ui); + static NAN_METHOD(VertexAttribI4uiv); + static NAN_METHOD(VertexAttribIPointer); + static NAN_METHOD(VertexAttribDivisor); + static NAN_METHOD(DrawArraysInstanced); + static NAN_METHOD(DrawElementsInstanced); + static NAN_METHOD(DrawRangeElements); + static NAN_METHOD(DrawBuffers); + static NAN_METHOD(ClearBufferfv); + static NAN_METHOD(ClearBufferiv); + static NAN_METHOD(ClearBufferuiv); + static NAN_METHOD(ClearBufferfi); + static NAN_METHOD(CreateQuery); + static NAN_METHOD(DeleteQuery); + static NAN_METHOD(IsQuery); + static NAN_METHOD(BeginQuery); + static NAN_METHOD(EndQuery); + static NAN_METHOD(GetQuery); + static NAN_METHOD(GetQueryParameter); + static NAN_METHOD(CreateSampler); + static NAN_METHOD(DeleteSampler); + static NAN_METHOD(IsSampler); + static NAN_METHOD(BindSampler); + static NAN_METHOD(SamplerParameteri); + static NAN_METHOD(SamplerParameterf); + static NAN_METHOD(GetSamplerParameter); + static NAN_METHOD(FenceSync); + static NAN_METHOD(IsSync); + static NAN_METHOD(DeleteSync); + static NAN_METHOD(ClientWaitSync); + static NAN_METHOD(WaitSync); + static NAN_METHOD(GetSyncParameter); + static NAN_METHOD(CreateTransformFeedback); + static NAN_METHOD(DeleteTransformFeedback); + static NAN_METHOD(IsTransformFeedback); + static NAN_METHOD(BindTransformFeedback); + static NAN_METHOD(BeginTransformFeedback); + static NAN_METHOD(EndTransformFeedback); + static NAN_METHOD(TransformFeedbackVaryings); + static NAN_METHOD(GetTransformFeedbackVarying); + static NAN_METHOD(PauseTransformFeedback); + static NAN_METHOD(ResumeTransformFeedback); + static NAN_METHOD(BindBufferBase); + static NAN_METHOD(BindBufferRange); + static NAN_METHOD(GetIndexedParameter); + static NAN_METHOD(GetUniformIndices); + static NAN_METHOD(GetActiveUniforms); + static NAN_METHOD(GetUniformBlockIndex); + static NAN_METHOD(GetActiveUniformBlockParameter); + static NAN_METHOD(GetActiveUniformBlockName); + static NAN_METHOD(UniformBlockBinding); + static NAN_METHOD(CreateVertexArray); + static NAN_METHOD(DeleteVertexArray); + static NAN_METHOD(IsVertexArray); + static NAN_METHOD(BindVertexArray); }; +void BindWebGL2(const Nan::FunctionCallbackInfo &info); + #endif