From df9d9650702247f3c2a1cbe2f9a5ef0657462513 Mon Sep 17 00:00:00 2001 From: Jamie Madill Date: Mon, 13 Jan 2025 15:29:15 -0500 Subject: [PATCH] Remove some redundant GL validation. ANGLE itself can manage most of WebGL validation. This makes the transition to WebGL 2 validation easier because we won't need to write new logic for all the new entry points. To this effect this enables robust memory features in ANGLE, and also updates the error set logic. This contribution is funded by http://higharc.com/ --- .../extensions/angle-instanced-arrays.js | 10 - src/javascript/webgl-rendering-context.js | 1137 +---------------- src/javascript/webgl-texture.js | 2 - src/javascript/webgl-vertex-attribute.js | 1 - src/native/webgl.cc | 189 +-- src/native/webgl.h | 7 +- test/misc.js | 2 - test/util/stencil-check-cache.js | 110 -- 8 files changed, 184 insertions(+), 1274 deletions(-) delete mode 100644 test/util/stencil-check-cache.js 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() -})