diff --git a/src/javascript/extensions/angle-instanced-arrays.js b/src/javascript/extensions/angle-instanced-arrays.js index ade28cfe..08fa43e9 100644 --- a/src/javascript/extensions/angle-instanced-arrays.js +++ b/src/javascript/extensions/angle-instanced-arrays.js @@ -19,16 +19,6 @@ class ANGLEInstancedArrays { } vertexAttribDivisorANGLE (index, divisor) { - const { ctx } = this - index |= 0 - divisor |= 0 - if (divisor < 0 || - index < 0 || index >= ctx._vertexObjectState._attribs.length) { - ctx.setError(gl.INVALID_VALUE) - return - } - const attrib = ctx._vertexObjectState._attribs[index] - attrib._divisor = divisor this._vertexAttribDivisorANGLE(index, divisor) } } diff --git a/src/javascript/webgl-rendering-context.js b/src/javascript/webgl-rendering-context.js index bf0dda52..bf593961 100644 --- a/src/javascript/webgl-rendering-context.js +++ b/src/javascript/webgl-rendering-context.js @@ -1,5 +1,3 @@ -const bits = require('bit-twiddle') -const tokenize = require('glsl-tokenizer/string') const HEADLESS_VERSION = require('../../package.json').version const { gl, NativeWebGLRenderingContext, NativeWebGL } = require('./native-gl') const { getANGLEInstancedArrays } = require('./extensions/angle-instanced-arrays') @@ -18,16 +16,13 @@ const { bindPublics, checkObject, checkUniform, - formatSize, isValidString, typeSize, uniformTypeSize, extractImageData, - vertexCount, isTypedArray, unpackTypedArray, convertPixels, - checkFormat, validCubeTarget } = require('./utils') @@ -98,38 +93,6 @@ 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 { - _checkDimensions ( - target, - width, - height, - level) { - if (level < 0 || - width < 0 || - height < 0) { - this.setError(gl.INVALID_VALUE) - return false - } - if (target === gl.TEXTURE_2D) { - if (width > this._maxTextureSize || - height > this._maxTextureSize || - level > this._maxTextureLevel) { - this.setError(gl.INVALID_VALUE) - return false - } - } else if (this._validCubeTarget(target)) { - if (width > this._maxCubeMapSize || - height > this._maxCubeMapSize || - level > this._maxCubeMapLevel) { - this.setError(gl.INVALID_VALUE) - return false - } - } else { - this.setError(gl.INVALID_ENUM) - return false - } - return true - } - _checkLocation (location) { if (!(location instanceof WebGLUniformLocation)) { this.setError(gl.INVALID_VALUE) @@ -160,80 +123,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } _checkShaderSource (shader) { - const source = shader._source - const tokens = tokenize(source) - - let errorStatus = false - const errorLog = [] - - for (let i = 0; i < tokens.length; ++i) { - const tok = tokens[i] - switch (tok.type) { - case 'ident': - if (!this._validGLSLIdentifier(tok.data)) { - errorStatus = true - errorLog.push(tok.line + ':' + tok.column + - ' invalid identifier - ' + tok.data) - } - break - case 'preprocessor': { - const bodyToks = tokenize(tok.data.match(/^\s*#\s*(.*)$/)[1]) - for (let j = 0; j < bodyToks.length; ++j) { - const btok = bodyToks[j] - if (btok.type === 'ident' || btok.type === undefined) { - if (!this._validGLSLIdentifier(btok.data)) { - errorStatus = true - errorLog.push(tok.line + ':' + btok.column + - ' invalid identifier - ' + btok.data) - } - } - } - break - } - case 'keyword': - switch (tok.data) { - case 'do': - errorStatus = true - errorLog.push(tok.line + ':' + tok.column + ' do not supported') - break - } - break - case 'builtin': - switch (tok.data) { - case 'dFdx': - case 'dFdy': - case 'fwidth': - if (!this._extensions.oes_standard_derivatives) { - errorStatus = true - errorLog.push(tok.line + ':' + tok.column + ' ' + tok.data + ' not supported') - } - break - } - } - } - - if (errorStatus) { - shader._compileInfo = errorLog.join('\n') - } - return !errorStatus - } - - _checkStencilState () { - if (!this._checkStencil) { - return this._stencilState - } - this._checkStencil = false - this._stencilState = true - if (this.getParameter(gl.STENCIL_WRITEMASK) !== - this.getParameter(gl.STENCIL_BACK_WRITEMASK) || - this.getParameter(gl.STENCIL_VALUE_MASK) !== - this.getParameter(gl.STENCIL_BACK_VALUE_MASK) || - this.getParameter(gl.STENCIL_REF) !== - this.getParameter(gl.STENCIL_BACK_REF)) { - this.setError(gl.INVALID_OPERATION) - this._stencilState = false - } - return this._stencilState + return true } _checkTextureTarget (target) { @@ -269,41 +159,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return object instanceof Type && object._ !== 0 } - _checkVertexAttribState (maxIndex) { - const program = this._activeProgram - if (!program) { - this.setError(gl.INVALID_OPERATION) - return false - } - const attribs = this._vertexObjectState._attribs - for (let i = 0; i < attribs.length; ++i) { - const attrib = attribs[i] - if (attrib._isPointer) { - const buffer = attrib._pointerBuffer - if (!buffer) { - this.setError(gl.INVALID_OPERATION) - return false - } - if (program._attributes.indexOf(i) >= 0) { - let maxByte = 0 - if (attrib._divisor) { - maxByte = attrib._pointerSize + - attrib._pointerOffset - } else { - maxByte = attrib._pointerStride * maxIndex + - attrib._pointerSize + - attrib._pointerOffset - } - if (maxByte > buffer._size) { - this.setError(gl.INVALID_OPERATION) - return false - } - } - } - } - return true - } - _checkVertexIndex (index) { if (index < 0 || index >= this._vertexObjectState._attribs.length) { this.setError(gl.INVALID_VALUE) @@ -312,46 +167,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return true } - _computePixelSize (type, internalFormat) { - const pixelSize = formatSize(internalFormat) - if (pixelSize === 0) { - this.setError(gl.INVALID_ENUM) - return 0 - } - switch (type) { - case gl.UNSIGNED_BYTE: - return pixelSize - case gl.UNSIGNED_SHORT_5_6_5: - if (internalFormat !== gl.RGB) { - this.setError(gl.INVALID_OPERATION) - break - } - return 2 - case gl.UNSIGNED_SHORT_4_4_4_4: - case gl.UNSIGNED_SHORT_5_5_5_1: - if (internalFormat !== gl.RGBA) { - this.setError(gl.INVALID_OPERATION) - break - } - return 2 - case gl.FLOAT: - return 1 - } - this.setError(gl.INVALID_ENUM) - return 0 - } - - _computeRowStride (width, pixelSize) { - let rowStride = width * pixelSize - if (rowStride % this._unpackAlignment) { - rowStride += this._unpackAlignment - (rowStride % this._unpackAlignment) - } - return rowStride - } - _fixupLink (program) { if (!super.getProgramParameter(program._, gl.LINK_STATUS)) { - program._linkInfoLog = super.getProgramInfoLog(program) + program._linkInfoLog = super.getProgramInfoLog(program._) return false } @@ -400,12 +218,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } _framebufferOk () { - const framebuffer = this._activeFramebuffer - if (framebuffer && - this._preCheckFramebufferStatus(framebuffer) !== gl.FRAMEBUFFER_COMPLETE) { - this.setError(gl.INVALID_FRAMEBUFFER_OPERATION) - return false - } return true } @@ -455,113 +267,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return null } - _preCheckFramebufferStatus (framebuffer) { - const attachments = framebuffer._attachments - const width = [] - const height = [] - const depthAttachment = attachments[gl.DEPTH_ATTACHMENT] - const depthStencilAttachment = attachments[gl.DEPTH_STENCIL_ATTACHMENT] - const stencilAttachment = attachments[gl.STENCIL_ATTACHMENT] - - if ((depthStencilAttachment && (stencilAttachment || depthAttachment)) || - (stencilAttachment && depthAttachment)) { - return gl.FRAMEBUFFER_UNSUPPORTED - } - - const colorAttachments = this._getColorAttachments() - let colorAttachmentCount = 0 - for (const attachmentEnum in attachments) { - if (attachments[attachmentEnum] && colorAttachments.indexOf(attachmentEnum * 1) !== -1) { - colorAttachmentCount++ - } - } - if (colorAttachmentCount === 0) { - return gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - } - - if (depthStencilAttachment instanceof WebGLTexture) { - return gl.FRAMEBUFFER_UNSUPPORTED - } else if (depthStencilAttachment instanceof WebGLRenderbuffer) { - if (depthStencilAttachment._format !== gl.DEPTH_STENCIL) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - width.push(depthStencilAttachment._width) - height.push(depthStencilAttachment._height) - } - - if (depthAttachment instanceof WebGLTexture) { - return gl.FRAMEBUFFER_UNSUPPORTED - } else if (depthAttachment instanceof WebGLRenderbuffer) { - if (depthAttachment._format !== gl.DEPTH_COMPONENT16) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - width.push(depthAttachment._width) - height.push(depthAttachment._height) - } - - if (stencilAttachment instanceof WebGLTexture) { - return gl.FRAMEBUFFER_UNSUPPORTED - } else if (stencilAttachment instanceof WebGLRenderbuffer) { - if (stencilAttachment._format !== gl.STENCIL_INDEX8) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - width.push(stencilAttachment._width) - height.push(stencilAttachment._height) - } - - let colorAttached = false - for (let i = 0; i < colorAttachments.length; ++i) { - const colorAttachment = attachments[colorAttachments[i]] - if (colorAttachment instanceof WebGLTexture) { - if (colorAttachment._format !== gl.RGBA || - !(colorAttachment._type === gl.UNSIGNED_BYTE || colorAttachment._type === gl.FLOAT)) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - colorAttached = true - const level = framebuffer._attachmentLevel[gl.COLOR_ATTACHMENT0] - width.push(colorAttachment._levelWidth[level]) - height.push(colorAttachment._levelHeight[level]) - } else if (colorAttachment instanceof WebGLRenderbuffer) { - const format = colorAttachment._format - if (format !== gl.RGBA4 && - format !== gl.RGB565 && - format !== gl.RGB5_A1) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - colorAttached = true - width.push(colorAttachment._width) - height.push(colorAttachment._height) - } - } - - if (!colorAttached && - !stencilAttachment && - !depthAttachment && - !depthStencilAttachment) { - return gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT - } - - if (width.length <= 0 || height.length <= 0) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - - for (let i = 1; i < width.length; ++i) { - if (width[i - 1] !== width[i] || - height[i - 1] !== height[i]) { - return gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS - } - } - - if (width[0] === 0 || height[0] === 0) { - return gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT - } - - framebuffer._width = width[0] - framebuffer._height = height[0] - - return gl.FRAMEBUFFER_COMPLETE - } - _isConstantBlendFunc (factor) { return ( factor === gl.CONSTANT_COLOR || @@ -697,55 +402,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } } - _updateFramebufferAttachments (framebuffer) { - const prevStatus = framebuffer._status - const attachments = this._getAttachments() - framebuffer._status = this._preCheckFramebufferStatus(framebuffer) - if (framebuffer._status !== gl.FRAMEBUFFER_COMPLETE) { - if (prevStatus === gl.FRAMEBUFFER_COMPLETE) { - for (let i = 0; i < attachments.length; ++i) { - const attachmentEnum = attachments[i] - super.framebufferTexture2D( - gl.FRAMEBUFFER, - attachmentEnum, - framebuffer._attachmentFace[attachmentEnum], - 0, - framebuffer._attachmentLevel[attachmentEnum]) - } - } - return - } - - for (let i = 0; i < attachments.length; ++i) { - const attachmentEnum = attachments[i] - super.framebufferTexture2D( - gl.FRAMEBUFFER, - attachmentEnum, - framebuffer._attachmentFace[attachmentEnum], - 0, - framebuffer._attachmentLevel[attachmentEnum]) - } - - for (let i = 0; i < attachments.length; ++i) { - const attachmentEnum = attachments[i] - const attachment = framebuffer._attachments[attachmentEnum] - if (attachment instanceof WebGLTexture) { - super.framebufferTexture2D( - gl.FRAMEBUFFER, - attachmentEnum, - framebuffer._attachmentFace[attachmentEnum], - attachment._ | 0, - framebuffer._attachmentLevel[attachmentEnum]) - } else if (attachment instanceof WebGLRenderbuffer) { - super.framebufferRenderbuffer( - gl.FRAMEBUFFER, - attachmentEnum, - gl.RENDERBUFFER, - attachment._ | 0) - } - } - } - _validBlendFunc (factor) { return factor === gl.ZERO || factor === gl.ONE || @@ -832,49 +488,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } } - _wrapShader (type, source) { // eslint-disable-line - // the gl implementation seems to define `GL_OES_standard_derivatives` even when the extension is disabled - // this behaviour causes one conformance test ('GL_OES_standard_derivatives defined in shaders when extension is disabled') to fail - // by `undef`ing `GL_OES_standard_derivatives`, this appears to solve the issue - if (!this._extensions.oes_standard_derivatives && /#ifdef\s+GL_OES_standard_derivatives/.test(source)) { - source = '#undef GL_OES_standard_derivatives\n' + source - } - - return this._extensions.webgl_draw_buffers ? source : '#define gl_MaxDrawBuffers 1\n' + source // eslint-disable-line - } - - _beginAttrib0Hack () { - super.bindBuffer(gl.ARRAY_BUFFER, this._attrib0Buffer._) - super.bufferData( - gl.ARRAY_BUFFER, - this._vertexGlobalState._attribs[0]._data, - gl.STREAM_DRAW) - super.enableVertexAttribArray(0) - super.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0) - super._vertexAttribDivisorANGLE(0, 1) - } - - _endAttrib0Hack () { - const attrib = this._vertexObjectState._attribs[0] - if (attrib._pointerBuffer) { - super.bindBuffer(gl.ARRAY_BUFFER, attrib._pointerBuffer._) - } else { - super.bindBuffer(gl.ARRAY_BUFFER, 0) - } - super.vertexAttribPointer( - 0, - attrib._inputSize, - attrib._pointerType, - attrib._pointerNormal, - attrib._inputStride, - attrib._pointerOffset) - super._vertexAttribDivisorANGLE(0, attrib._divisor) - super.disableVertexAttribArray(0) - if (this._vertexGlobalState._arrayBufferBinding) { - super.bindBuffer(gl.ARRAY_BUFFER, this._vertexGlobalState._arrayBufferBinding._) - } else { - super.bindBuffer(gl.ARRAY_BUFFER, 0) - } + _wrapShader (type, source) { + return source } activeTexture (texture) { @@ -938,20 +553,23 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { if (!checkObject(framebuffer)) { throw new TypeError('bindFramebuffer(GLenum, WebGLFramebuffer)') } - if (target !== gl.FRAMEBUFFER) { - this.setError(gl.INVALID_ENUM) - return - } + let error = 0 if (!framebuffer) { + this._saveError() super.bindFramebuffer( - gl.FRAMEBUFFER, + target, this._drawingBuffer._framebuffer) + error = super.getError() + this._restoreError(error) } else if (framebuffer._pendingDelete) { return } else if (this._checkWrapper(framebuffer, WebGLFramebuffer)) { + this._saveError() super.bindFramebuffer( - gl.FRAMEBUFFER, + target, framebuffer._ | 0) + error = super.getError() + this._restoreError(error) } else { return } @@ -965,9 +583,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { framebuffer._refCount += 1 } } - this._activeFramebuffer = framebuffer - if (framebuffer) { - this._updateFramebufferAttachments(framebuffer) + if (error === 0) { + this._activeFramebuffer = framebuffer } } @@ -1051,11 +668,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('bindTexture(GLenum, WebGLTexture)') } - if (!this._validTextureTarget(target)) { - this.setError(gl.INVALID_ENUM) - return - } - // Get texture id let textureId = 0 if (!texture) { @@ -1065,16 +677,8 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { // Special case: error codes for deleted textures don't get set for some dumb reason return } else if (this._checkWrapper(texture, WebGLTexture)) { - // Check binding mode of texture - if (texture._binding && texture._binding !== target) { - this.setError(gl.INVALID_OPERATION) - return - } texture._binding = target - - if (texture._complete) { - textureId = texture._ | 0 - } + textureId = texture._ | 0 } else { return } @@ -1421,17 +1025,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } checkFramebufferStatus (target) { - if (target !== gl.FRAMEBUFFER) { - this.setError(gl.INVALID_ENUM) - return 0 - } - - const framebuffer = this._activeFramebuffer - if (!framebuffer) { - return gl.FRAMEBUFFER_COMPLETE - } - - return this._preCheckFramebufferStatus(framebuffer) + return super.checkFramebufferStatus(target) } clear (mask) { @@ -1491,31 +1085,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { height |= 0 border |= 0 - const texture = this._getTexImage(target) - if (!texture) { - this.setError(gl.INVALID_OPERATION) - return - } - - if (internalFormat !== gl.RGBA && - internalFormat !== gl.RGB && - internalFormat !== gl.ALPHA && - internalFormat !== gl.LUMINANCE && - internalFormat !== gl.LUMINANCE_ALPHA) { - this.setError(gl.INVALID_ENUM) - return - } - - if (level < 0 || width < 0 || height < 0 || border !== 0) { - this.setError(gl.INVALID_VALUE) - return - } - - if (level > 0 && !(bits.isPow2(width) && bits.isPow2(height))) { - this.setError(gl.INVALID_VALUE) - return - } - this._saveError() super.copyTexImage2D( target, @@ -1530,8 +1099,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { this._restoreError(error) if (error === gl.NO_ERROR) { - texture._levelWidth[level] = width - texture._levelHeight[level] = height + const texture = this._getTexImage(target) texture._format = gl.RGBA texture._type = gl.UNSIGNED_BYTE } @@ -1551,17 +1119,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { width |= 0 height |= 0 - const texture = this._getTexImage(target) - if (!texture) { - this.setError(gl.INVALID_OPERATION) - return - } - - if (width < 0 || height < 0 || xoffset < 0 || yoffset < 0 || level < 0) { - this.setError(gl.INVALID_VALUE) - return - } - super.copyTexSubImage2D( target, level, @@ -1831,48 +1388,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { first |= 0 count |= 0 - if (first < 0 || count < 0) { - this.setError(gl.INVALID_VALUE) - return - } - - if (!this._checkStencilState()) { - return - } - - const reducedCount = vertexCount(mode, count) - if (reducedCount < 0) { - this.setError(gl.INVALID_ENUM) - return - } - - if (!this._framebufferOk()) { - return - } - - if (count === 0) { - return - } - - let maxIndex = first - if (count > 0) { - maxIndex = (count + first - 1) >>> 0 - } - if (this._checkVertexAttribState(maxIndex)) { - if ( - this._vertexObjectState._attribs[0]._isPointer || ( - this._extensions.webgl_draw_buffers && - this._extensions.webgl_draw_buffers._buffersState && - this._extensions.webgl_draw_buffers._buffersState.length > 0 - ) - ) { - return super.drawArrays(mode, first, reducedCount) - } else { - this._beginAttrib0Hack() - super._drawArraysInstancedANGLE(mode, first, reducedCount, 1) - this._endAttrib0Hack() - } - } + return super.drawArrays(mode, first, count) } drawElements (mode, count, type, ioffset) { @@ -1881,120 +1397,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { type |= 0 ioffset |= 0 - if (count < 0 || ioffset < 0) { - this.setError(gl.INVALID_VALUE) - return - } - - if (!this._checkStencilState()) { - return - } - - const elementBuffer = this._vertexObjectState._elementArrayBufferBinding - if (!elementBuffer) { - this.setError(gl.INVALID_OPERATION) - return - } - - // Unpack element data - let elementData = null - let offset = ioffset - if (type === gl.UNSIGNED_SHORT) { - if (offset % 2) { - this.setError(gl.INVALID_OPERATION) - return - } - offset >>= 1 - elementData = new Uint16Array(elementBuffer._elements.buffer) - } else if (this._extensions.oes_element_index_uint && type === gl.UNSIGNED_INT) { - if (offset % 4) { - this.setError(gl.INVALID_OPERATION) - return - } - offset >>= 2 - elementData = new Uint32Array(elementBuffer._elements.buffer) - } else if (type === gl.UNSIGNED_BYTE) { - elementData = elementBuffer._elements - } else { - this.setError(gl.INVALID_ENUM) - return - } - - let reducedCount = count - switch (mode) { - case gl.TRIANGLES: - if (count % 3) { - reducedCount -= (count % 3) - } - break - case gl.LINES: - if (count % 2) { - reducedCount -= (count % 2) - } - break - case gl.POINTS: - break - case gl.LINE_LOOP: - case gl.LINE_STRIP: - if (count < 2) { - this.setError(gl.INVALID_OPERATION) - return - } - break - case gl.TRIANGLE_FAN: - case gl.TRIANGLE_STRIP: - if (count < 3) { - this.setError(gl.INVALID_OPERATION) - return - } - break - default: - this.setError(gl.INVALID_ENUM) - return - } - - if (!this._framebufferOk()) { - return - } - - if (count === 0) { - this._checkVertexAttribState(0) - return - } - - if ((count + offset) >>> 0 > elementData.length) { - this.setError(gl.INVALID_OPERATION) - return - } - - // Compute max index - let maxIndex = -1 - for (let i = offset; i < offset + count; ++i) { - maxIndex = Math.max(maxIndex, elementData[i]) - } - - if (maxIndex < 0) { - this._checkVertexAttribState(0) - return - } - - if (this._checkVertexAttribState(maxIndex)) { - if (reducedCount > 0) { - if ( - this._vertexObjectState._attribs[0]._isPointer || ( - this._extensions.webgl_draw_buffers && - this._extensions.webgl_draw_buffers._buffersState && - this._extensions.webgl_draw_buffers._buffersState.length > 0 - ) - ) { - return super.drawElements(mode, reducedCount, type, ioffset) - } else { - this._beginAttrib0Hack() - super._drawElementsInstancedANGLE(mode, reducedCount, type, ioffset, 1) - this._endAttrib0Hack() - } - } - } + return super.drawElements(mode, count, type, ioffset) } enable (cap) { @@ -2035,8 +1438,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('framebufferRenderbuffer(GLenum, GLenum, GLenum, WebGLRenderbuffer)') } - if (target !== gl.FRAMEBUFFER || - !this._validFramebufferAttachment(attachment) || + if (!this._validFramebufferAttachment(attachment) || renderbufferTarget !== gl.RENDERBUFFER) { this.setError(gl.INVALID_ENUM) return @@ -2052,8 +1454,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } - framebuffer._setAttachment(renderbuffer, attachment) - this._updateFramebufferAttachments(framebuffer) + super.framebufferRenderbuffer(target, attachment, renderbufferTarget, renderbuffer?._ ?? null) } framebufferTexture2D ( @@ -2071,8 +1472,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } // Check parameters are ok - if (target !== gl.FRAMEBUFFER || - !this._validFramebufferAttachment(attachment)) { + if (!this._validFramebufferAttachment(attachment)) { this.setError(gl.INVALID_ENUM) return } @@ -2110,10 +1510,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } - framebuffer._attachmentLevel[attachment] = level - framebuffer._attachmentFace[attachment] = textarget - framebuffer._setAttachment(texture, attachment) - this._updateFramebufferAttachments(framebuffer) + super.framebufferTexture2D(target, attachment, textarget, texture?._ ?? null, level) } frontFace (mode) { @@ -2189,8 +1586,9 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { } getParameter (pname) { - pname |= 0 switch (pname) { + case gl.COMPRESSED_TEXTURE_FORMATS: + return new Uint32Array(0) case gl.ARRAY_BUFFER_BINDING: return this._vertexGlobalState._arrayBufferBinding case gl.ELEMENT_ARRAY_BUFFER_BINDING: @@ -2214,144 +1612,13 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { case gl.SHADING_LANGUAGE_VERSION: return 'WebGL GLSL ES 1.0 stack-gl' - case gl.COMPRESSED_TEXTURE_FORMATS: - return new Uint32Array(0) - - // Int arrays - case gl.MAX_VIEWPORT_DIMS: - case gl.SCISSOR_BOX: - case gl.VIEWPORT: - return new Int32Array(super.getParameter(pname)) - - // Float arrays - case gl.ALIASED_LINE_WIDTH_RANGE: - case gl.ALIASED_POINT_SIZE_RANGE: - case gl.DEPTH_RANGE: - case gl.BLEND_COLOR: - case gl.COLOR_CLEAR_VALUE: - return new Float32Array(super.getParameter(pname)) - - case gl.COLOR_WRITEMASK: - return super.getParameter(pname) - - case gl.DEPTH_CLEAR_VALUE: - case gl.LINE_WIDTH: - case gl.POLYGON_OFFSET_FACTOR: - case gl.POLYGON_OFFSET_UNITS: - case gl.SAMPLE_COVERAGE_VALUE: - return +super.getParameter(pname) - - case gl.BLEND: - case gl.CULL_FACE: - case gl.DEPTH_TEST: - case gl.DEPTH_WRITEMASK: - case gl.DITHER: - case gl.POLYGON_OFFSET_FILL: - case gl.SAMPLE_COVERAGE_INVERT: - case gl.SCISSOR_TEST: - case gl.STENCIL_TEST: - case gl.UNPACK_FLIP_Y_WEBGL: - case gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL: - return !!super.getParameter(pname) - - case gl.ACTIVE_TEXTURE: - case gl.ALPHA_BITS: - case gl.BLEND_DST_ALPHA: - case gl.BLEND_DST_RGB: - case gl.BLEND_EQUATION_ALPHA: - case gl.BLEND_EQUATION_RGB: - case gl.BLEND_SRC_ALPHA: - case gl.BLEND_SRC_RGB: - case gl.BLUE_BITS: - case gl.CULL_FACE_MODE: - case gl.DEPTH_BITS: - case gl.DEPTH_FUNC: - case gl.FRONT_FACE: - case gl.GENERATE_MIPMAP_HINT: - case gl.GREEN_BITS: - case gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS: - case gl.MAX_CUBE_MAP_TEXTURE_SIZE: - case gl.MAX_FRAGMENT_UNIFORM_VECTORS: - case gl.MAX_RENDERBUFFER_SIZE: - case gl.MAX_TEXTURE_IMAGE_UNITS: - case gl.MAX_TEXTURE_SIZE: - case gl.MAX_VARYING_VECTORS: - case gl.MAX_VERTEX_ATTRIBS: - case gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS: - case gl.MAX_VERTEX_UNIFORM_VECTORS: - case gl.PACK_ALIGNMENT: - case gl.RED_BITS: - case gl.SAMPLE_BUFFERS: - case gl.SAMPLES: - case gl.STENCIL_BACK_FAIL: - case gl.STENCIL_BACK_FUNC: - case gl.STENCIL_BACK_PASS_DEPTH_FAIL: - case gl.STENCIL_BACK_PASS_DEPTH_PASS: - case gl.STENCIL_BACK_REF: - case gl.STENCIL_BACK_VALUE_MASK: - case gl.STENCIL_BACK_WRITEMASK: - case gl.STENCIL_BITS: - case gl.STENCIL_CLEAR_VALUE: - case gl.STENCIL_FAIL: - case gl.STENCIL_FUNC: - case gl.STENCIL_PASS_DEPTH_FAIL: - case gl.STENCIL_PASS_DEPTH_PASS: - case gl.STENCIL_REF: - case gl.STENCIL_VALUE_MASK: - case gl.STENCIL_WRITEMASK: - case gl.SUBPIXEL_BITS: - case gl.UNPACK_ALIGNMENT: - case gl.UNPACK_COLORSPACE_CONVERSION_WEBGL: - return super.getParameter(pname) | 0 - - case gl.IMPLEMENTATION_COLOR_READ_FORMAT: - case gl.IMPLEMENTATION_COLOR_READ_TYPE: - return super.getParameter(pname) - default: - if (this._extensions.webgl_draw_buffers) { - const ext = this._extensions.webgl_draw_buffers - switch (pname) { - case ext.DRAW_BUFFER0_WEBGL: - case ext.DRAW_BUFFER1_WEBGL: - case ext.DRAW_BUFFER2_WEBGL: - case ext.DRAW_BUFFER3_WEBGL: - case ext.DRAW_BUFFER4_WEBGL: - case ext.DRAW_BUFFER5_WEBGL: - case ext.DRAW_BUFFER6_WEBGL: - case ext.DRAW_BUFFER7_WEBGL: - case ext.DRAW_BUFFER8_WEBGL: - case ext.DRAW_BUFFER9_WEBGL: - case ext.DRAW_BUFFER10_WEBGL: - case ext.DRAW_BUFFER11_WEBGL: - case ext.DRAW_BUFFER12_WEBGL: - case ext.DRAW_BUFFER13_WEBGL: - case ext.DRAW_BUFFER14_WEBGL: - case ext.DRAW_BUFFER15_WEBGL: - if (ext._buffersState.length === 1 && ext._buffersState[0] === gl.BACK) { - return gl.BACK - } - return super.getParameter(pname) - case ext.MAX_DRAW_BUFFERS_WEBGL: - case ext.MAX_COLOR_ATTACHMENTS_WEBGL: - return super.getParameter(pname) + if (this._extensions) { + if (this._extensions.oes_vertex_array_object && pname === this._extensions.oes_vertex_array_object.VERTEX_ARRAY_BINDING_OES) { + return this._extensions.oes_vertex_array_object._activeVertexArrayObject } } - - if (this._extensions.oes_standard_derivatives && pname === this._extensions.oes_standard_derivatives.FRAGMENT_SHADER_DERIVATIVE_HINT_OES) { - return super.getParameter(pname) - } - - if (this._extensions.ext_texture_filter_anisotropic && pname === this._extensions.ext_texture_filter_anisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT) { - return super.getParameter(pname) - } - - if (this._extensions.oes_vertex_array_object && pname === this._extensions.oes_vertex_array_object.VERTEX_ARRAY_BINDING_OES) { - return this._extensions.oes_vertex_array_object._activeVertexArrayObject - } - - this.setError(gl.INVALID_ENUM) - return null + return super.getParameter(pname) } } @@ -2409,50 +1676,31 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { attachment |= 0 pname |= 0 - if (target !== gl.FRAMEBUFFER || - !this._validFramebufferAttachment(attachment)) { - this.setError(gl.INVALID_ENUM) + // 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) return null } - const framebuffer = this._activeFramebuffer - if (!framebuffer) { - this.setError(gl.INVALID_OPERATION) + this._saveError() + const result = super.getFramebufferAttachmentParameter(target, attachment, pname) + const error = super.getError() + this._restoreError(error) + + if (error) { return null } - const object = framebuffer._attachments[attachment] - if (object === null) { - if (pname === gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) { - return gl.NONE - } - } else if (object instanceof WebGLTexture) { - switch (pname) { - case gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return object - case gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return gl.TEXTURE - case gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: - return framebuffer._attachmentLevel[attachment] - case gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: { - const face = framebuffer._attachmentFace[attachment] - if (face === gl.TEXTURE_2D) { - return 0 - } - return face - } - } - } else if (object instanceof WebGLRenderbuffer) { - switch (pname) { - case gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return object - case gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return gl.RENDERBUFFER + 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) { + return this._renderbuffers[result] + } else { + return this._textures[result] } } - this.setError(gl.INVALID_ENUM) - return null + return result } getProgramParameter (program, pname) { @@ -2730,13 +1978,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { const attrib = this._vertexObjectState._attribs[index] const vertexAttribValue = this._vertexGlobalState._attribs[index]._data - const extInstancing = this._extensions.angle_instanced_arrays - if (extInstancing) { - if (pname === extInstancing.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE) { - return attrib._divisor - } - } - switch (pname) { case gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: return attrib._pointerBuffer @@ -2753,8 +1994,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { case gl.CURRENT_VERTEX_ATTRIB: return new Float32Array(vertexAttribValue) default: - this.setError(gl.INVALID_ENUM) - return null + return super.getVertexAttrib(index, pname) } } @@ -2899,119 +2139,14 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { width |= 0 height |= 0 - if (!(this._extensions.oes_texture_float && type === gl.FLOAT && format === gl.RGBA)) { - if (format === gl.RGB || - format === gl.ALPHA || - type !== gl.UNSIGNED_BYTE) { - this.setError(gl.INVALID_OPERATION) - return - } else if (format !== gl.RGBA) { - this.setError(gl.INVALID_ENUM) - return - } else if ( - width < 0 || - height < 0 || - !(pixels instanceof Uint8Array)) { - this.setError(gl.INVALID_VALUE) - return - } - } - - if (!this._framebufferOk()) { - return - } - - let rowStride = width * 4 - if (rowStride % this._packAlignment !== 0) { - rowStride += this._packAlignment - (rowStride % this._packAlignment) - } - - const imageSize = rowStride * (height - 1) + width * 4 - if (imageSize <= 0) { - return - } - if (pixels.length < imageSize) { - this.setError(gl.INVALID_VALUE) - return - } - - // Handle reading outside the window - let viewWidth = this.drawingBufferWidth - let viewHeight = this.drawingBufferHeight - - if (this._activeFramebuffer) { - viewWidth = this._activeFramebuffer._width - viewHeight = this._activeFramebuffer._height - } - - const pixelData = unpackTypedArray(pixels) - - if (x >= viewWidth || x + width <= 0 || - y >= viewHeight || y + height <= 0) { - for (let i = 0; i < pixelData.length; ++i) { - pixelData[i] = 0 - } - } else if (x < 0 || x + width > viewWidth || - y < 0 || y + height > viewHeight) { - for (let i = 0; i < pixelData.length; ++i) { - pixelData[i] = 0 - } - - let nx = x - let nWidth = width - if (x < 0) { - nWidth += x - nx = 0 - } - if (nx + width > viewWidth) { - nWidth = viewWidth - nx - } - let ny = y - let nHeight = height - if (y < 0) { - nHeight += y - ny = 0 - } - if (ny + height > viewHeight) { - nHeight = viewHeight - ny - } - - let nRowStride = nWidth * 4 - if (nRowStride % this._packAlignment !== 0) { - nRowStride += this._packAlignment - (nRowStride % this._packAlignment) - } - - if (nWidth > 0 && nHeight > 0) { - const subPixels = new Uint8Array(nRowStride * nHeight) - super.readPixels( - nx, - ny, - nWidth, - nHeight, - format, - type, - subPixels) - - const offset = 4 * (nx - x) + (ny - y) * rowStride - for (let j = 0; j < nHeight; ++j) { - for (let i = 0; i < nWidth; ++i) { - for (let k = 0; k < 4; ++k) { - pixelData[offset + j * rowStride + 4 * i + k] = - subPixels[j * nRowStride + 4 * i + k] - } - } - } - } - } else { - super.readPixels( - x, - y, - width, - height, - format, - type, - pixelData) - } + super.readPixels( + x, + y, + width, + height, + format, + type, + pixels) } renderbufferStorage ( @@ -3035,17 +2170,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { return } - if (internalFormat !== gl.RGBA4 && - internalFormat !== gl.RGB565 && - internalFormat !== gl.RGB5_A1 && - internalFormat !== gl.DEPTH_COMPONENT16 && - internalFormat !== gl.STENCIL_INDEX && - internalFormat !== gl.STENCIL_INDEX8 && - internalFormat !== gl.DEPTH_STENCIL) { - this.setError(gl.INVALID_ENUM) - return - } - this._saveError() super.renderbufferStorage( target, @@ -3061,21 +2185,6 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { renderbuffer._width = width renderbuffer._height = height renderbuffer._format = internalFormat - - const activeFramebuffer = this._activeFramebuffer - if (activeFramebuffer) { - let needsUpdate = false - const attachments = this._getAttachments() - for (let i = 0; i < attachments.length; ++i) { - if (activeFramebuffer._attachments[attachments[i]] === renderbuffer) { - needsUpdate = true - break - } - } - if (needsUpdate) { - this._updateFramebufferAttachments(this._activeFramebuffer) - } - } } resize (width, height) { @@ -3185,49 +2294,16 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('texImage2D(GLenum, GLint, GLenum, GLint, GLint, GLint, GLenum, GLenum, Uint8Array)') } - if (!checkFormat(format) || !checkFormat(internalFormat)) { - this.setError(gl.INVALID_ENUM) - return - } - - if (type === gl.FLOAT && !this._extensions.oes_texture_float) { - this.setError(gl.INVALID_ENUM) - return - } - - const texture = this._getTexImage(target) - if (!texture || format !== internalFormat) { - this.setError(gl.INVALID_OPERATION) - return - } - - const pixelSize = this._computePixelSize(type, format) - if (pixelSize === 0) { - return - } - - if (!this._checkDimensions( - target, - width, - height, - level)) { - return + // 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) + return + } } const data = convertPixels(pixels) - const rowStride = this._computeRowStride(width, pixelSize) - const imageSize = rowStride * height - - if (data && data.length < imageSize) { - this.setError(gl.INVALID_OPERATION) - return - } - if (border !== 0 || - (validCubeTarget(target) && width !== height)) { - this.setError(gl.INVALID_VALUE) - return - } // Need to check for out of memory error this._saveError() super.texImage2D( @@ -3242,29 +2318,10 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { data) const error = this.getError() this._restoreError(error) - if (error !== gl.NO_ERROR) { - return - } - - // Save width and height at level - texture._levelWidth[level] = width - texture._levelHeight[level] = height - texture._format = format - texture._type = type - - const activeFramebuffer = this._activeFramebuffer - if (activeFramebuffer) { - let needsUpdate = false - const attachments = this._getAttachments() - for (let i = 0; i < attachments.length; ++i) { - if (activeFramebuffer._attachments[attachments[i]] === texture) { - needsUpdate = true - break - } - } - if (needsUpdate) { - this._updateFramebufferAttachments(this._activeFramebuffer) - } + if (error === gl.NO_ERROR) { + const texture = this._getTexImage(target) + texture._format = format + texture._type = type } } @@ -3298,52 +2355,7 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { throw new TypeError('texSubImage2D(GLenum, GLint, GLint, GLint, GLint, GLint, GLenum, GLenum, Uint8Array)') } - target |= 0 - level |= 0 - xoffset |= 0 - yoffset |= 0 - width |= 0 - height |= 0 - format |= 0 - type |= 0 - - const texture = this._getTexImage(target) - if (!texture) { - this.setError(gl.INVALID_OPERATION) - return - } - - if (type === gl.FLOAT && !this._extensions.oes_texture_float) { - this.setError(gl.INVALID_ENUM) - return - } - - const pixelSize = this._computePixelSize(type, format) - if (pixelSize === 0) { - return - } - - if (!this._checkDimensions( - target, - width, - height, - level)) { - return - } - - if (xoffset < 0 || yoffset < 0) { - this.setError(gl.INVALID_VALUE) - return - } - const data = convertPixels(pixels) - const rowStride = this._computeRowStride(width, pixelSize) - const imageSize = rowStride * height - - if (!data || data.length < imageSize) { - this.setError(gl.INVALID_OPERATION) - return - } super.texSubImage2D( target, @@ -3385,22 +2397,11 @@ class WebGLRenderingContext extends NativeWebGLRenderingContext { pname |= 0 param |= 0 - 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: - return super.texParameteri(target, pname, param) - } - - if (this._extensions.ext_texture_filter_anisotropic && pname === this._extensions.ext_texture_filter_anisotropic.TEXTURE_MAX_ANISOTROPY_EXT) { - return super.texParameteri(target, pname, param) - } - - this.setError(gl.INVALID_ENUM) + // Note: there's an ANGLE bug where it doesn't check for setting texParameter on texture zero in WebGL compat. + if (!this._checkTextureTarget(target)) { + return } + return super.texParameteri(target, pname, param) } useProgram (program) { diff --git a/src/javascript/webgl-texture.js b/src/javascript/webgl-texture.js index dc16376d..753c468a 100644 --- a/src/javascript/webgl-texture.js +++ b/src/javascript/webgl-texture.js @@ -6,8 +6,6 @@ class WebGLTexture extends Linkable { super(_) this._ctx = ctx this._binding = 0 - this._levelWidth = new Int32Array(32) - this._levelHeight = new Int32Array(32) this._format = 0 this._type = 0 this._complete = true diff --git a/src/javascript/webgl-vertex-attribute.js b/src/javascript/webgl-vertex-attribute.js index e623c929..d99a5348 100644 --- a/src/javascript/webgl-vertex-attribute.js +++ b/src/javascript/webgl-vertex-attribute.js @@ -16,7 +16,6 @@ class WebGLVertexArrayObjectAttribute { this._pointerStride = 0 this._pointerType = gl.FLOAT this._pointerNormal = false - this._divisor = 0 this._inputSize = 4 this._inputStride = 0 } diff --git a/src/native/webgl.cc b/src/native/webgl.cc index 181a78d3..3713e822 100644 --- a/src/native/webgl.cc +++ b/src/native/webgl.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -106,6 +107,17 @@ GLenum SizeFloatingPointFormat(GLenum format) { return format; } +GLenum OverrideDrawBufferEnum(GLenum buffer) { + switch (buffer) { + case GL_BACK: + return GL_COLOR_ATTACHMENT0; + case GL_DEPTH: + return GL_DEPTH_ATTACHMENT; + case GL_STENCIL: + return GL_STENCIL_ATTACHMENT; + } +} + std::set GetStringSetFromCString(const char *cstr) { std::set result; std::istringstream iss(cstr); // Create an input string stream @@ -154,12 +166,10 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, bool preferLowPowerToHighPerformance, bool failIfMajorPerformanceCaveat) : state(GLCONTEXT_STATE_INIT), unpack_flip_y(false), unpack_premultiply_alpha(false), - unpack_colorspace_conversion(0x9244), unpack_alignment(4), next(NULL), prev(NULL), - lastError(GL_NO_ERROR) { - + unpack_colorspace_conversion(0x9244), unpack_alignment(4), next(NULL), prev(NULL) { if (!eglGetProcAddress) { if (!eglLibrary.open("libEGL")) { - std::cerr << "Error opening ANGLE shared library." << std::endl; + errorMessage = "Error opening ANGLE shared library."; state = GLCONTEXT_STATE_ERROR; return; } @@ -172,12 +182,14 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, if (!HAS_DISPLAY) { DISPLAY = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (DISPLAY == EGL_NO_DISPLAY) { + errorMessage = "Error retrieving EGL default display."; state = GLCONTEXT_STATE_ERROR; return; } // Initialize EGL if (!eglInitialize(DISPLAY, NULL, NULL)) { + errorMessage = "Error initializing EGL."; state = GLCONTEXT_STATE_ERROR; return; } @@ -206,6 +218,7 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, EGL_NONE}; EGLint num_config; if (!eglChooseConfig(DISPLAY, attrib_list, &config, 1, &num_config) || num_config != 1) { + errorMessage = "Error choosing EGL config."; state = GLCONTEXT_STATE_ERROR; return; } @@ -217,6 +230,8 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, EGL_TRUE, EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE, EGL_FALSE, + EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE, + EGL_TRUE, EGL_NONE}; context = eglCreateContext(DISPLAY, config, EGL_NO_CONTEXT, contextAttribs); if (context == EGL_NO_CONTEXT) { @@ -227,12 +242,14 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, EGLint surfaceAttribs[] = {EGL_WIDTH, (EGLint)width, EGL_HEIGHT, (EGLint)height, EGL_NONE}; surface = eglCreatePbufferSurface(DISPLAY, config, surfaceAttribs); if (surface == EGL_NO_SURFACE) { + errorMessage = "Error creating EGL surface."; state = GLCONTEXT_STATE_ERROR; return; } // Set active if (!eglMakeCurrent(DISPLAY, surface, surface, context)) { + errorMessage = "Error making context current."; state = GLCONTEXT_STATE_ERROR; return; } @@ -247,6 +264,12 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, // Enable the debug callback to debug GL errors. // EnableDebugCallback(nullptr); + // Log the GL_RENDERER and GL_VERSION strings. + // const char *rendererString = (const char *)glGetString(GL_RENDERER); + // const char *versionString = (const char *)glGetString(GL_VERSION); + // std::cout << "ANGLE GL_RENDERER: " << rendererString << std::endl; + // std::cout << "ANGLE GL_VERSION: " << versionString << std::endl; + // Check extensions const char *extensionsString = (const char *)(glGetString(GL_EXTENSIONS)); enabledExtensions = GetStringSetFromCString(extensionsString); @@ -257,7 +280,6 @@ WebGLRenderingContext::WebGLRenderingContext(int width, int height, bool alpha, // Request necessary WebGL extensions. glRequestExtensionANGLE("GL_EXT_texture_storage"); - glRequestExtensionANGLE("GL_ANGLE_instanced_arrays"); // Select best preferred depth preferredDepth = GL_DEPTH_COMPONENT16; @@ -302,15 +324,10 @@ bool WebGLRenderingContext::setActive() { } void WebGLRenderingContext::setError(GLenum error) { - if (error == GL_NO_ERROR || lastError != GL_NO_ERROR) { + if (error == GL_NO_ERROR || errorSet.count(error) > 0) { return; } - GLenum prevError = glGetError(); - if (prevError == GL_NO_ERROR) { - lastError = error; - } else { - lastError = prevError; - } + errorSet.insert(error); } void WebGLRenderingContext::dispose() { @@ -405,7 +422,12 @@ GL_METHOD(New) { (Nan::To(info[9]).ToChecked())); // fail if crap if (instance->state != GLCONTEXT_STATE_OK) { - return Nan::ThrowError("Error creating WebGLContext"); + if (!instance->errorMessage.empty()) { + std::string error = std::string("Error creating WebGLContext: ") + instance->errorMessage; + return Nan::ThrowError(error.c_str()); + } else { + return Nan::ThrowError("Error creating WebGLContext"); + } } instance->Wrap(info.This()); @@ -549,11 +571,13 @@ GL_METHOD(BindAttribLocation) { } GLenum WebGLRenderingContext::getError() { - GLenum error = glGetError(); - if (lastError != GL_NO_ERROR) { - error = lastError; + GLenum error = GL_NO_ERROR; + if (errorSet.empty()) { + error = glGetError(); + } else { + error = *errorSet.begin(); + errorSet.erase(errorSet.begin()); } - lastError = GL_NO_ERROR; return error; } @@ -801,8 +825,8 @@ GL_METHOD(ClearDepth) { glClearDepthf(depth); } -// Two specific enums are accepted by ANGLE when they shouldn't be. This shows -// up for headless, but not browsers, because browsers do additional validation. +// Two specific enums are accepted by ANGLE when they shouldn't be. This shows up +// for headless, but not browsers, because browsers do additional validation. // The ANGLE fix is to make EXT_multisample_compatibility "enableable". bool IsBuggedANGLECap(GLenum cap) { return cap == GL_MULTISAMPLE || cap == GL_SAMPLE_ALPHA_TO_ONE; } @@ -849,8 +873,8 @@ GL_METHOD(BindTexture) { glBindTexture(target, texture); } -unsigned char *WebGLRenderingContext::unpackPixels(GLenum type, GLenum format, GLint width, - GLint height, unsigned char *pixels) { +std::vector WebGLRenderingContext::unpackPixels(GLenum type, GLenum format, GLint width, + GLint height, unsigned char *pixels) { // Compute pixel size GLint pixelSize = 1; @@ -883,15 +907,15 @@ unsigned char *WebGLRenderingContext::unpackPixels(GLenum type, GLenum format, G } GLint imageSize = rowStride * height; - unsigned char *unpacked = new unsigned char[imageSize]; + std::vector unpacked(imageSize); if (unpack_flip_y) { for (int i = 0, j = height - 1; j >= 0; ++i, --j) { - memcpy(reinterpret_cast(unpacked + j * rowStride), - reinterpret_cast(pixels + i * rowStride), width * pixelSize); + memcpy(&unpacked[j * rowStride], reinterpret_cast(pixels + i * rowStride), + width * pixelSize); } } else { - memcpy(reinterpret_cast(unpacked), reinterpret_cast(pixels), imageSize); + memcpy(unpacked.data(), reinterpret_cast(pixels), imageSize); } // Premultiply alpha unpacking @@ -899,7 +923,7 @@ unsigned char *WebGLRenderingContext::unpackPixels(GLenum type, GLenum format, G for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { - unsigned char *pixel = unpacked + (row * rowStride) + (col * pixelSize); + uint8_t *pixel = &unpacked[(row * rowStride) + (col * pixelSize)]; if (format == GL_LUMINANCE_ALPHA) { pixel[0] *= pixel[1] / 255.0; } else if (type == GL_UNSIGNED_BYTE) { @@ -934,15 +958,17 @@ unsigned char *WebGLRenderingContext::unpackPixels(GLenum type, GLenum format, G } void CallTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, - GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { + GLsizei height, GLint border, GLenum format, GLenum type, GLsizei bufSize, + const void *pixels) { GLenum sizedInternalFormat = SizeFloatingPointFormat(internalformat); if (type == GL_FLOAT && sizedInternalFormat != internalformat) { glTexStorage2DEXT(target, 1, sizedInternalFormat, width, height); if (pixels) { - glTexSubImage2D(target, level, 0, 0, width, height, format, type, pixels); + glTexSubImage2DRobustANGLE(target, level, 0, 0, width, height, format, type, bufSize, pixels); } } else { - glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + glTexImage2DRobustANGLE(target, level, internalformat, width, height, border, format, type, + bufSize, pixels); } } @@ -961,21 +987,15 @@ GL_METHOD(TexImage2D) { if (*pixels) { if (inst->unpack_flip_y || inst->unpack_premultiply_alpha) { - unsigned char *unpacked = inst->unpackPixels(type, format, width, height, *pixels); - CallTexImage2D(target, level, internalformat, width, height, border, format, type, unpacked); - delete[] unpacked; + std::vector unpacked = inst->unpackPixels(type, format, width, height, *pixels); + CallTexImage2D(target, level, internalformat, width, height, border, format, type, + unpacked.size(), unpacked.data()); } else { - CallTexImage2D(target, level, internalformat, width, height, border, format, type, *pixels); + CallTexImage2D(target, level, internalformat, width, height, border, format, type, + pixels.length(), *pixels); } } else { - size_t length = width * height * 4; - if (type == GL_FLOAT) { - length *= 4; - } - char *data = new char[length]; - memset(data, 0, length); - CallTexImage2D(target, level, internalformat, width, height, border, format, type, data); - delete[] data; + CallTexImage2D(target, level, internalformat, width, height, border, format, type, 0, nullptr); } } @@ -993,11 +1013,12 @@ GL_METHOD(TexSubImage2D) { Nan::TypedArrayContents pixels(info[8]); if (inst->unpack_flip_y || inst->unpack_premultiply_alpha) { - unsigned char *unpacked = inst->unpackPixels(type, format, width, height, *pixels); - glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, unpacked); - delete[] unpacked; + std::vector unpacked = inst->unpackPixels(type, format, width, height, *pixels); + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, + unpacked.size(), unpacked.data()); } else { - glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, *pixels); + glTexSubImage2DRobustANGLE(target, level, xoffset, yoffset, width, height, format, type, + pixels.length(), *pixels); } } @@ -1751,10 +1772,22 @@ GL_METHOD(GetAttachedShaders) { delete[] shaders; } +template +void ReturnParamValueOrNull(Nan::NAN_METHOD_ARGS_TYPE info, v8::Local value, + GLsizei bytesWritten) { + if (bytesWritten > 0) { + info.GetReturnValue().Set(value); + } else { + info.GetReturnValue().Set(Nan::Null()); + } +} + GL_METHOD(GetParameter) { GL_BOILERPLATE; GLenum name = Nan::To(info[0]).ToChecked(); + GLsizei bytesWritten = 0; + switch (name) { case 0x9240 /* UNPACK_FLIP_Y_WEBGL */: info.GetReturnValue().Set(Nan::New(inst->unpack_flip_y)); @@ -1777,10 +1810,10 @@ GL_METHOD(GetParameter) { case GL_SAMPLE_COVERAGE_INVERT: case GL_SCISSOR_TEST: case GL_STENCIL_TEST: { - GLboolean params; - glGetBooleanv(name, ¶ms); + GLboolean params = GL_FALSE; + glGetBooleanvRobustANGLE(name, sizeof(GLboolean), &bytesWritten, ¶ms); - info.GetReturnValue().Set(Nan::New(params != 0)); + ReturnParamValueOrNull(info, Nan::New(params != 0), bytesWritten); return; } @@ -1791,10 +1824,10 @@ GL_METHOD(GetParameter) { case GL_POLYGON_OFFSET_UNITS: case GL_SAMPLE_COVERAGE_VALUE: case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT: { - GLfloat params; - glGetFloatv(name, ¶ms); + GLfloat params = 0; + glGetFloatvRobustANGLE(name, sizeof(GLfloat), &bytesWritten, ¶ms); - info.GetReturnValue().Set(Nan::New(params)); + ReturnParamValueOrNull(info, Nan::New(params), bytesWritten); return; } @@ -1812,28 +1845,28 @@ GL_METHOD(GetParameter) { } case GL_MAX_VIEWPORT_DIMS: { - GLint params[2]; - glGetIntegerv(name, params); + GLint params[2] = {}; + glGetIntegervRobustANGLE(name, sizeof(GLint) * 2, &bytesWritten, params); v8::Local arr = Nan::New(2); Nan::Set(arr, 0, Nan::New(params[0])); Nan::Set(arr, 1, Nan::New(params[1])); - info.GetReturnValue().Set(arr); + ReturnParamValueOrNull(info, arr, bytesWritten); return; } case GL_SCISSOR_BOX: case GL_VIEWPORT: { - GLint params[4]; - glGetIntegerv(name, params); + GLint params[4] = {}; + glGetIntegervRobustANGLE(name, sizeof(GLint) * 4, &bytesWritten, params); v8::Local arr = Nan::New(4); Nan::Set(arr, 0, Nan::New(params[0])); Nan::Set(arr, 1, Nan::New(params[1])); Nan::Set(arr, 2, Nan::New(params[2])); Nan::Set(arr, 3, Nan::New(params[3])); - info.GetReturnValue().Set(arr); + ReturnParamValueOrNull(info, arr, bytesWritten); return; } @@ -1841,50 +1874,50 @@ GL_METHOD(GetParameter) { case GL_ALIASED_LINE_WIDTH_RANGE: case GL_ALIASED_POINT_SIZE_RANGE: case GL_DEPTH_RANGE: { - GLfloat params[2]; - glGetFloatv(name, params); + GLfloat params[2] = {}; + glGetFloatvRobustANGLE(name, sizeof(GLfloat) * 2, &bytesWritten, params); v8::Local arr = Nan::New(2); Nan::Set(arr, 0, Nan::New(params[0])); Nan::Set(arr, 1, Nan::New(params[1])); - info.GetReturnValue().Set(arr); + ReturnParamValueOrNull(info, arr, bytesWritten); return; } case GL_BLEND_COLOR: case GL_COLOR_CLEAR_VALUE: { - GLfloat params[4]; - glGetFloatv(name, params); + GLfloat params[4] = {}; + glGetFloatvRobustANGLE(name, sizeof(GLfloat) * 4, &bytesWritten, params); v8::Local arr = Nan::New(4); Nan::Set(arr, 0, Nan::New(params[0])); Nan::Set(arr, 1, Nan::New(params[1])); Nan::Set(arr, 2, Nan::New(params[2])); Nan::Set(arr, 3, Nan::New(params[3])); - info.GetReturnValue().Set(arr); + ReturnParamValueOrNull(info, arr, bytesWritten); return; } case GL_COLOR_WRITEMASK: { - GLboolean params[4]; - glGetBooleanv(name, params); + GLboolean params[4] = {}; + glGetBooleanvRobustANGLE(name, sizeof(GLboolean) * 4, &bytesWritten, params); v8::Local arr = Nan::New(4); Nan::Set(arr, 0, Nan::New(params[0] == GL_TRUE)); Nan::Set(arr, 1, Nan::New(params[1] == GL_TRUE)); Nan::Set(arr, 2, Nan::New(params[2] == GL_TRUE)); Nan::Set(arr, 3, Nan::New(params[3] == GL_TRUE)); - info.GetReturnValue().Set(arr); + ReturnParamValueOrNull(info, arr, bytesWritten); return; } default: { - GLint params; - glGetIntegerv(name, ¶ms); - info.GetReturnValue().Set(Nan::New(params)); + GLint params = 0; + glGetIntegervRobustANGLE(name, sizeof(GLint), &bytesWritten, ¶ms); + ReturnParamValueOrNull(info, Nan::New(params), bytesWritten); return; } } @@ -1909,7 +1942,7 @@ GL_METHOD(GetFramebufferAttachmentParameter) { GLenum attachment = Nan::To(info[1]).ToChecked(); GLenum pname = Nan::To(info[2]).ToChecked(); - GLint params; + GLint params = 0; glGetFramebufferAttachmentParameteriv(target, attachment, pname, ¶ms); info.GetReturnValue().Set(Nan::New(params)); @@ -1998,14 +2031,14 @@ GL_METHOD(GetVertexAttrib) { return; } + case GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: + if (inst->enabledExtensions.count("GL_ANGLE_instanced_arrays") == 0) { + break; + } + case GL_VERTEX_ATTRIB_ARRAY_SIZE: case GL_VERTEX_ATTRIB_ARRAY_STRIDE: - case GL_VERTEX_ATTRIB_ARRAY_TYPE: { - glGetVertexAttribiv(index, pname, &value); - info.GetReturnValue().Set(Nan::New(value)); - return; - } - + case GL_VERTEX_ATTRIB_ARRAY_TYPE: case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: { glGetVertexAttribiv(index, pname, &value); info.GetReturnValue().Set(Nan::New(value)); @@ -2023,14 +2056,14 @@ GL_METHOD(GetVertexAttrib) { Nan::Set(arr, 2, Nan::New(vextex_attribs[2])); Nan::Set(arr, 3, Nan::New(vextex_attribs[3])); info.GetReturnValue().Set(arr); - return; } default: - inst->setError(GL_INVALID_ENUM); + break; } + inst->setError(GL_INVALID_ENUM); info.GetReturnValue().SetNull(); } diff --git a/src/native/webgl.h b/src/native/webgl.h index a220d240..70610450 100644 --- a/src/native/webgl.h +++ b/src/native/webgl.h @@ -48,6 +48,7 @@ struct WebGLRenderingContext : public node::ObjectWrap { EGLConfig config; EGLSurface surface; GLContextState state; + std::string errorMessage; // Pixel storage flags bool unpack_flip_y; @@ -99,11 +100,11 @@ struct WebGLRenderingContext : public node::ObjectWrap { bool setActive(); // Unpacks a buffer full of pixels into memory - unsigned char *unpackPixels(GLenum type, GLenum format, GLint width, GLint height, - unsigned char *pixels); + std::vector unpackPixels(GLenum type, GLenum format, GLint width, GLint height, + unsigned char *pixels); // Error handling - GLenum lastError; + std::set errorSet; void setError(GLenum error); GLenum getError(); static NAN_METHOD(SetError); diff --git a/test/misc.js b/test/misc.js index 222a2f7f..4600f38e 100644 --- a/test/misc.js +++ b/test/misc.js @@ -7,5 +7,3 @@ const BLACKLIST = [ require('./util/conformance')(function (str) { return str.indexOf('misc') === 0 && BLACKLIST.indexOf(str) < 0 }) - -require('./util/stencil-check-cache') diff --git a/test/util/stencil-check-cache.js b/test/util/stencil-check-cache.js deleted file mode 100644 index ba356559..00000000 --- a/test/util/stencil-check-cache.js +++ /dev/null @@ -1,110 +0,0 @@ -const tape = require('tape') -const { WebGLRenderingContext } = require('../../src/javascript/webgl-rendering-context') -const createContext = require('../../src/javascript/node-index') - -tape('stencil check cache - gl.stencilFunc()', function (t) { - const gl = new WebGLRenderingContext() - t.equals(gl._checkStencil, undefined, 'gl._checkStencil starts undefined') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilFunc()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilFunc() - t.equals(gl._checkStencil, true, 'gl.stencilFunc() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilFuncSeparate()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilFuncSeparate() - t.equals(gl._checkStencil, true, 'gl.stencilFuncSeparate() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilMask()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilMask() - t.equals(gl._checkStencil, true, 'gl.stencilMask() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilMaskSeparate()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilMaskSeparate() - t.equals(gl._checkStencil, true, 'gl.stencilMaskSeparate() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilOp()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilOp() - t.equals(gl._checkStencil, true, 'gl.stencilOp() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.stencilOpSeparate()', function (t) { - const gl = new WebGLRenderingContext() - gl.stencilOpSeparate() - t.equals(gl._checkStencil, true, 'gl.stencilOpSeparate() calls set gl._checkStencil to true') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl.clearStencil()', function (t) { - const gl = new WebGLRenderingContext() - gl.clearStencil() - t.equals(gl._checkStencil, false, 'gl.clearStencil() calls set gl._checkStencil to false') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl._checkStencilState() without errors', function (t) { - const gl = new WebGLRenderingContext() - gl._checkStencil = true - t.equals(gl._checkStencilState(), true, 'gl._checkStencilState() value is cached and returned') - t.equals(gl._checkStencil, false, 'gl._checkStencilState() calls set gl._checkStencil to false') - t.equals(gl._stencilState, true, 'gl._checkStencilState() calls set gl._stencilState to true') - gl._stencilState = 'test value' - gl.getParameter = function () { - throw new Error('should not be called!') - } - t.equals(gl._checkStencilState(), 'test value', 'subsequent gl._checkStencilState() calls use the cached state stored in gl._stencilState') - gl.destroy() - t.end() -}) - -tape('stencil check cache - gl._checkStencilState() with errors', function (t) { - const gl = new WebGLRenderingContext() - gl._checkStencil = true - gl.getParameter = function (stencil) { - if (stencil === gl.STENCIL_WRITEMASK) return 1 - } - t.equals(gl._checkStencilState(), false, 'gl._checkStencilState() value is cached and returned') - t.equals(gl._checkStencil, false, 'gl._checkStencilState() calls set gl._checkStencil to false') - t.equals(gl._stencilState, false, 'gl._checkStencilState() calls set gl._stencilState to true') - gl._stencilState = 'test value' - gl.getParameter = function () { - throw new Error('should not be called!') - } - t.equals(gl._checkStencilState(), 'test value', 'subsequent gl._checkStencilState() calls use the cached state stored in gl._stencilState') - gl.destroy() - t.end() -}) - -tape('stencil check cache - createContext initial state', function (t) { - const gl = createContext(1, 1) - gl.getParameter = function () { - throw new Error('should not be called!') - } - gl._stencilState = 'test value' - t.equals(gl._checkStencilState(), true, 'initial call to createContext()._checkStencilState() return gl._stencilState and not call gl.getParameter()') - gl.destroy() - t.end() -})