From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../checkout/js/tests/canvas-tests-utils.js | 825 +++++++++++++++ .../checkout/js/tests/clipping-wide-points.js | 92 ++ .../checkout/js/tests/compositing-test.js | 136 +++ .../tests/compound-assignment-type-combination.js | 133 +++ .../checkout/js/tests/compressed-tex-image.js | 138 +++ .../checkout/js/tests/compressed-texture-utils.js | 258 +++++ .../checkout/js/tests/context-methods.js | 52 + .../js/tests/ext-color-buffer-half-float.js | 473 +++++++++ .../checkout/js/tests/ext-float-blend.js | 237 +++++ .../js/tests/ext-texture-filter-anisotropic.js | 169 +++ .../js/tests/gl-bindattriblocation-aliasing.js | 44 + .../webgl-conf/checkout/js/tests/gl-enum-tests.js | 123 +++ .../checkout/js/tests/gl-get-tex-parameter.js | 183 ++++ .../checkout/js/tests/gl-object-get-calls.js | 1090 ++++++++++++++++++++ .../checkout/js/tests/gl-vertex-attrib.js | 263 +++++ .../checkout/js/tests/instanceof-test.js | 105 ++ .../js/tests/invalid-vertex-attrib-test.js | 129 +++ .../webgl-conf/checkout/js/tests/iterable-test.js | 183 ++++ .../checkout/js/tests/line-rendering-quality.js | 163 +++ .../no-over-optimizations-on-uniform-array.js | 247 +++++ .../oes-texture-float-and-half-float-linear.js | 151 +++ .../tests/offscreencanvas-transfer-image-bitmap.js | 57 + .../checkout/js/tests/out-of-bounds-test.js | 321 ++++++ .../checkout/js/tests/ovr_multiview2_util.js | 263 +++++ .../js/tests/shader-with-non-reserved-words.js | 664 ++++++++++++ ...e-and-sub-image-2d-with-canvas-sub-rectangle.js | 292 ++++++ .../tex-image-and-sub-image-2d-with-canvas.js | 468 +++++++++ ...and-sub-image-2d-with-image-bitmap-from-blob.js | 49 + ...d-sub-image-2d-with-image-bitmap-from-canvas.js | 74 ++ ...image-2d-with-image-bitmap-from-image-bitmap.js | 56 + ...b-image-2d-with-image-bitmap-from-image-data.js | 49 + ...nd-sub-image-2d-with-image-bitmap-from-image.js | 46 + ...nd-sub-image-2d-with-image-bitmap-from-video.js | 83 ++ .../tex-image-and-sub-image-2d-with-image-data.js | 240 +++++ .../tests/tex-image-and-sub-image-2d-with-image.js | 257 +++++ .../tex-image-and-sub-image-2d-with-svg-image.js | 140 +++ .../tests/tex-image-and-sub-image-2d-with-video.js | 291 ++++++ ...tex-image-and-sub-image-2d-with-webgl-canvas.js | 298 ++++++ ...e-and-sub-image-3d-with-canvas-sub-rectangle.js | 287 ++++++ .../tex-image-and-sub-image-3d-with-canvas.js | 226 ++++ ...and-sub-image-3d-with-image-bitmap-from-blob.js | 48 + ...d-sub-image-3d-with-image-bitmap-from-canvas.js | 74 ++ ...image-3d-with-image-bitmap-from-image-bitmap.js | 56 + ...b-image-3d-with-image-bitmap-from-image-data.js | 49 + ...nd-sub-image-3d-with-image-bitmap-from-image.js | 45 + ...nd-sub-image-3d-with-image-bitmap-from-video.js | 83 ++ .../tex-image-and-sub-image-3d-with-image-data.js | 259 +++++ .../tests/tex-image-and-sub-image-3d-with-image.js | 260 +++++ .../tex-image-and-sub-image-3d-with-svg-image.js | 104 ++ .../tests/tex-image-and-sub-image-3d-with-video.js | 244 +++++ ...tex-image-and-sub-image-3d-with-webgl-canvas.js | 212 ++++ .../js/tests/tex-image-and-sub-image-utils.js | 865 ++++++++++++++++ ...-image-and-sub-image-with-image-bitmap-utils.js | 435 ++++++++ .../checkout/js/tests/tex-input-validation.js | 563 ++++++++++ .../js/tests/texture-corner-case-videos.js | 299 ++++++ .../checkout/js/tests/typed-array-test-cases.js | 73 ++ .../checkout/js/tests/typed-array-worker.js | 72 ++ .../tests/webgl-compressed-texture-size-limit.js | 226 ++++ .../checkout/js/tests/webgl-draw-buffers-utils.js | 69 ++ 59 files changed, 13391 insertions(+) create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/clipping-wide-points.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/compositing-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js (limited to 'dom/canvas/test/webgl-conf/checkout/js/tests') diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js new file mode 100644 index 0000000000..7c30104379 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/canvas-tests-utils.js @@ -0,0 +1,825 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// Some variables that will be used in this file +var canvas; +var gl; +var OES_vertex_array_object; +var uniformLocation; +var extension; +var buffer; +var framebuffer; +var program; +var renderbuffer; +var shader; +var texture; +var arrayBuffer; +var arrayBufferView; +var vertexArrayObject; +var imageData; +var float32array; +var int32array; + +var OES_texture_float; +var new_WEBGL_lose_context; +var allowRestore; +var contextLostEventFired; +var contextRestoredEventFired; +var newExtension; + +function compareGLError(glError, evalStr) { + var exception; + try { + eval(evalStr); + } catch (e) { + exception = e; + } + if (exception) { + return false; + } else { + if (gl.getError() == glError) + return true; + return false; + } +} + +function contextCreation(contextType) { + canvas = new OffscreenCanvas(10, 10); + gl = canvas.getContext(contextType); + + if (contextType == 'webgl') { + if (gl instanceof WebGLRenderingContext) + return true; + return false; + } else if (contextType == 'webgl2') { + if (gl instanceof WebGL2RenderingContext) + return true; + return false; + } else { + return false; + } +} + +function transferredOffscreenCanvasCreation(placeholder, width, height) { + placeholder.width = width; + placeholder.height = height; + return placeholder.transferControlToOffscreen(); +} + +function assertWidthAndHeight(entity, entityName, width, height) { + if (entity.width == width && entity.height == height) { + testPassed("The width and height of " + entityName + " are correct."); + return; + } + var errMsg = ""; + if (entity.width != width) { + errMsg += "The width of " + entityName + " is " + entity.width + " while expected value is " + width + ". "; + } + if (entity.height != height) { + errMsg += "The height of " + entityName + " is " + entity.height + " while expected value is " + height + ". "; + } + testFailed(errMsg); +} + +var webgl1Methods = [ + "getContextAttributes", + "activeTexture", + "attachShader", + "bindAttribLocation", + "bindBuffer", + "bindFramebuffer", + "bindRenderbuffer", + "bindTexture", + "blendColor", + "blendEquation", + "blendEquationSeparate", + "blendFunc", + "blendFuncSeparate", + "bufferData", + "bufferSubData", + "checkFramebufferStatus", + "clear", + "clearColor", + "clearDepth", + "clearStencil", + "colorMask", + "compileShader", + "compressedTexImage2D", + "compressedTexSubImage2D", + "copyTexImage2D", + "copyTexSubImage2D", + "createBuffer", + "createFramebuffer", + "createProgram", + "createRenderbuffer", + "createShader", + "createTexture", + "cullFace", + "deleteBuffer", + "deleteFramebuffer", + "deleteProgram", + "deleteRenderbuffer", + "deleteShader", + "deleteTexture", + "depthFunc", + "depthMask", + "depthRange", + "detachShader", + "disable", + "disableVertexAttribArray", + "drawArrays", + "drawElements", + "enable", + "enableVertexAttribArray", + "finish", + "flush", + "framebufferRenderbuffer", + "framebufferTexture2D", + "frontFace", + "generateMipmap", + "getActiveAttrib", + "getActiveUniform", + "getAttachedShaders", + "getAttribLocation", + "getParameter", + "getBufferParameter", + "getError", + "getExtension", + "getFramebufferAttachmentParameter", + "getProgramParameter", + "getProgramInfoLog", + "getRenderbufferParameter", + "getShaderParameter", + "getShaderInfoLog", + "getShaderPrecisionFormat", + "getShaderSource", + "getSupportedExtensions", + "getTexParameter", + "getUniform", + "getUniformLocation", + "getVertexAttrib", + "getVertexAttribOffset", + "hint", + "isBuffer", + "isContextLost", + "isEnabled", + "isFramebuffer", + "isProgram", + "isRenderbuffer", + "isShader", + "isTexture", + "lineWidth", + "linkProgram", + "pixelStorei", + "polygonOffset", + "readPixels", + "renderbufferStorage", + "sampleCoverage", + "scissor", + "shaderSource", + "stencilFunc", + "stencilFuncSeparate", + "stencilMask", + "stencilMaskSeparate", + "stencilOp", + "stencilOpSeparate", + "texImage2D", + "texParameterf", + "texParameteri", + "texSubImage2D", + "uniform1f", + "uniform1fv", + "uniform1i", + "uniform1iv", + "uniform2f", + "uniform2fv", + "uniform2i", + "uniform2iv", + "uniform3f", + "uniform3fv", + "uniform3i", + "uniform3iv", + "uniform4f", + "uniform4fv", + "uniform4i", + "uniform4iv", + "uniformMatrix2fv", + "uniformMatrix3fv", + "uniformMatrix4fv", + "useProgram", + "validateProgram", + "vertexAttrib1f", + "vertexAttrib1fv", + "vertexAttrib2f", + "vertexAttrib2fv", + "vertexAttrib3f", + "vertexAttrib3fv", + "vertexAttrib4f", + "vertexAttrib4fv", + "vertexAttribPointer", + "viewport", +]; + +var webgl2Methods = [ + "getBufferSubData", + "copyBufferSubData", + "blitFramebuffer", + "framebufferTextureLayer", + "getInternalformatParameter", + "invalidateFramebuffer", + "invalidateSubFramebuffer", + "readBuffer", + "renderbufferStorageMultisample", + "texImage3D", + "texStorage2D", + "texStorage3D", + "texSubImage3D", + "copyTexSubImage3D", + "compressedTexImage3D", + "compressedTexSubImage3D", + "getFragDataLocation", + "uniform1ui", + "uniform2ui", + "uniform3ui", + "uniform4ui", + "uniform1uiv", + "uniform2uiv", + "uniform3uiv", + "uniform4uiv", + "uniformMatrix2x3fv", + "uniformMatrix3x2fv", + "uniformMatrix2x4fv", + "uniformMatrix4x2fv", + "uniformMatrix3x4fv", + "uniformMatrix4x3fv", + "vertexAttribI4i", + "vertexAttribI4iv", + "vertexAttribI4ui", + "vertexAttribI4uiv", + "vertexAttribIPointer", + "vertexAttribDivisor", + "drawArraysInstanced", + "drawElementsInstanced", + "drawRangeElements", + "drawBuffers", + "clearBufferiv", + "clearBufferuiv", + "clearBufferfv", + "clearBufferfi", + "createQuery", + "deleteQuery", + "isQuery", + "beginQuery", + "endQuery", + "getQuery", + "getQueryParameter", + "createSampler", + "deleteSampler", + "isSampler", + "bindSampler", + "samplerParameteri", + "samplerParameterf", + "getSamplerParameter", + "fenceSync", + "isSync", + "deleteSync", + "clientWaitSync", + "waitSync", + "getSyncParameter", + "createTransformFeedback", + "deleteTransformFeedback", + "isTransformFeedback", + "bindTransformFeedback", + "beginTransformFeedback", + "endTransformFeedback", + "transformFeedbackVaryings", + "getTransformFeedbackVarying", + "pauseTransformFeedback", + "resumeTransformFeedback", + "bindBufferBase", + "bindBufferRange", + "getIndexedParameter", + "getUniformIndices", + "getActiveUniforms", + "getUniformBlockIndex", + "getActiveUniformBlockParameter", + "getActiveUniformBlockName", + "uniformBlockBinding", + "createVertexArray", + "deleteVertexArray", + "isVertexArray", + "bindVertexArray", +]; + +function assertFunction(v, f) { + try { + if (typeof v[f] != "function") { + return false; + } else { + return true; + } + } catch(e) { + return false; + } +} + +function testAPIs(contextType) { + canvas = new OffscreenCanvas(10, 10); + gl = canvas.getContext(contextType); + var passed = true; + var methods; + if (contextType == 'webgl') + methods = webgl1Methods; + else + methods = webgl1Methods.concat(webgl2Methods); + for (var i=0; i wtu.waitForComposite(resolve)); +} + +async function testPreserveDrawingBufferFalse(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: false with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + if (drawFn(gl)) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + // enable scissor here, before compositing, to make sure it's correctly + // ignored and restored + const halfWidth = gl.canvas.width / 2; + const halfHeight = gl.canvas.height / 2; + gl.scissor(0, halfHeight, halfWidth, halfHeight); + gl.enable(gl.SCISSOR_TEST); + + await waitForComposite(); + + // scissor was set earlier + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + wtu.checkCanvasRect(gl, 0, halfHeight, halfWidth, halfHeight, [0, 0, 255, 255], + "cleared corner should be blue, stencil should be preserved"); + wtu.checkCanvasRect(gl, 0, 0, halfWidth, halfHeight, [0, 0, 0, 0], + "remainder of buffer should be cleared"); + + gl.disable(gl.SCISSOR_TEST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +async function testPreserveDrawingBufferTrue(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: true with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + const skipTest = drawFn(gl); + if (skipTest) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + await waitForComposite(); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function setupWebGL({ + webglVersion, + shadersFn, + attribs, +}) { + const existingCanvases = document.querySelectorAll('canvas'); + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + canvas.style.display = 'block'; + canvas.style.position = 'fixed'; + canvas.style.left = `${existingCanvases.length * 25}px`; + canvas.style.top = '0'; + // The canvas needs to be visible or the test will fail. + document.body.insertBefore(canvas, [...existingCanvases].pop()); + const gl = wtu.create3DContext(canvas, attribs, webglVersion); + if (!gl) { + testFailed('WebGL context creation failed'); + return gl; + } + + const shaders = shadersFn(gl); + const program = wtu.setupProgram(gl, shaders, ["position"]); + if (!program) { + debug(`program failed to compile: ${wtu.getLastError()}`); + } + const positionBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + const indexBuf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); + return gl; +} + +function createCompositingTestFn(options) { + const glPreserveDrawingBufferFalse = setupWebGL({ + ...options, + attribs: {antialias: false}, + }); + const glPreserveDrawingBufferTrue = setupWebGL({ + ...options, + attribs: {antialias: false, preserveDrawingBuffer: true}, + }); + return async function(drawFn) { + debug('---'); + await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn, false); + await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn, true); + + await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn, false); + await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn, true); + }; +} + +return createCompositingTestFn; +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js new file mode 100644 index 0000000000..41a49bced4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compound-assignment-type-combination.js @@ -0,0 +1,133 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +// ESSL 1.00 spec section 5.8 (also ESSL 3.00 spec section 5.8): +// "The l-value and the expression must satisfy the semantic requirements of both op and equals (=)" +// In the semantic requirements of assignment (=): +// "The lvalue-expression and rvalue-expression must have the same type" + +var runTest = function(contextVersion) { + var vertexTemplateESSL1 = [ + 'precision mediump float;', + + 'uniform $(rtype) ur;', + 'uniform $(ltype) ul;', + + 'void main() {', + ' $(ltype) a = ul;', + ' a $(op) ur;', + ' gl_Position = vec4(float(a$(ltypeToScalar)));', + '}' + ].join('\n'); + + var vertexTemplateESSL3 = [ + '#version 300 es', + vertexTemplateESSL1 + ].join('\n'); + + var fragmentTemplateESSL1 = [ + 'precision mediump float;', + + 'uniform $(rtype) ur;', + 'uniform $(ltype) ul;', + + 'void main() {', + ' $(ltype) a = ul;', + ' a $(op) ur;', + ' gl_FragColor = vec4(float(a$(ltypeToScalar)));', + '}' + ].join('\n'); + + var fragmentTemplateESSL3 = [ + '#version 300 es', + 'out mediump vec4 my_FragColor;', + fragmentTemplateESSL1 + ].join('\n').replace('gl_FragColor', 'my_FragColor'); + + var isNonSquareMatrix = function(typeStr) { + return typeStr.substring(0, 3) == 'mat' && + typeStr.length > 5 && + typeStr[3] != typeStr[5]; + } + + var vsTemplate = contextVersion < 2 ? vertexTemplateESSL1 : vertexTemplateESSL3; + var fsTemplate = contextVersion < 2 ? fragmentTemplateESSL1 : fragmentTemplateESSL3; + + var wtu = WebGLTestUtils; + + var tests = []; + + var baseTypes = ['float', 'int']; + var vecTypes = [['vec2', 'vec3', 'vec4', 'mat2', 'mat3', 'mat4'], ['ivec2', 'ivec3', 'ivec4']]; + if (contextVersion >= 2) { + vecTypes[0] = ['vec2', 'vec3', 'vec4', 'mat2x2', 'mat3x3', 'mat4x4', 'mat2x3', 'mat2x4', 'mat3x2', 'mat3x4', 'mat4x2', 'mat4x3']; + } + var ops = ['+=', '-=', '*=', '/=']; + + var fs, vs; + for (var k = 0; k < ops.length; ++k) { + var op = ops[k]; + for (var i = 0; i < baseTypes.length; ++i) { + var baseType = baseTypes[i]; + for (var j = 0; j < vecTypes[i].length; ++j) { + var vecType = vecTypes[i][j]; + var vecTypeToScalar = vecType.substring(0, 3) == 'mat' ? '[0].x' : '.x'; + + var pushTest = function(ltype, rtype, ltypeToScalar, expectSuccess) { + vs = wtu.replaceParams(vsTemplate, {ltype: ltype, rtype: rtype, ltypeToScalar: ltypeToScalar, op: op}); + fs = wtu.replaceParams(fsTemplate, {ltype: ltype, rtype: rtype, ltypeToScalar: ltypeToScalar, op: op}); + tests.push({ + vShaderSource: vs, + vShaderSuccess: expectSuccess, + linkSuccess: expectSuccess, + passMsg: ltype + " " + op + " " + rtype + " in a vertex shader should " + (expectSuccess ? "succeed." : "fail.") + }); + tests.push({ + fShaderSource: fs, + fShaderSuccess: expectSuccess, + linkSuccess: expectSuccess, + passMsg: ltype + " " + op + " " + rtype + " in a fragment shader should " + (expectSuccess ? "succeed." : "fail.") + }); + } + + // "scalar op= vector" is not okay, since the result of op is a vector, + // which can't be assigned to a scalar. + pushTest(baseType, vecType, '', false); + + if (j > 0) { + var vecType2 = vecTypes[i][j - 1]; + // "vector1 op= vector2" is not okay when vector1 and vector2 have + // non-matching dimensions. + pushTest(vecType, vecType2, vecTypeToScalar, false); + } + + // "vector op= scalar" is okay. + pushTest(vecType, baseType, vecTypeToScalar, true); + + // vecX *= matX is okay (effectively, this treats vector as a row vector). + if (vecType.substring(0, 3) == 'vec' && op == '*=') { + pushTest(vecType, 'mat' + vecType[3], vecTypeToScalar, true); + } + + if (op != '*=' || !isNonSquareMatrix(vecType)) { + // "vector1 op= vector2" is okay when vector1 and vector2 have the same + // type (does a component-wise operation or matrix multiplication). + pushTest(vecType, vecType, vecTypeToScalar, true); + } else { + // non-square matrices can only be compound multiplied with a square matrix. + pushTest(vecType, vecType, vecTypeToScalar, false); + pushTest(vecType, 'mat' + vecType[3], vecTypeToScalar, true); + } + } + } + } + + GLSLConformanceTester.runTests(tests, contextVersion); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js new file mode 100644 index 0000000000..7886181f4c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-tex-image.js @@ -0,0 +1,138 @@ +"use strict"; +description("This test ensures WebGL implementations correctly implement querying for compressed textures when extensions are disabled."); + +debug(""); + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext(null, undefined, contextVersion); + +const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; +const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; + +let formats = null; +let ext; + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], + "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 10, 10, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8));"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)"); + shouldBeNonNull("formats"); + shouldBe("formats.length", "0"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4*4*4));"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, + "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8));"); + + // Check too-many and too-few args. + + wtu.shouldThrow(gl, false, "too many args", function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 4, 4, 0, new Uint8Array(8), null); + }); + wtu.shouldThrow(gl, TypeError, "too few args", function() { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 4, 4, 0); + }); + + wtu.shouldThrow(gl, false, "too many args", function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, new Uint8Array(8), null); + }); + wtu.shouldThrow(gl, TypeError, "too few args", function() { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_RGB_PVRTC_4BPPV1_IMG); + }); + + // - + + let pbo; + // WebGL 2.0 specific + if (gl.PIXEL_UNPACK_BUFFER) { + pbo = gl.createBuffer(); + } + + gl.bindTexture(gl.TEXTURE_2D, tex); + + function validateExt(extName, enumName, blockSize, blockByteSize, expectedSubImageError) { + debug('\n---------------------------'); + debug('\n' + extName); + ext = gl.getExtension(extName); + if (!ext) { + testPassed(`Optional ext ${extName} MAY be unsupported.`); + return; + } + testPassed(`Optional ext ${extName} is supported.`); + + const data = new Uint8Array(blockByteSize); + + const views = [ + data, + new Uint8ClampedArray(data.buffer), + new Int8Array(data.buffer), + new Uint16Array(data.buffer), + new Int16Array(data.buffer), + new Uint32Array(data.buffer), + new Int32Array(data.buffer), + new Float32Array(data.buffer), + new DataView(data.buffer), + ]; + if (window.SharedArrayBuffer) { + const sharedBuffer = new SharedArrayBuffer(blockByteSize); + views.push( + new Uint8Array(sharedBuffer), + new Uint8ClampedArray(sharedBuffer), + new DataView(sharedBuffer) + ); + } + + for (const view of views) { + window.g_view = view; + debug(`\nfrom ${view.constructor.name} of ${view.buffer.constructor.name}`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, g_view)`); + + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, g_view)`); + } + + if (pbo) { + debug('\nfrom PBO'); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.bufferData(gl.PIXEL_UNPACK_BUFFER, ${blockByteSize}*2, gl.STATIC_DRAW)`); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, 0)`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, 1)`); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, ${blockByteSize})`); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, + `gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ext.${enumName}, ${blockSize},${blockSize}, 0, ${blockByteSize}, ${blockByteSize+1})`); + + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, 0)`); + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, 1)`); + wtu.shouldGenerateGLError(gl, expectedSubImageError, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, ${blockByteSize})`); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, + `gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0,0, ${blockSize},${blockSize}, ext.${enumName}, ${blockByteSize}, ${blockByteSize+1})`); + + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + } + } + + validateExt('WEBGL_compressed_texture_s3tc', 'COMPRESSED_RGBA_S3TC_DXT5_EXT', 4, 16, gl.NO_ERROR); + validateExt('WEBGL_compressed_texture_etc1', 'COMPRESSED_RGB_ETC1_WEBGL', 4, 8, gl.INVALID_OPERATION); + validateExt('WEBGL_compressed_texture_etc', 'COMPRESSED_RGBA8_ETC2_EAC', 4, 16, gl.NO_ERROR); + validateExt('WEBGL_compressed_texture_astc', 'COMPRESSED_RGBA_ASTC_4x4_KHR', 4, 16, gl.NO_ERROR); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js new file mode 100644 index 0000000000..46d155f5f1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js @@ -0,0 +1,258 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +let CompressedTextureUtils = (function() { + +let formatToString = function(ext, format) { + for (let p in ext) { + if (ext[p] == format) { + return p; + } + } + return "0x" + format.toString(16); +}; + +/** + * Make an image element from Uint8Array bitmap data. + * @param {number} imageHeight Height of the data in pixels. + * @param {number} imageWidth Width of the data in pixels. + * @param {number} dataWidth Width of each row in the data buffer, in pixels. + * @param {Uint8Array} data Image data buffer to display. Each pixel takes up 4 bytes in the array regardless of the alpha parameter. + * @param {boolean} alpha True if alpha data should be taken from data. Otherwise alpha channel is set to 255. + * @return {HTMLImageElement} The image element. + */ +let makeScaledImage = function(imageWidth, imageHeight, dataWidth, data, alpha, opt_scale) { + let scale = opt_scale ? opt_scale : 8; + let c = document.createElement("canvas"); + c.width = imageWidth * scale; + c.height = imageHeight * scale; + let ctx = c.getContext("2d"); + for (let yy = 0; yy < imageHeight; ++yy) { + for (let xx = 0; xx < imageWidth; ++xx) { + let offset = (yy * dataWidth + xx) * 4; + ctx.fillStyle = "rgba(" + + data[offset + 0] + "," + + data[offset + 1] + "," + + data[offset + 2] + "," + + (alpha ? data[offset + 3] / 255 : 1) + ")"; + ctx.fillRect(xx * scale, yy * scale, scale, scale); + } + } + return wtu.makeImageFromCanvas(c); +}; + +let insertCaptionedImg = function(parent, caption, img) { + let div = document.createElement("div"); + div.appendChild(img); + let label = document.createElement("div"); + label.appendChild(document.createTextNode(caption)); + div.appendChild(label); + parent.appendChild(div); +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} compressedFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + */ +let testCompressedFormatsUnavailableWhenExtensionDisabled = function(gl, compressedFormats, expectedByteLength, testSize) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + for (let name in compressedFormats) { + if (compressedFormats.hasOwnProperty(name)) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, compressedFormats[name], testSize, testSize, 0, new Uint8Array(expectedByteLength(testSize, testSize, compressedFormats[name]))); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with extension disabled."); + } + } + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} expectedFormats Mapping from format names to format enum values. + */ +let testCompressedFormatsListed = function(gl, expectedFormats) { + debug(""); + debug("Testing that every format is listed by the compressed texture formats query"); + + let supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); + + let failed; + let count = 0; + for (let name in expectedFormats) { + if (expectedFormats.hasOwnProperty(name)) { + ++count; + let format = expectedFormats[name]; + failed = true; + for (let ii = 0; ii < supportedFormats.length; ++ii) { + if (format == supportedFormats[ii]) { + testPassed("supported format " + name + " exists"); + failed = false; + break; + } + } + if (failed) { + testFailed("supported format " + name + " does not exist"); + } + } + } + if (supportedFormats.length != count) { + testFailed("Incorrect number of supported formats, was " + supportedFormats.length + " should be " + count); + } +}; + +/** + * @param {Object} ext Compressed texture extension object. + * @param {Object} expectedFormats Mapping from format names to format enum values. + */ +let testCorrectEnumValuesInExt = function(ext, expectedFormats) { + debug(""); + debug("Testing that format enum values in the extension object are correct"); + + for (name in expectedFormats) { + if (expectedFormats.hasOwnProperty(name)) { + if (isResultCorrect(ext[name], expectedFormats[name])) { + testPassed("Enum value for " + name + " matches 0x" + ext[name].toString(16)); + } else { + testFailed("Enum value for " + name + " mismatch: 0x" + ext[name].toString(16) + " should be 0x" + expectedFormats[name].toString(16)); + } + } + } +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} validFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + * @param getBlockDimensions A function that takes in a format and returns block size in pixels. + */ +let testFormatRestrictionsOnBufferSize = function(gl, validFormats, expectedByteLength, getBlockDimensions) { + debug(""); + debug("Testing format restrictions on texture upload buffer size"); + + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + let expectedSize = expectedByteLength(blockSize.width * 4, blockSize.height * 4, format); + let data = new Uint8Array(expectedSize); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 3, blockSize.height * 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small width)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 5, blockSize.height * 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large width)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 3, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small height)"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 5, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large height)"); + } + } +}; + +/** + * @param {WebGLRenderingContextBase} gl + * @param {Object} validFormats Mapping from format names to format enum values. + * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. + * @param getBlockDimensions A function that takes in a format and returns block size in pixels. + * @param {number} width Width of the image in pixels. + * @param {number} height Height of the image in pixels. + * @param {Object} subImageConfigs configs for compressedTexSubImage calls + */ +let testTexSubImageDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, width, height, subImageConfigs) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + let expectedSize = expectedByteLength(width, height, format); + let data = new Uint8Array(expectedSize); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setting up compressed texture"); + + for (let i = 0, len = subImageConfigs.length; i < len; ++i) { + let c = subImageConfigs[i]; + let subData = new Uint8Array(expectedByteLength(c.width, c.height, format)); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, c.xoffset, c.yoffset, c.width, c.height, format, subData); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +}; + +let testTexImageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + for (let formatId in validFormats) { + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + + for (let i = 0, len = imageConfigs.length; i < len; ++i) { + let c = imageConfigs[i]; + let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); + gl.compressedTexImage2D(gl.TEXTURE_2D, c.level, format, c.width, c.height, 0, data); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); +} + +let testTexStorageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { + for (let formatId in validFormats) { + let tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + if (validFormats.hasOwnProperty(formatId)) { + let format = validFormats[formatId]; + let blockSize = getBlockDimensions(format); + debug("testing " + ctu.formatToString(ext, format)); + + for (let i = 0, len = imageConfigs.length; i < len; ++i) { + let c = imageConfigs[i]; + let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); + if (i == 0) { + gl.texStorage2D(gl.TEXTURE_2D, imageConfigs.length, format, c.width, c.height); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + gl.compressedTexSubImage2D(gl.TEXTURE_2D, i, 0, 0, c.width, c.height, format, data); + wtu.glErrorShouldBe(gl, c.expectation, c.message); + } + } + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(tex); + } +} + +return { + formatToString: formatToString, + insertCaptionedImg: insertCaptionedImg, + makeScaledImage: makeScaledImage, + testCompressedFormatsListed: testCompressedFormatsListed, + testCompressedFormatsUnavailableWhenExtensionDisabled: testCompressedFormatsUnavailableWhenExtensionDisabled, + testCorrectEnumValuesInExt: testCorrectEnumValuesInExt, + testFormatRestrictionsOnBufferSize: testFormatRestrictionsOnBufferSize, + testTexSubImageDimensions: testTexSubImageDimensions, + testTexImageLevelDimensions: testTexImageLevelDimensions, + testTexStorageLevelDimensions: testTexStorageLevelDimensions, +}; + +})(); \ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js b/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js new file mode 100644 index 0000000000..f6476463d2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/context-methods.js @@ -0,0 +1,52 @@ +"use strict"; + +// Properties to be ignored because they were added in versions of the +// spec that are backward-compatible with this version +const IGNORED_METHODS = [ + // There is no official spec for the commit API yet, the proposal link is: + // https://wiki.whatwg.org/wiki/OffscreenCanvas + "commit", + + // For WebXR integration: + "makeXRCompatible", +]; + +function assertFunction(v, f) { + try { + if (typeof v[f] != "function") { + testFailed(`Property either does not exist or is not a function: ${f}`); + return false; + } else { + return true; + } + } catch(e) { + testFailed(`Trying to access the property '${f}' threw an error: ${e.toString()}`); + } +} + +function testContextMethods(gl, requiredContextMethods) { + const acceptableMethods = [].concat(requiredContextMethods, IGNORED_METHODS); + + let passed = true; + requiredContextMethods.forEach(method => { + const r = assertFunction(gl, method); + passed = passed && r; + }); + if (passed) { + testPassed("All WebGL methods found."); + } + let extended = false; + for (let propertyName of Object.getOwnPropertyNames(gl)) { + if (typeof gl[propertyName] == "function" && !acceptableMethods.includes(propertyName)) { + if (!extended) { + extended = true; + testFailed("Also found the following extra methods:"); + } + testFailed(propertyName); + } + } + + if (!extended) { + testPassed("No extra methods found on WebGL context."); + } +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js new file mode 100644 index 0000000000..51509e8a6e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js @@ -0,0 +1,473 @@ +"use strict"; + +function allocateTexture() +{ + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +function checkRenderingResults() +{ + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function arrayToString(arr, size) { + var mySize; + if (!size) + mySize = arr.length; + else + mySize = size; + var out = "["; + for (var ii = 0; ii < mySize; ++ii) { + if (ii > 0) { + out += ", "; + } + out += arr[ii]; + } + return out + "]"; +} + +function runReadbackTest(testProgram, subtractor) +{ + // Verify floating point readback + debug("Checking readback of floating-point values"); + var buf = new Float32Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, buf); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels from floating-point framebuffer should succeed"); + var ok = true; + var tolerance = 8.0; // TODO: factor this out from both this test and the subtractor shader above. + for (var ii = 0; ii < buf.length; ++ii) { + if (Math.abs(buf[ii] - subtractor[ii]) > tolerance) { + ok = false; + break; + } + } + if (ok) { + testPassed("readPixels of float-type data from floating-point framebuffer succeeded"); + } else { + testFailed("readPixels of float-type data from floating-point framebuffer failed: expected " + + arrayToString(subtractor, 4) + ", got " + arrayToString(buf)); + } +} + +function runFloatTextureRenderTargetTest(enabled, internalFormatString, format, type, testProgram, numberOfChannels, subtractor, texSubImageCover) +{ + let internalFormat = eval(internalFormatString); + debug(""); + debug("testing floating-point " + internalFormatString + " texture render target" + (texSubImageCover > 0 ? " after calling texSubImage" : "")); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed"); + + // Try to use this texture as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (!enabled) { + if (completeStatus == gl.FRAMEBUFFER_COMPLETE && !enabled) + testFailed("floating-point " + internalFormatString + " render target should not be supported"); + else + testPassed("floating-point " + internalFormatString + " render target should not be supported"); + return; + } + + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + if (version == 1 && format == gl.RGB) + testPassed("floating-point " + internalFormatString + " render target not supported; this is allowed.") + else + testFailed("floating-point " + internalFormatString + " render target not supported"); + return; + } + + if (texSubImageCover > 0) { + // Ensure that replacing the whole texture or a part of it with texSubImage2D doesn't affect renderability + gl.bindTexture(gl.TEXTURE_2D, texture); + var data = new Float32Array(width * height * numberOfChannels * texSubImageCover); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height * texSubImageCover, format, type, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texSubImage2D should succeed if EXT_color_buffer_half_float is enabled"); + gl.bindTexture(gl.TEXTURE_2D, null); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("render target support changed after calling texSubImage2D"); + return; + } + } + + var renderProgram = + wtu.setupProgram(gl, + [wtu.simpleVertexShader, `void main() + { + gl_FragColor = vec4(1000.0, 1000.0, 1000.0, 1000.0); + }`], + ['vPosition'], + [0]); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering to floating-point texture should succeed"); + + // Now sample from the floating-point texture and verify we got the correct values. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.useProgram(testProgram); + gl.uniform1i(gl.getUniformLocation(testProgram, "tex"), 0); + gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering from floating-point texture should succeed"); + checkRenderingResults(); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + runReadbackTest(testProgram, subtractor); +} + +function runFloatRenderbufferRenderTargetTest(enabled, internalFormatString, testProgram, numberOfChannels, subtractor) +{ + var internalFormat = eval(internalFormatString); + var samples = [0]; + if (enabled && version > 1) { + samples = Array.prototype.slice.call(gl.getInternalformatParameter(gl.RENDERBUFFER, internalFormat, gl.SAMPLES)); + samples.push(0); + } + for (var ndx = 0; ndx < samples.length; ++ndx) { + debug(""); + debug("testing floating-point " + internalFormatString + " renderbuffer render target with number of samples " + samples[ndx]); + + var colorbuffer = gl.createRenderbuffer(); + var width = 2; + var height = 2; + gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer); + if (samples[ndx] == 0) + gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); + else + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[ndx], internalFormat, width, height); + if (!enabled) { + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "floating-point renderbuffer allocation should fail if EXT_color_buffer_half_float is not enabled or this is a 32 bit format"); + return; + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point renderbuffer allocation should succeed if EXT_color_buffer_half_float is enabled"); + } + + // Try to use this renderbuffer as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorbuffer); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + testFailed("floating-point " + internalFormatString + " render target not supported"); + return; + } + var resolveColorRbo = null; + var resolveFbo = null; + if (samples[ndx] > 0) { + resolveColorRbo = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, resolveColorRbo); + gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height); + resolveFbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, resolveColorRbo); + completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Failed to create resolve framebuffer"); + return; + } + } + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.clearColor(1000.0, 1000.0, 1000.0, 1000.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + if (samples[ndx] > 0) { + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resolveFbo); + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, resolveFbo); + } + runReadbackTest(testProgram, subtractor); + } +} + +function runRGB16FNegativeTest() +{ + debug(""); + debug("testing RGB16F isn't color renderable"); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, width, height, 0, gl.RGB, gl.FLOAT, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "RGB16F texture allocation should succeed"); + + // Try to use this texture as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + + var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (completeStatus == gl.FRAMEBUFFER_COMPLETE) + testFailed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_half_float"); + else + testPassed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_half_float"); + gl.deleteTexture(texture); + + var colorbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB16F, width, height); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "RGB16F renderbuffer allocation should fail with or without enabling EXT_color_buffer_half_float"); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.deleteRenderbuffer(colorbuffer); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteFramebuffer(fbo); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + gl.getExtension("EXT_color_buffer_half_float").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("EXT_color_buffer_half_float").myProperty', '2'); +} + +function runInternalFormatQueryTest() +{ + debug(""); + debug("testing the internal format query"); + + var maxSamples = gl.getParameter(gl.MAX_SAMPLES); + const formats = [gl.RGBA16F, gl.R16F, gl.RG16F]; + var firstMultiOnlyFormat = 4; + for (var fmt = 0; fmt < formats.length; ++fmt) { + var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, formats[fmt], gl.SAMPLES); + if (fmt >= firstMultiOnlyFormat && (samples.length == 0 || samples[0] < maxSamples)) { + testFailed("the maximum value in SAMPLES should be at least " + maxSamples); + return; + } + + var prevSampleCount = 0; + var sampleCount; + for (var ndx = 0; ndx < samples.length; ++ndx, prevSampleCount = sampleCount) { + sampleCount = samples[ndx]; + // sample count must be > 0 + if (sampleCount <= 0) { + testFailed("Expected sample count to be at least one; got " + sampleCount); + return; + } + + // samples must be ordered descending + if (ndx > 0 && sampleCount >= prevSampleCount) { + testFailed("Expected sample count to be ordered in descending order; got " + prevSampleCount + " at index " + (ndx - 1) + ", and " + sampleCount + " at index " + ndx); + return; + } + } + } + testPassed("Internal format query succeeded"); +} + +function runCopyTexImageTest(enabled) +{ + var width = 16; + var height = 16; + var level = 0; + var cases = [ + { internalformat: "RGBA16F", format: "RGBA", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RG16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RGB16F", valid: true, renderable: true, }, + { internalformat: "RGBA16F", format: "RGBA", destFormat: "RGBA16F", valid: true, renderable: true, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "R16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RG16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RGB16F", valid: true, renderable: false, }, + { internalformat: "RGB16F", format: "RGB", destFormat: "RGBA16F", valid: false, renderable: false, }, + { internalformat: "RG16F", format: "RG", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RG16F", valid: true, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RGB16F", valid: false, renderable: true, }, + { internalformat: "RG16F", format: "RG", destFormat: "RGBA16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "R16F", valid: true, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RG16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RGB16F", valid: false, renderable: true, }, + { internalformat: "R16F", format: "RED", destFormat: "RGBA16F", valid: false, renderable: true, }, + ]; + if (version == 1) { + cases = [ + { valid: true, renderable: true, format: "RGBA", destFormat: "LUMINANCE", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "ALPHA", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "LUMINANCE_ALPHA", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "RGB", }, + { valid: true, renderable: true, format: "RGBA", destFormat: "RGBA", }, + { valid: true, renderable: true, format: "RGB", destFormat: "LUMINANCE", }, + { valid: false, renderable: true, format: "RGB", destFormat: "ALPHA", }, + { valid: false, renderable: true, format: "RGB", destFormat: "LUMINANCE_ALPHA", }, + { valid: true, renderable: true, format: "RGB", destFormat: "RGB", }, + { valid: false, renderable: true, format: "RGB", destFormat: "RGBA", }, + { valid: true, renderable: false, format: "ALPHA", destFormat: "ALPHA", }, + { valid: true, renderable: false, format: "LUMINANCE", destFormat: "LUMINANCE", }, + { valid: true, renderable: false, format: "LUMINANCE_ALPHA", destFormat: "LUMINANCE_ALPHA", }, + ]; + } + cases.forEach(function(testcase) { + debug(""); + debug(`Testing CopyTexImage2D for format: ${testcase.format}, internalformat: ${testcase.internalformat}, destformat: ${testcase.destFormat}`); + + var format = gl[testcase.format]; + var internalformat = version > 1 ? gl[testcase.internalformat] : format; + var type = version > 1 ? gl.HALF_FLOAT : 0x8D61 /* HALF_FLOAT_OES */; + var destFormat = gl[testcase.destFormat]; + var texSrc = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texSrc); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + var data = new Uint16Array(width * height * 4); + gl.texImage2D(gl.TEXTURE_2D, level, internalformat, width, height, 0, format, type, data); + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texSrc, level); + var texDest = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texDest); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + if (enabled && testcase.renderable) { + if (version == 1 && format == gl.RGB && gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT) { + testPassed("RGB framebuffer attachment not supported. This is allowed.") + } else { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + gl.copyTexImage2D(gl.TEXTURE_2D, level, destFormat, 0, 0, width, height, 0); + wtu.glErrorShouldBe(gl, testcase.valid ? gl.NO_ERROR : [gl.INVALID_ENUM, gl.INVALID_OPERATION], "CopyTexImage2D"); + } + } else { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + gl.copyTexImage2D(gl.TEXTURE_2D, level, destFormat, 0, 0, width, height, 0); + wtu.glErrorShouldBe(gl, [gl.INVALID_ENUM, gl.INVALID_FRAMEBUFFER_OPERATION], "CopyTexImage2D should fail."); + } + + gl.deleteTexture(texDest); + gl.deleteTexture(texSrc); + gl.deleteFramebuffer(fbo); + }); +} + +description("This test verifies the functionality of the EXT_color_buffer_half_float extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, version); + +if (version < 2) { + // These are exposed on the extension, but we need them before the extension has been requested so we can + // make sure they don't work. + gl.R16F = 0x822D; + gl.RG16F = 0x822F; + gl.RGB16F = 0x881B; + gl.RGBA16F = 0x881A; +} + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var texturedShaders = [ + wtu.simpleTextureVertexShader, + `precision mediump float; + uniform sampler2D tex; + uniform vec4 subtractor; + varying vec2 texCoord; + void main() + { + vec4 color = texture2D(tex, texCoord); + if (abs(color.r - subtractor.r) + + abs(color.g - subtractor.g) + + abs(color.b - subtractor.b) + + abs(color.a - subtractor.a) < 16.0) { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + }`, + ]; + var testProgram = + wtu.setupProgram(gl, + texturedShaders, + ['vPosition', 'texCoord0'], + [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + + if (version > 1) { + // Ensure these formats can't be used for rendering if the extension is disabled + runFloatTextureRenderTargetTest(false, "gl.R16F", gl.RED, gl.FLOAT); + runFloatTextureRenderTargetTest(false, "gl.RG16F", gl.RG, gl.FLOAT); + runFloatTextureRenderTargetTest(false, "gl.RGBA16F", gl.RGBA, gl.FLOAT); + } + + runFloatRenderbufferRenderTargetTest(false, "gl.R16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RG16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RGBA16F"); + runFloatRenderbufferRenderTargetTest(false, "gl.R32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RG32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.RGBA32F"); + runFloatRenderbufferRenderTargetTest(false, "gl.R11F_G11F_B10F"); + + if (version > 1) { + runCopyTexImageTest(false); + // Ensure RGB16F can't be used for rendering. + runRGB16FNegativeTest(); + } + + let oesTextureHalfFloat = null; + if (version == 1) { + // oesTextureHalfFloat implicitly enables EXT_color_buffer_half_float if supported + oesTextureHalfFloat = gl.getExtension("OES_texture_half_float"); + if (oesTextureHalfFloat && gl.getSupportedExtensions().includes("EXT_color_buffer_half_float")) { + runFloatTextureRenderTargetTest(true, "gl.RGBA", gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(true, "gl.RGB", gl.RGB, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 3, [1000, 1000, 1000, 1], 0); + } + } + + var ext = null; + if (!(ext = gl.getExtension("EXT_color_buffer_half_float"))) { + testPassed("No EXT_color_buffer_half_float support -- this is legal"); + } else { + testPassed("Successfully enabled EXT_color_buffer_half_float extension"); + + shouldBe("ext.RGB16F_EXT", "gl.RGB16F"); + shouldBe("ext.RGBA16F_EXT", "gl.RGBA16F"); + shouldBe("ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT", "0x8211"); + shouldBe("ext.UNSIGNED_NORMALIZED_EXT", "0x8C17"); + + if (version > 1) { + runInternalFormatQueryTest(); + runFloatTextureRenderTargetTest(true, "gl.R16F", gl.RED, gl.FLOAT, testProgram, 1, [1000, 1, 1, 1], 0); + runFloatTextureRenderTargetTest(true, "gl.RG16F", gl.RG, gl.FLOAT, testProgram, 2, [1000, 1000, 1, 1], 0); + runFloatTextureRenderTargetTest(true, "gl.RGBA16F", gl.RGBA, gl.FLOAT, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatRenderbufferRenderTargetTest(true, "gl.R16F", testProgram, 1, [1000, 1, 1, 1]); + runFloatRenderbufferRenderTargetTest(true, "gl.RG16F", testProgram, 2, [1000, 1000, 1, 1]); + runFloatRenderbufferRenderTargetTest(true, "gl.RGBA16F", testProgram, 4, [1000, 1000, 1000, 1000]); + } + if (version == 1) { + shouldBeNonNull(oesTextureHalfFloat); // Required by spec + runFloatTextureRenderTargetTest(true, "gl.RGBA", gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 4, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(true, "gl.RGB", gl.RGB, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 3, [1000, 1000, 1000, 1], 0); + runFloatTextureRenderTargetTest(false, "gl.LUMINANCE_ALPHA", gl.LUMINANCE_ALPHA, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 2, [1000, 1000, 1000, 1000], 0); + runFloatTextureRenderTargetTest(false, "gl.LUMINANCE", gl.LUMINANCE, oesTextureHalfFloat.HALF_FLOAT_OES, testProgram, 1, [1000, 1, 1, 1], 0); + } + + if (version > 1) + runRGB16FNegativeTest(); // Ensure EXT_color_buffer_half_float does not enable RGB16F as color renderable. + + runCopyTexImageTest(true); + + runUniqueObjectTest(); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js new file mode 100644 index 0000000000..ab2f9f9288 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-float-blend.js @@ -0,0 +1,237 @@ +'use strict'; + +const trivialVsSrc = ` +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsSrc = ` +void main() +{ + gl_FragColor = vec4(0,1,0,1); +} +`; +const trivialVsMrtSrc100 = ` +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsMrtSrc100 = ` +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() +{ + gl_FragData[0] = vec4(1, 0, 0, 1); + gl_FragData[1] = vec4(0, 1, 0, 1); +} +`; +const trivialVsMrtSrc300 = `#version 300 es +void main() +{ + gl_Position = vec4(0,0,0,1); +} +`; +const trivialFsMrtSrc300 = `#version 300 es +precision mediump float; +layout(location = 0) out vec4 o_color0; +layout(location = 1) out vec4 o_color1; +void main() +{ + o_color0 = vec4(1, 0, 0, 1); + o_color1 = vec4(0, 1, 0, 1); +} +`; + +function testExtFloatBlend(internalFormat) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, [trivialVsSrc, trivialFsSrc]); + gl.useProgram(prog); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.disable(gl.BLEND); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Float32 draw target without blending'); + + gl.enable(gl.BLEND); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); +} + +function testExtFloatBlendMRTImpl(version, internalFormat, shaders, attachments, drawBuffers) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, shaders); + gl.useProgram(prog); + + const tex1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + const tex2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + const texF1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texF1); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const texF2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texF2); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, tex1, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, tex2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + drawBuffers(attachments); + + gl.enable(gl.BLEND); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'No Float32 color attachment'); + + if (version < 2) { + // EXT_draw_buffers require all color buffers having the same number of bitplanes + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, texF1, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, texF2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + } else { + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, texF1, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, texF2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[0], gl.TEXTURE_2D, tex1, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + drawBuffers([attachments[0]]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Float32 color attachment draw buffer is not enabled'); + + drawBuffers(attachments); + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[1], gl.TEXTURE_2D, tex2, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'No Float32 color attachment'); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex1); + gl.deleteTexture(tex2); + gl.deleteTexture(texF1); + gl.deleteTexture(texF2); +} + +function testExtFloatBlendMRT(version, drawBuffersExt) { + if (version < 2) { + if (!drawBuffersExt) return; + testExtFloatBlendMRTImpl( + version, + gl.RGBA, + [trivialVsMrtSrc100, trivialFsMrtSrc100], + [drawBuffersExt.COLOR_ATTACHMENT0_WEBGL, drawBuffersExt.COLOR_ATTACHMENT1_WEBGL], + drawBuffersExt.drawBuffersWEBGL.bind(drawBuffersExt) + ); + } else { + testExtFloatBlendMRTImpl( + version, + gl.RGBA32F, + [trivialVsMrtSrc300, trivialFsMrtSrc300], + [gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1], + gl.drawBuffers.bind(gl) + ); + } +} + +function testExtFloatBlendNonFloat32TypeImpl(internalFormat, formats) { + const shouldBlend = gl.getSupportedExtensions().indexOf('EXT_float_blend') != -1; + + const prog = wtu.setupProgram(gl, [trivialVsSrc, trivialFsSrc]); + gl.useProgram(prog); + + gl.enable(gl.BLEND); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, shouldBlend ? 0 : gl.INVALID_OPERATION, + 'Float32 blending is ' + (shouldBlend ? '' : 'not ') + 'allowed '); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'UNSIGNED_BYTE should blend anyway'); + + for (let i = 0, len = formats.length; i < len; i++) { + gl.texImage2D(gl.TEXTURE_2D, 0, formats[i][0], 1, 1, 0, formats[i][1], formats[i][2], null); + gl.drawArrays(gl.POINTS, 0, 1); + wtu.glErrorShouldBe(gl, 0, 'Any other float type which is not 32-bit-Float should blend anyway'); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); +} + +function testExtFloatBlendNonFloat32Type(version, oesTextureHalfFloat) { + if (version < 2) { + if (!oesTextureHalfFloat) return; + const formats = [ + [gl.RGBA, gl.RGBA, oesTextureHalfFloat.HALF_FLOAT_OES] + ]; + testExtFloatBlendNonFloat32TypeImpl(gl.RGBA, formats); + } else { + const formats = [ + [gl.RGBA16F, gl.RGBA, gl.HALF_FLOAT], + [gl.RGBA16F, gl.RGBA, gl.FLOAT], + [gl.RG16F, gl.RG, gl.FLOAT], + [gl.R16F, gl.RED, gl.FLOAT], + [gl.R11F_G11F_B10F, gl.RGB, gl.FLOAT] + ]; + testExtFloatBlendNonFloat32TypeImpl(gl.RGBA32F, formats); + } +} + +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js new file mode 100644 index 0000000000..1e60a79717 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-texture-filter-anisotropic.js @@ -0,0 +1,169 @@ +"use strict"; +description("This test verifies the functionality of the EXT_texture_filter_anisotropic extension, if it is available."); + +debug(""); + +let wtu = WebGLTestUtils; +let canvas = document.getElementById("canvas"); +let gl = wtu.create3DContext(canvas, undefined, contextVersion); +let ext = null; +let sampler; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runHintTestDisabled(); + if (contextVersion >= 2) { + runSamplerTestDisabled(); + } + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic"); + + if (!ext) { + testPassed("No EXT_texture_filter_anisotropic support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled EXT_texture_filter_anisotropic extension"); + + runSupportedTest(true); + runHintTestEnabled(); + if (contextVersion >= 2) { + runSamplerTestEnabled(); + } + } +} + +function runSupportedTest(extensionEnabled) { + if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic") !== undefined) { + if (extensionEnabled) { + testPassed("EXT_texture_filter_anisotropic listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_texture_filter_anisotropic listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_texture_filter_anisotropic not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_texture_filter_anisotropic not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runHintTestDisabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + + const MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; + shouldBeNull(`gl.getParameter(${MAX_TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "MAX_TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + const TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + shouldBeNull(`gl.getTexParameter(gl.TEXTURE_2D, ${TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + gl.texParameterf(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.texParameteri(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.deleteTexture(texture); +} + +function runHintTestEnabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension enabled"); + + shouldBe("ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT", "0x84FF"); + + let max_anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "MAX_TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if (max_anisotropy >= 2) { + testPassed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0"); + } else { + testFailed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0, returned values was: " + max_anisotropy); + } + + // TODO make a texture and verify initial value == 1 and setting to less than 1 is invalid value + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + shouldBe("ext.TEXTURE_MAX_ANISOTROPY_EXT", "0x84FE"); + + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + let queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if (queried_value == 1) { + testPassed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT is 1.0"); + } else { + testFailed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT should be 1.0, returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if (queried_value == max_anisotropy) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + max_anisotropy + " , returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 1.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to 1.5 should succeed"); + + queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if (queried_value == 1.5) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + 1.5 + " , returned value was: " + queried_value); + } + + gl.deleteTexture(texture); +} + +function runSamplerTestDisabled() { + sampler = gl.createSampler(); + const TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + gl.samplerParameterf(sampler, TEXTURE_MAX_ANISOTROPY_EXT, 1.0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "setting TEXTURE_MAX_ANISOTROPY_EXT on sampler without extension enabled should fail"); + shouldBeNull(`gl.getSamplerParameter(sampler, ${TEXTURE_MAX_ANISOTROPY_EXT})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "querying TEXTURE_MAX_ANISOTROPY_EXT on sampler without extension enabled should fail"); + gl.deleteSampler(sampler); +} + +function runSamplerTestEnabled() { + let sampler = gl.createSampler(); + gl.samplerParameterf(sampler, ext.TEXTURE_MAX_ANISOTROPY_EXT, 1.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setting TEXTURE_MAX_ANISOTROPY_EXT on sampler should succeed"); + let queried_value = gl.getSamplerParameter(sampler, ext.TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "querying TEXTURE_MAX_ANISOTROPY_EXT on sampler should succeed"); + if (queried_value == 1.5) { + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT on sampler matches expecation"); + } else { + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT on sampler should be: " + 1.5 + " , returned value was: " + queried_value); + } + gl.deleteSampler(sampler); +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js new file mode 100644 index 0000000000..4c853fc4fa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-bindattriblocation-aliasing.js @@ -0,0 +1,44 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +var runBindAttribLocationAliasingTest = function(wtu, gl, glFragmentShader, vertexShaderTemplate) { + var typeInfo = [ + { type: 'float', asVec4: 'vec4(0.0, $(var), 0.0, 1.0)' }, + { type: 'vec2', asVec4: 'vec4($(var), 0.0, 1.0)' }, + { type: 'vec3', asVec4: 'vec4($(var), 1.0)' }, + { type: 'vec4', asVec4: '$(var)' }, + ]; + var maxAttributes = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + // Test all type combinations of a_1 and a_2. + typeInfo.forEach(function(typeInfo1) { + typeInfo.forEach(function(typeInfo2) { + debug('attribute_1: ' + typeInfo1.type + ' attribute_2: ' + typeInfo2.type); + var replaceParams = { + type_1: typeInfo1.type, + type_2: typeInfo2.type, + gl_Position_1: wtu.replaceParams(typeInfo1.asVec4, {var: 'a_1'}), + gl_Position_2: wtu.replaceParams(typeInfo2.asVec4, {var: 'a_2'}) + }; + var strVertexShader = wtu.replaceParams(vertexShaderTemplate, replaceParams); + var glVertexShader = wtu.loadShader(gl, strVertexShader, gl.VERTEX_SHADER); + assertMsg(glVertexShader != null, "Vertex shader compiled successfully."); + // Bind both a_1 and a_2 to the same position and verify the link fails. + // Do so for all valid positions available. + for (var l = 0; l < maxAttributes; l++) { + var glProgram = gl.createProgram(); + gl.bindAttribLocation(glProgram, l, 'a_1'); + gl.bindAttribLocation(glProgram, l, 'a_2'); + gl.attachShader(glProgram, glVertexShader); + gl.attachShader(glProgram, glFragmentShader); + gl.linkProgram(glProgram); + var linkStatus = gl.getProgramParameter(glProgram, gl.LINK_STATUS); + assertMsg(!linkStatus, "Link should fail when both attributes are aliased to location " + l); + } + }); + }); +}; \ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js new file mode 100644 index 0000000000..32d451f7a2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-enum-tests.js @@ -0,0 +1,123 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("This test ensures various WebGL functions fail when passed invalid OpenGL ES enums."); + +debug(""); +debug("Canvas.getContext"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + debug("Checking gl enums."); + + var buffer = new ArrayBuffer(2); + var buf = new Uint16Array(buffer); + var tex = gl.createTexture(); + var program = wtu.createProgram(gl, wtu.loadStandardVertexShader(gl), wtu.loadStandardFragmentShader(gl)); + gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var tests = [ + "gl.disable(desktopGL['CLIP_PLANE0'])", + "gl.disable(desktopGL['POINT_SPRITE'])", + "gl.getBufferParameter(gl.ARRAY_BUFFER, desktopGL['PIXEL_PACK_BUFFER'])", + "gl.hint(desktopGL['PERSPECTIVE_CORRECTION_HINT'], gl.FASTEST)", + "gl.isEnabled(desktopGL['CLIP_PLANE0'])", + "gl.isEnabled(desktopGL['POINT_SPRITE'])", + "gl.pixelStorei(desktopGL['PACK_SWAP_BYTES'], 1)", + "gl.getParameter(desktopGL['NUM_COMPRESSED_TEXTURE_FORMATS'])", + "gl.getParameter(desktopGL['EXTENSIONS'])", + "gl.getParameter(desktopGL['SHADER_COMPILER'])", + "gl.getParameter(desktopGL['SHADER_BINARY_FORMATS'])", + "gl.getParameter(desktopGL['NUM_SHADER_BINARY_FORMATS'])", + ]; + + if (contextVersion < 2) { + tests = tests.concat([ + "gl.blendEquation(desktopGL['MIN'])", + "gl.blendEquation(desktopGL['MAX'])", + "gl.blendEquationSeparate(desktopGL['MIN'], gl.FUNC_ADD)", + "gl.blendEquationSeparate(desktopGL['MAX'], gl.FUNC_ADD)", + "gl.blendEquationSeparate(gl.FUNC_ADD, desktopGL['MIN'])", + "gl.blendEquationSeparate(gl.FUNC_ADD, desktopGL['MAX'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STREAM_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STREAM_COPY'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STATIC_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['STATIC_COPY'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['DYNAMIC_READ'])", + "gl.bufferData(gl.ARRAY_BUFFER, 16, desktopGL['DYNAMIC_COPY'])", + "gl.bindTexture(desktopGL['TEXTURE_2D_ARRAY'], tex)", + "gl.bindTexture(desktopGL['TEXTURE_3D'], tex)", + ]); + } else { + tests = tests.concat([ + "gl.bindTexture(desktopGL['TEXTURE_RECTANGLE_EXT'], tex)", + "gl.enable(desktopGL['PRIMITIVE_RESTART_FIXED_INDEX'])", + "gl.getActiveUniforms(program, [0], desktopGL['UNIFORM_NAME_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH'])", + "gl.getProgramParameter(program, desktopGL['PROGRAM_BINARY_RETRIEVABLE_HINT'])", + "gl.getProgramParameter(program, desktopGL['PROGRAM_BINARY_LENGTH'])", + "gl.getParameter(program, desktopGL['NUM_PROGRAM_BINARY_FORMATS'])", + ]); + } + + for (var ii = 0; ii < tests.length; ++ii) { + TestEval(tests[ii]); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, tests[ii] + " should return INVALID_ENUM."); + } + + gl.bindTexture(gl.TEXTURE_2D, tex); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + tests = [ + "gl.getTexParameter(gl.TEXTURE_2D, desktopGL['GENERATE_MIPMAP'])", + "gl.texParameteri(gl.TEXTURE_2D, desktopGL['GENERATE_MIPMAP'], 1)", + "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, desktopGL['CLAMP_TO_BORDER'])", + ]; + + if (contextVersion < 2) { + tests = tests.concat([ + "gl.texParameteri(desktopGL['TEXTURE_2D_ARRAY'], gl.TEXTURE_MAG_FILTER, gl.NEAREST)", + "gl.texParameteri(desktopGL['TEXTURE_3D'], gl.TEXTURE_MAG_FILTER, gl.NEAREST)", + ]); + } else { + tests = tests.concat([ + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_R_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_G_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_B_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], desktopGL['TEXTURE_SWIZZLE_A_EXT'], gl.RED)", + "gl.texParameteri(desktopGL['TEXTURE_2D'], gl.TEXTURE_WRAP_R, desktopGL['CLAMP_TO_BORDER'])", + ]); + } + + for (var ii = 0; ii < tests.length; ++ii) { + TestEval(tests[ii]); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, tests[ii] + " should return INVALID_ENUM."); + } + if (contextVersion >= 2) { + var uniformBlockProgram = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(uniformBlockProgram); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + gl.getActiveUniformBlockParameter(uniformBlockProgram, 0, desktopGL['UNIFORM_BLOCK_NAME_LENGTH']); + shouldBe('gl.getError()', 'gl.INVALID_ENUM'); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js new file mode 100644 index 0000000000..8844bbf544 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-get-tex-parameter.js @@ -0,0 +1,183 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description(); +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("example", undefined, contextVersion); + +// NOTE: We explicitly do this in a funky order +// to hopefully find subtle bugs. + +var targets = [ + 'TEXTURE_2D', + 'TEXTURE_2D', + 'TEXTURE_CUBE_MAP', + 'TEXTURE_CUBE_MAP' +]; + +if (contextVersion > 1) { + targets = targets.concat([ + 'TEXTURE_2D_ARRAY', + 'TEXTURE_2D_ARRAY', + 'TEXTURE_3D', + 'TEXTURE_3D' + ]); +} + +// Create textures on different active textures. +for (var ii = 0; ii < targets.length; ++ii) { + var target = targets[ii]; + var tex = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0 + ii); + gl.bindTexture(gl[target], tex); +} + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + +var states = [ + { state: 'TEXTURE_WRAP_S', default: 'REPEAT', value1: 'CLAMP_TO_EDGE', value2: 'REPEAT' }, + { state: 'TEXTURE_WRAP_T', default: 'REPEAT', value1: 'MIRRORED_REPEAT', value2: 'REPEAT' }, + { state: 'TEXTURE_MAG_FILTER', default: 'LINEAR', value1: 'NEAREST', value2: 'LINEAR' }, + { state: 'TEXTURE_MIN_FILTER', default: 'NEAREST_MIPMAP_LINEAR', value1: 'LINEAR_MIPMAP_LINEAR', value2: 'NEAREST' } +]; + +if (contextVersion > 1) { + states = states.concat([ + { state: 'TEXTURE_WRAP_R', default: 'REPEAT', value1: 'CLAMP_TO_EDGE', value2: 'MIRRORED_REPEAT' }, + { state: 'TEXTURE_COMPARE_FUNC', default: 'LEQUAL', value1: 'GREATER', value2: 'LESS' }, + { state: 'TEXTURE_COMPARE_MODE', default: 'NONE', value1: 'COMPARE_REF_TO_TEXTURE', value2: 'NONE' }, + { state: 'TEXTURE_BASE_LEVEL', default: 0, value1: 100, value2: 99 }, + { state: 'TEXTURE_MAX_LEVEL', default: 1000, value1: 800, value2: 300 }, + { state: 'TEXTURE_MIN_LOD', default: -1000.0, value1: -500.0, value2: -999.0 }, + { state: 'TEXTURE_MAX_LOD', default: 1000.0, value1: 500.0, value2: 999.0 }, + // Note: For TEXTURE_IMMUTABLE_LEVELS and TEXTURE_IMMUTABLE_FORMAT, + // these two pname are used by getTexParameter API only, not available in texParameter[fi] in specifications. + // Thus, these two states store default value only. + { state: 'TEXTURE_IMMUTABLE_LEVELS', default: 0, }, + { state: 'TEXTURE_IMMUTABLE_FORMAT', default: false, } + ]); +} + +function getStateInfoValue(stateInfo, item, method) { + switch (stateInfo.state) { + case 'TEXTURE_WRAP_R': + case 'TEXTURE_WRAP_S': + case 'TEXTURE_WRAP_T': + case 'TEXTURE_MAG_FILTER': + case 'TEXTURE_MIN_FILTER': + case 'TEXTURE_COMPARE_FUNC': + case 'TEXTURE_COMPARE_MODE': + if (method === 'Get') { + return 'gl["' + stateInfo[item] + '"]'; + } else if (method === 'Set') { + return gl[stateInfo[item]]; + } + break; + case 'TEXTURE_BASE_LEVEL': + case 'TEXTURE_MAX_LEVEL': + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + if (method === 'Get') { + return '' + stateInfo[item]; + } else if (method === 'Set') { + return stateInfo[item]; + } + break; + case 'TEXTURE_IMMUTABLE_LEVELS': + case 'TEXTURE_IMMUTABLE_FORMAT': + // Return default value only. + return '' + stateInfo.default; + default: + wtu.error("Not reached!"); + return null; + break; + } +} + +function applyStates(fn) { + for (var ss = 0; ss < states.length; ++ss) { + var stateInfo = states[ss]; + for (var ii = 0; ii < targets.length; ++ii) { + var target = targets[ii]; + gl.activeTexture(gl.TEXTURE0 + ii); + fn(target, stateInfo); + } + } +} + +// test the default state. +applyStates(function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, 'default', 'Get'); + shouldBe(a, b); +}); + +// test new state +applyStates(function(target, stateInfo) { + switch (stateInfo.state) { + case 'TEXTURE_IMMUTABLE_FORMAT': + case 'TEXTURE_IMMUTABLE_LEVELS': + // Skip these two pname for texParameterf[fi]. + break; + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + gl.texParameterf(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, 'value1', 'Set')); + break; + default: + gl.texParameteri(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, 'value1', 'Set')); + break; + } +}); + +applyStates(function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, 'value1', 'Get'); + shouldBe(a, b); +}); + +// test different states on each target. +function getItem(count) { + return (count % 2) ? 'value2' : 'value1'; +} + +applyStates(function() { + var count = 0; + return function(target, stateInfo) { + switch (stateInfo.state) { + case 'TEXTURE_IMMUTABLE_FORMAT': + case 'TEXTURE_IMMUTABLE_LEVELS': + // Skip these two pname for texParameterf[fi]. + break; + case 'TEXTURE_MIN_LOD': + case 'TEXTURE_MAX_LOD': + gl.texParameterf(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, getItem(count), 'Set')); + break; + default: + gl.texParameteri(gl[target], gl[stateInfo.state], getStateInfoValue(stateInfo, getItem(count), 'Set')); + break; + } + ++count; + } +}()); + +applyStates(function() { + var count = 0; + return function(target, stateInfo) { + var a = 'gl.getTexParameter(gl["' + target + '"], gl["' + stateInfo.state + '"])'; + var b = getStateInfoValue(stateInfo, getItem(count), 'Get'); + shouldBe(a, b); + ++count; + }; +}()); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js new file mode 100644 index 0000000000..bd97434b77 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-object-get-calls.js @@ -0,0 +1,1090 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +const wtu = WebGLTestUtils; +description("Test of get calls against GL objects like getBufferParameter, etc."); + +let gl = wtu.create3DContext(undefined, undefined, contextVersion); + +async function testInvalidArgument(funcName, argumentName, validArgumentArray, func) { + let validArguments = {}; + for (let ii = 0; ii < validArgumentArray.length; ++ii) { + validArguments[validArgumentArray[ii]] = true; + } + let success = true; + const MAX = 0x10000; + const STEP = Math.ceil(MAX / 10); + for (let ii = 0; ii < MAX; ii += 1) { + if (ii && ii % STEP == 0) { + debug(`(${ii} of ${MAX}: ${(ii/MAX*100).toFixed(1)}%)`); + await wtu.dispatchPromise(); // Spin the event loop. + } + if (!validArguments[ii]) { + let result = func(ii); + if (result !== null) { + success = false; + testFailed(funcName + " returned " + result + " instead of null for invalid " + argumentName + " enum: " + wtu.glEnumToString(gl, ii)); + break; + } + let err = gl.getError(); + if (err != gl.INVALID_ENUM) { + success = false; + testFailed(funcName + " did not generate INVALID_ENUM for invalid " + argumentName + " enum: " + wtu.glEnumToString(gl, ii)); + break; + } + } + } + if (success) { + testPassed(funcName + " correctly handled invalid " + argumentName + " enums"); + } +} + +(async () => { + debug(""); + debug("test getBufferParameter"); + // Test getBufferParameter + let bufferTypes = [gl.ARRAY_BUFFER, gl.ELEMENT_ARRAY_BUFFER]; + if (contextVersion > 1) { + bufferTypes = bufferTypes.concat([gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, gl.PIXEL_PACK_BUFFER, gl.PIXEL_UNPACK_BUFFER, gl.TRANSFORM_FEEDBACK_BUFFER, gl.UNIFORM_BUFFER]); + } + for (let bb = 0; bb < bufferTypes.length; ++bb) { + let bufferType = bufferTypes[bb]; + let buffer = gl.createBuffer(); + gl.bindBuffer(bufferType, buffer); + gl.bufferData(bufferType, 16, gl.DYNAMIC_DRAW); + let expression1 = "gl.getBufferParameter(" + bufferType + ", gl.BUFFER_SIZE)"; + let expression2 = "gl.getBufferParameter(" + bufferType + ", gl.BUFFER_USAGE)"; + shouldBe(expression1, '16'); + shouldBe(expression2, 'gl.DYNAMIC_DRAW'); + await testInvalidArgument("getBufferParameter", "parameter", [gl.BUFFER_SIZE, gl.BUFFER_USAGE], function(bufferType) { + return function(parameter) { + return gl.getBufferParameter(bufferType, parameter); + }; + }(bufferType)); + gl.bindBuffer(bufferType, null); + } + await testInvalidArgument( + "getBufferParameter", + "target", + bufferTypes, + function(target) { + return gl.getBufferParameter(target, gl.BUFFER_SIZE); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + let testCases = [ + { contextStencil: true}, + { contextStencil: false} + ]; + + for (let run = 0; run < testCases.length; ++run) { + debug(""); + debug("Test getFramebufferAttachmentParameter with stencil " + testCases[run].contextStencil); + + if (testCases[run].contextStencil) { + gl = wtu.create3DContext(null, {stencil: true}, contextVersion); + } else { + gl = wtu.create3DContext(null, {stencil: false}, contextVersion); + } + + window.texture = gl.createTexture(); + window.anotherTexture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, + new Uint8Array([ + 0, 0, 0, 255, + 255, 255, 255, 255, + 255, 255, 255, 255, + 0, 0, 0, 255])); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.bindTexture(gl.TEXTURE_2D, null); + window.framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + let colorAttachmentsNum = 1; + if (contextVersion > 1) { + gl.bindTexture(gl.TEXTURE_2D, anotherTexture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, + new Uint8Array([ + 0, 0, 0, 255, + 255, 255, 255, 255, + 255, 255, 255, 255, + 0, 0, 0, 255])); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.bindTexture(gl.TEXTURE_2D, null); + colorAttachmentsNum = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + colorAttachmentsNum - 1, gl.TEXTURE_2D, anotherTexture, 0); + } + window.renderbuffer = gl.createRenderbuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + if (contextVersion == 1) + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 2, 2); + else + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, 2, 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); + if (contextVersion > 1) + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderbuffer); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + // The for loop tests two color attachments for WebGL 2: the first one (gl.COLOR_ATTACHMENT0) + // and the last one (gl.COLOR_ATTACHMENT0 + gl.MAX_COLOR_ATTACHMENTS - 1). + for (let ii = 0; ii < colorAttachmentsNum; ii += (colorAttachmentsNum > 1 ? colorAttachmentsNum - 1 : 1)) { + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.TEXTURE'); + if (ii == 0) + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'texture'); + else + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'anotherTexture'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL)', '0'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE)', '0'); + if (contextVersion > 1) { + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ' + ii + ', gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER)', '0'); + } + } + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + if (contextVersion > 1) { + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.RENDERBUFFER'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)', 'renderbuffer'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + } + let validParametersForFBAttachment = + [ gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, + gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE + ]; + if (contextVersion > 1) { + validParametersForFBAttachment = validParametersForFBAttachment.concat([ + gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, + gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, + gl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER + ]); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "parameter", + validParametersForFBAttachment, + function(parameter) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT, parameter); + } + ); + let validTargetsForFBAttachment = [gl.FRAMEBUFFER]; + if (contextVersion > 1) { + validTargetsForFBAttachment = validTargetsForFBAttachment.concat([gl.READ_FRAMEBUFFER, gl.DRAW_FRAMEBUFFER]); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "target", + validTargetsForFBAttachment, + function(target) { + return gl.getFramebufferAttachmentParameter(target, gl.COLOR_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + let validAttachmentsForFBAttachment = new Array( + gl.COLOR_ATTACHMENT0, + gl.DEPTH_ATTACHMENT, + gl.STENCIL_ATTACHMENT, + gl.DEPTH_STENCIL_ATTACHMENT + ); + if (contextVersion > 1) { + for (let ii = 1; ii < gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); ++ii) { + validAttachmentsForFBAttachment[validAttachmentsForFBAttachment.length] = gl.COLOR_ATTACHMENT0 + ii; + } + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "attachment", + validAttachmentsForFBAttachment, + function(attachment) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + if (contextVersion > 1) { + // test default framebuffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + if (testCases[run].contextStencil) + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.FRAMEBUFFER_DEFAULT'); + else + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_RED_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_GREEN_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_BLUE_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + if (testCases[run].contextStencil) { + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + shouldBeNonZero('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } else { + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING)'); + } + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "parameter", + validParametersForFBAttachment, + function(parameter) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.BACK, parameter); + } + ); + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "target", + validTargetsForFBAttachment, + function(target) { + return gl.getFramebufferAttachmentParameter(target, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + await testInvalidArgument( + "getFramebufferAttachmentParameter", + "attachment", + [ gl.BACK, + gl.DEPTH, + gl.STENCIL + ], + function(attachment) { + return gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, attachment, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE); + } + ); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("test getAttachedShaders"); + window.standardVert = wtu.loadStandardVertexShader(gl); + window.standardFrag = wtu.loadStandardFragmentShader(gl); + window.standardProgram = gl.createProgram(); + gl.attachShader(standardProgram, standardVert); + gl.attachShader(standardProgram, standardFrag); + gl.linkProgram(standardProgram); + window.shaders = gl.getAttachedShaders(standardProgram); + shouldBe('shaders.length', '2'); + shouldBeTrue('shaders[0] == standardVert && shaders[1] == standardFrag || shaders[1] == standardVert && shaders[0] == standardFrag'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldThrow('gl.getAttachedShaders(null)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldThrow('gl.getAttachedShaders(standardVert)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getProgramParameter"); + shouldBe('gl.getProgramParameter(standardProgram, gl.DELETE_STATUS)', 'false'); + shouldBe('gl.getProgramParameter(standardProgram, gl.LINK_STATUS)', 'true'); + shouldBe('typeof gl.getProgramParameter(standardProgram, gl.VALIDATE_STATUS)', '"boolean"'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ATTACHED_SHADERS)', '2'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ACTIVE_ATTRIBUTES)', '2'); + shouldBe('gl.getProgramParameter(standardProgram, gl.ACTIVE_UNIFORMS)', '1'); + if (contextVersion > 1) { + let buffer = gl.createBuffer(); + gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, buffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 1024, gl.DYNAMIC_DRAW); + window.uniformBlockProgram = wtu.loadUniformBlockProgram(gl); + let transformFeedbackVars = ["normal", "ecPosition"]; + gl.transformFeedbackVaryings(uniformBlockProgram, transformFeedbackVars, gl.INTERLEAVED_ATTRIBS); + gl.linkProgram(uniformBlockProgram); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.ACTIVE_UNIFORM_BLOCKS)', '1'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.TRANSFORM_FEEDBACK_VARYINGS)', '2'); + shouldBe('gl.getProgramParameter(uniformBlockProgram, gl.TRANSFORM_FEEDBACK_BUFFER_MODE)', 'gl.INTERLEAVED_ATTRIBS'); + } + window.program = standardProgram; + let validArrayForProgramParameter = [ + gl.DELETE_STATUS, + gl.LINK_STATUS, + gl.VALIDATE_STATUS, + gl.ATTACHED_SHADERS, + gl.ACTIVE_ATTRIBUTES, + gl.ACTIVE_UNIFORMS + ]; + if (contextVersion > 1) { + validArrayForProgramParameter = validArrayForProgramParameter.concat([ + gl.ACTIVE_UNIFORM_BLOCKS, + gl.TRANSFORM_FEEDBACK_VARYINGS, + gl.TRANSFORM_FEEDBACK_BUFFER_MODE + ]); + program = uniformBlockProgram; + } + await testInvalidArgument( + "getProgramParameter", + "parameter", + validArrayForProgramParameter, + function(parameter) { + return gl.getProgramParameter(program, parameter); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getRenderbufferParameter"); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)', '2'); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)', '2'); + // Note: we can't test the actual value of the internal format since + // the implementation is allowed to change it. + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE)'); + let colorbuffer = gl.createRenderbuffer(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 2, 2); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)'); + shouldBeNonZero('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)'); + if (contextVersion > 1) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA4, 2, 2); + shouldBe('gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_SAMPLES)', '4'); + } + let validArrayForRenderbuffer = new Array( + gl.RENDERBUFFER_WIDTH, + gl.RENDERBUFFER_HEIGHT, + gl.RENDERBUFFER_INTERNAL_FORMAT, + gl.RENDERBUFFER_RED_SIZE, + gl.RENDERBUFFER_GREEN_SIZE, + gl.RENDERBUFFER_BLUE_SIZE, + gl.RENDERBUFFER_ALPHA_SIZE, + gl.RENDERBUFFER_DEPTH_SIZE, + gl.RENDERBUFFER_STENCIL_SIZE + ); + if (contextVersion > 1) { + validArrayForRenderbuffer[validArrayForRenderbuffer.length] = gl.RENDERBUFFER_SAMPLES; + } + await testInvalidArgument( + "getRenderbufferParameter", + "parameter", + validArrayForRenderbuffer, + function(parameter) { + return gl.getRenderbufferParameter(gl.RENDERBUFFER, parameter); + }); + await testInvalidArgument( + "getRenderbufferParameter", + "target", + [ gl.RENDERBUFFER ], + function(target) { + return gl.getRenderbufferParameter(target, gl.RENDERBUFFER_WIDTH); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getShaderParameter"); + shouldBe('gl.getShaderParameter(standardVert, gl.SHADER_TYPE)', 'gl.VERTEX_SHADER'); + shouldBe('gl.getShaderParameter(standardVert, gl.DELETE_STATUS)', 'false'); + shouldBe('gl.getShaderParameter(standardVert, gl.COMPILE_STATUS)', 'true'); + await testInvalidArgument( + "getShaderParameter", + "parameter", + [ gl.DELETE_STATUS, + gl.COMPILE_STATUS, + gl.SHADER_TYPE + ], + function(parameter) { + return gl.getShaderParameter(standardVert, parameter); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getTexParameter"); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T)', 'gl.CLAMP_TO_EDGE'); + if (contextVersion > 1) { + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 10); + gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD, 10); + gl.texParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_LOD, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL)', '0'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC)', 'gl.LEQUAL'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE)', 'gl.COMPARE_REF_TO_TEXTURE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL)', '10'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MAX_LOD)', '10'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_MIN_LOD)', '0'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_IMMUTABLE_FORMAT)', 'false'); + shouldBe('gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_IMMUTABLE_LEVELS)', '0'); + } + let validParametersForTexture = [ + gl.TEXTURE_MAG_FILTER, + gl.TEXTURE_MIN_FILTER, + gl.TEXTURE_WRAP_S, + gl.TEXTURE_WRAP_T, + ]; + if (contextVersion > 1) { + validParametersForTexture = validParametersForTexture.concat([ + gl.TEXTURE_BASE_LEVEL, + gl.TEXTURE_COMPARE_FUNC, + gl.TEXTURE_COMPARE_MODE, + gl.TEXTURE_MAX_LEVEL, + gl.TEXTURE_MAX_LOD, + gl.TEXTURE_MIN_LOD, + gl.TEXTURE_WRAP_R, + gl.TEXTURE_IMMUTABLE_FORMAT, + gl.TEXTURE_IMMUTABLE_LEVELS, + ]); + } + await testInvalidArgument( + "getTexParameter", + "parameter", + validParametersForTexture, + function(parameter) { + return gl.getTexParameter(gl.TEXTURE_2D, parameter); + } + ); + let validTargetsForTexture = [ gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP]; + if (contextVersion > 1) { + validTargetsForTexture = validTargetsForTexture.concat([ gl.TEXTURE_3D, gl.TEXTURE_2D_ARRAY]); + } + await testInvalidArgument( + "getTexParameter", + "target", + validTargetsForTexture, + function(target) { + return gl.getTexParameter(target, gl.TEXTURE_MAG_FILTER); + } + ); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Test getUniform with all variants of data types"); + debug("Boolean uniform variables"); + window.boolProgram = wtu.loadProgramFromFile(gl, "../../resources/boolUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(boolProgram, gl.LINK_STATUS)', 'true'); + window.bvalLoc = gl.getUniformLocation(boolProgram, "bval"); + window.bval2Loc = gl.getUniformLocation(boolProgram, "bval2"); + window.bval3Loc = gl.getUniformLocation(boolProgram, "bval3"); + window.bval4Loc = gl.getUniformLocation(boolProgram, "bval4"); + gl.useProgram(boolProgram); + gl.uniform1i(bvalLoc, 1); + gl.uniform2i(bval2Loc, 1, 0); + gl.uniform3i(bval3Loc, 1, 0, 1); + gl.uniform4i(bval4Loc, 1, 0, 1, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(boolProgram, bvalLoc)', 'true'); + shouldBe('gl.getUniform(boolProgram, bval2Loc)', '[true, false]'); + shouldBe('gl.getUniform(boolProgram, bval3Loc)', '[true, false, true]'); + shouldBe('gl.getUniform(boolProgram, bval4Loc)', '[true, false, true, false]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Integer uniform variables"); + window.intProgram = wtu.loadProgramFromFile(gl, "../../resources/intUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(intProgram, gl.LINK_STATUS)', 'true'); + window.ivalLoc = gl.getUniformLocation(intProgram, "ival"); + window.ival2Loc = gl.getUniformLocation(intProgram, "ival2"); + window.ival3Loc = gl.getUniformLocation(intProgram, "ival3"); + window.ival4Loc = gl.getUniformLocation(intProgram, "ival4"); + gl.useProgram(intProgram); + gl.uniform1i(ivalLoc, 1); + gl.uniform2i(ival2Loc, 2, 3); + gl.uniform3i(ival3Loc, 4, 5, 6); + gl.uniform4i(ival4Loc, 7, 8, 9, 10); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(intProgram, ivalLoc)', '1'); + shouldBe('gl.getUniform(intProgram, ival2Loc)', '[2, 3]'); + shouldBe('gl.getUniform(intProgram, ival3Loc)', '[4, 5, 6]'); + shouldBe('gl.getUniform(intProgram, ival4Loc)', '[7, 8, 9, 10]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Float uniform variables"); + window.floatProgram = wtu.loadProgramFromFile(gl, "../../resources/floatUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(floatProgram, gl.LINK_STATUS)', 'true'); + window.fvalLoc = gl.getUniformLocation(floatProgram, "fval"); + window.fval2Loc = gl.getUniformLocation(floatProgram, "fval2"); + window.fval3Loc = gl.getUniformLocation(floatProgram, "fval3"); + window.fval4Loc = gl.getUniformLocation(floatProgram, "fval4"); + gl.useProgram(floatProgram); + gl.uniform1f(fvalLoc, 11); + gl.uniform2f(fval2Loc, 12, 13); + gl.uniform3f(fval3Loc, 14, 15, 16); + gl.uniform4f(fval4Loc, 17, 18, 19, 20); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(floatProgram, fvalLoc)', '11'); + shouldBe('gl.getUniform(floatProgram, fval2Loc)', '[12, 13]'); + shouldBe('gl.getUniform(floatProgram, fval3Loc)', '[14, 15, 16]'); + shouldBe('gl.getUniform(floatProgram, fval4Loc)', '[17, 18, 19, 20]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Sampler uniform variables"); + window.samplerProgram = wtu.loadProgramFromFile(gl, "../../resources/noopUniformShader.vert", "../../resources/samplerUniformShader.frag"); + shouldBe('gl.getProgramParameter(samplerProgram, gl.LINK_STATUS)', 'true'); + window.s2DValLoc = gl.getUniformLocation(samplerProgram, "s2D"); + window.sCubeValLoc = gl.getUniformLocation(samplerProgram, "sCube"); + gl.useProgram(samplerProgram); + gl.uniform1i(s2DValLoc, 0); + gl.uniform1i(sCubeValLoc, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(samplerProgram, s2DValLoc)', '0'); + shouldBe('gl.getUniform(samplerProgram, sCubeValLoc)', '1'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Matrix uniform variables"); + window.matProgram = wtu.loadProgramFromFile(gl, "../../resources/matUniformShader.vert", "../../resources/noopUniformShader.frag"); + shouldBe('gl.getProgramParameter(matProgram, gl.LINK_STATUS)', 'true'); + window.mval2Loc = gl.getUniformLocation(matProgram, "mval2"); + window.mval3Loc = gl.getUniformLocation(matProgram, "mval3"); + window.mval4Loc = gl.getUniformLocation(matProgram, "mval4"); + gl.useProgram(matProgram); + gl.uniformMatrix2fv(mval2Loc, false, [1, 2, 3, 4]); + gl.uniformMatrix3fv(mval3Loc, false, [5, 6, 7, 8, 9, 10, 11, 12, 13]); + gl.uniformMatrix4fv(mval4Loc, false, [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(matProgram, mval2Loc)', '[1, 2, 3, 4]'); + shouldBe('gl.getUniform(matProgram, mval3Loc)', '[5, 6, 7, 8, 9, 10, 11, 12, 13]'); + shouldBe('gl.getUniform(matProgram, mval4Loc)', '[14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + if (contextVersion > 1) { + debug("Unsigned Integer uniform variables"); + window.uintProgram = wtu.loadProgramFromFile(gl, "../../resources/uintUniformShader.vert", "../../resources/noopUniformShaderES3.frag"); + shouldBe('gl.getProgramParameter(uintProgram, gl.LINK_STATUS)', 'true'); + window.uvalLoc = gl.getUniformLocation(uintProgram, "uval"); + window.uval2Loc = gl.getUniformLocation(uintProgram, "uval2"); + window.uval3Loc = gl.getUniformLocation(uintProgram, "uval3"); + window.uval4Loc = gl.getUniformLocation(uintProgram, "uval4"); + gl.useProgram(uintProgram); + gl.uniform1ui(uvalLoc, 1); + gl.uniform2ui(uval2Loc, 2, 3); + gl.uniform3ui(uval3Loc, 4, 5, 6); + gl.uniform4ui(uval4Loc, 7, 8, 9, 10); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(uintProgram, uvalLoc)', '1'); + shouldBe('gl.getUniform(uintProgram, uval2Loc)', '[2, 3]'); + shouldBe('gl.getUniform(uintProgram, uval3Loc)', '[4, 5, 6]'); + shouldBe('gl.getUniform(uintProgram, uval4Loc)', '[7, 8, 9, 10]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Matrix uniform variables for WebGL 2"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + window.matForWebGL2Program = wtu.loadProgramFromFile(gl, "../../resources/matForWebGL2UniformShader.vert", "../../resources/noopUniformShaderES3.frag"); + shouldBe('gl.getProgramParameter(matForWebGL2Program, gl.LINK_STATUS)', 'true'); + window.mval2x3Loc = gl.getUniformLocation(matForWebGL2Program, "mval2x3"); + window.mval2x4Loc = gl.getUniformLocation(matForWebGL2Program, "mval2x4"); + window.mval3x2Loc = gl.getUniformLocation(matForWebGL2Program, "mval3x2"); + window.mval3x4Loc = gl.getUniformLocation(matForWebGL2Program, "mval3x4"); + window.mval4x2Loc = gl.getUniformLocation(matForWebGL2Program, "mval4x2"); + window.mval4x3Loc = gl.getUniformLocation(matForWebGL2Program, "mval4x3"); + gl.useProgram(matForWebGL2Program); + gl.uniformMatrix2x3fv(mval2x3Loc, false, [1, 2, 3, 4, 5, 6]); + gl.uniformMatrix2x4fv(mval2x4Loc, false, [7, 8, 9, 10, 11, 12, 13, 14]); + gl.uniformMatrix3x2fv(mval3x2Loc, false, [15, 16, 17, 18, 19, 20]); + gl.uniformMatrix3x4fv(mval3x4Loc, false, [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]); + gl.uniformMatrix4x2fv(mval4x2Loc, false, [33, 34, 35, 36, 37, 38, 39, 40]); + gl.uniformMatrix4x3fv(mval4x3Loc, false, [41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(matForWebGL2Program, mval2x3Loc)', '[1, 2, 3, 4, 5, 6]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval2x4Loc)', '[7, 8, 9, 10, 11, 12, 13, 14]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval3x2Loc)', '[15, 16, 17, 18, 19, 20]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval3x4Loc)', '[21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval4x2Loc)', '[33, 34, 35, 36, 37, 38, 39, 40]'); + shouldBe('gl.getUniform(matForWebGL2Program, mval4x3Loc)', '[41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52]'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Sampler uniform variables for WebGL2"); + window.samplerForWebGL2Program = wtu.loadProgramFromFile(gl, "../../resources/noopUniformShaderES3.vert", "../../resources/samplerForWebGL2UniformShader.frag"); + shouldBe('gl.getProgramParameter(samplerForWebGL2Program, gl.LINK_STATUS)', 'true'); + window.s3DValLoc = gl.getUniformLocation(samplerForWebGL2Program, "s3D"); + window.s2DArrayValLoc = gl.getUniformLocation(samplerForWebGL2Program, "s2DArray"); + gl.useProgram(samplerForWebGL2Program); + gl.uniform1i(s3DValLoc, 0); + gl.uniform1i(s2DArrayValLoc, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe('gl.getUniform(samplerForWebGL2Program, s3DValLoc)', '0'); + shouldBe('gl.getUniform(samplerForWebGL2Program, s2DArrayValLoc)', '1'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } + + debug(""); + debug("test getVertexAttrib"); + let array = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + window.buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, array, gl.DYNAMIC_DRAW); + // Vertex attribute 0 is special in that it has no current state, so + // fetching GL_CURRENT_VERTEX_ATTRIB generates an error. Use attribute + // 1 for these tests instead. + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)', 'buffer'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_ENABLED)', 'true'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_SIZE)', '4'); + // Stride MUST be the value the user put in. + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_STRIDE)', '0'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_TYPE)', 'gl.FLOAT'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED)', 'false'); + if (contextVersion > 1) { + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_DIVISOR)', '0'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_INTEGER)', 'false'); + gl.vertexAttribDivisor(1, 2); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_DIVISOR)', '2'); + } + gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 36, 12); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_STRIDE)', '36'); + shouldBe('gl.getVertexAttribOffset(1, gl.VERTEX_ATTRIB_ARRAY_POINTER)', '12'); + gl.disableVertexAttribArray(1); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_ENABLED)', 'false'); + gl.vertexAttrib4f(1, 5, 6, 7, 8); + shouldBe('gl.getVertexAttrib(1, gl.CURRENT_VERTEX_ATTRIB)', '[5, 6, 7, 8]'); + if (contextVersion > 1) { + let intArray = new Int32Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); + gl.bufferData(gl.ARRAY_BUFFER, intArray, gl.DYNAMIC_DRAW); + gl.enableVertexAttribArray(1); + // feed fixed-point data to buffer + gl.vertexAttribIPointer(1, 4, gl.INT, false, 0, 0); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_TYPE)', 'gl.INT'); + shouldBe('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_INTEGER)', 'true'); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + let validArrayForVertexAttrib = new Array( + gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, + gl.VERTEX_ATTRIB_ARRAY_ENABLED, + gl.VERTEX_ATTRIB_ARRAY_SIZE, + gl.VERTEX_ATTRIB_ARRAY_STRIDE, + gl.VERTEX_ATTRIB_ARRAY_TYPE, + gl.VERTEX_ATTRIB_ARRAY_NORMALIZED, + gl.CURRENT_VERTEX_ATTRIB + ); + if (contextVersion > 1) { + validArrayForVertexAttrib[validArrayForVertexAttrib.length] = gl.VERTEX_ATTRIB_ARRAY_DIVISOR; + validArrayForVertexAttrib[validArrayForVertexAttrib.length] = gl.VERTEX_ATTRIB_ARRAY_INTEGER; + } + await testInvalidArgument( + "getVertexAttrib", + "parameter", + validArrayForVertexAttrib, + function(parameter) { + return gl.getVertexAttrib(1, parameter); + } + ); + let numVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, 'gl.getVertexAttrib(' + numVertexAttribs + ', gl.CURRENT_VERTEX_ATTRIB)'); + + debug(""); + debug("Test cases where name == 0"); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + + shouldNotBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + gl.deleteTexture(texture); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + shouldNotBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + gl.deleteRenderbuffer(renderbuffer); + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)', 'gl.NONE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + gl.deleteBuffer(buffer); + shouldBeNull('gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + if (contextVersion > 1) { + debug(""); + debug("Test getInternalformatParameter") + + shouldBeNonNull('gl.getInternalformatParameter(gl.RENDERBUFFER, gl.R32I, gl.SAMPLES)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + await testInvalidArgument( + "getInternalformatParameter", + "target", + [ gl.RENDERBUFFER ], + function(target) { + return gl.getInternalformatParameter(target, gl.R32I, gl.SAMPLES); + }); + + await testInvalidArgument( + "getInternalformatParameter", + "pname", + [ gl.SAMPLES ], + function(pname) { + return gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA4, pname); + }); + + let validArrayForInterformat = new Array( + gl.R8, gl.R8_SNORM, gl.RG8, gl.RG8_SNORM, + gl.RGB8, gl.RGB8_SNORM, gl.RGB565, gl.RGBA4, + gl.RGB5_A1, gl.RGBA8, gl.RGBA8_SNORM, gl.RGB10_A2, + gl.RGB10_A2UI, gl.SRGB8, gl.SRGB8_ALPHA8, gl.R16F, + gl.RG16F, gl.RGB16F, gl.RGBA16F, gl.R32F, + gl.RG32F, gl.RGB32F, gl.RGBA32F, gl.R11F_G11F_B10F, + gl.RGB9_E5, gl.R8I, gl.R8UI, gl.R16I, + gl.R16UI, gl.R32I, gl.R32UI, gl.RG8I, + gl.RG8UI, gl.RG16I, gl.RG16UI, gl.RG32I, + gl.RG32UI, gl.RGB8I, gl.RGB8UI, gl.RGB16I, + gl.RGB16UI, gl.RGB32I, gl.RGB32UI, gl.RGBA8I, + gl.RGBA8UI, gl.RGBA16I, gl.RGBA16UI, gl.RGBA32I, + gl.RGBA32UI, gl.RGB, gl.RGBA, gl.DEPTH_STENCIL, gl.DEPTH_COMPONENT16, + gl.DEPTH_COMPONENT24, gl.DEPTH_COMPONENT32F, gl.DEPTH24_STENCIL8, + gl.DEPTH32F_STENCIL8, gl.STENCIL_INDEX8 + ); + await testInvalidArgument( + "getInternalformatParameter", + "internalformat", + validArrayForInterformat, + function(internalformat) { + return gl.getInternalformatParameter(gl.RENDERBUFFER, internalformat, gl.SAMPLES); + }); + + + debug(""); + debug("Test getIndexedParameter"); + window.buffer = gl.createBuffer(); + gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, buffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 64, gl.DYNAMIC_DRAW); + gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buffer, 4, 8); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)', 'buffer'); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)', '8'); + shouldBe('gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)', '4'); + window.buffer1 = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, buffer1); + gl.bufferData(gl.UNIFORM_BUFFER, 64, gl.DYNAMIC_DRAW); + window.offsetUniform = gl.getParameter(gl.UNIFORM_BUFFER_OFFSET_ALIGNMENT); + gl.bindBufferRange(gl.UNIFORM_BUFFER, 1, buffer1, offsetUniform, 8); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)', 'buffer1'); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, 1)', '8'); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_START, 1)', 'offsetUniform'); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, 1, null); + shouldBe('gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, 1)', 'null'); + + let validArrayForTarget = new Array( + gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, + gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, + gl.TRANSFORM_FEEDBACK_BUFFER_START, + gl.UNIFORM_BUFFER_BINDING, + gl.UNIFORM_BUFFER_SIZE, + gl.UNIFORM_BUFFER_START + ); + await testInvalidArgument( + "getIndexedParameter", + "target", + validArrayForTarget, + function(target) { + return gl.getIndexedParameter(target, 0); + }); + + debug(""); + debug("Test getSamplerParameter"); + window.sampler = gl.createSampler(); + gl.samplerParameteri(sampler, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL); + gl.samplerParameteri(sampler, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + gl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.samplerParameterf(sampler, gl.TEXTURE_MAX_LOD, 10); + gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.samplerParameterf(sampler, gl.TEXTURE_MIN_LOD, 0); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_COMPARE_FUNC)', 'gl.LEQUAL'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_COMPARE_MODE)', 'gl.COMPARE_REF_TO_TEXTURE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MAG_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MAX_LOD)', '10'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MIN_FILTER)', 'gl.NEAREST'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_MIN_LOD)', '0'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_R)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_S)', 'gl.CLAMP_TO_EDGE'); + shouldBe('gl.getSamplerParameter(sampler, gl.TEXTURE_WRAP_T)', 'gl.CLAMP_TO_EDGE'); + let validArrayForSamplerParameter = new Array( + gl.TEXTURE_COMPARE_FUNC, + gl.TEXTURE_COMPARE_MODE, + gl.TEXTURE_MAG_FILTER, + gl.TEXTURE_MAX_LOD, + gl.TEXTURE_MIN_FILTER, + gl.TEXTURE_MIN_LOD, + gl.TEXTURE_WRAP_R, + gl.TEXTURE_WRAP_S, + gl.TEXTURE_WRAP_T + ); + await testInvalidArgument( + "getSamplerParameter", + "pname", + validArrayForSamplerParameter, + function(pname) { + return gl.getSamplerParameter(sampler, pname); + }); + + debug(""); + debug("Test getSyncParameter"); + window.sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + shouldBe('gl.getSyncParameter(sync, gl.OBJECT_TYPE)', 'gl.SYNC_FENCE'); + let sync_status = gl.getSyncParameter(sync, gl.SYNC_STATUS); + switch (sync_status) { + case gl.UNSIGNALED: + testPassed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) is gl.UNSIGNALED'); + break; + case gl.SIGNALED: + testPassed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) is gl.SIGNALED'); + break; + default: + testFailed('gl.getSyncParameter(sync, gl.SYNC_CONDITION) was ' + sync_status + + ', expected gl.UNSIGNALED or gl.SIGNALED'); + break; + } + shouldBe('gl.getSyncParameter(sync, gl.SYNC_CONDITION)', 'gl.SYNC_GPU_COMMANDS_COMPLETE'); + shouldBe('gl.getSyncParameter(sync, gl.SYNC_FLAGS)', '0'); + let validArrayForSyncParameter = new Array( + gl.OBJECT_TYPE, + gl.SYNC_STATUS, + gl.SYNC_CONDITION, + gl.SYNC_FLAGS + ); + await testInvalidArgument( + "getSyncParameter", + "pname", + validArrayForSyncParameter, + function(pname) { + return gl.getSyncParameter(sync, pname); + }); + + debug(""); + debug("Test getQueryParameter"); + window.query = gl.createQuery(); + gl.beginQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query); + gl.endQuery(gl.TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + shouldBe('gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE)', 'false'); + // Queries' results are tested elsewhere in the conformance suite. It's complicated + // to wait for this query's result to become available and verify it. + let validArrayForPname = new Array( + gl.QUERY_RESULT, + gl.QUERY_RESULT_AVAILABLE + ); + await testInvalidArgument( + "getQueryParameter", + "pname", + validArrayForPname, + function(pname) { + return gl.getQueryParameter(query, pname); + } + ); + + debug(""); + debug("Test getFragDataLocation"); + let baseVertShader = '' + + '#version 300 es\n' + + 'uniform mat4 modelViewMatrix;\n' + + 'uniform mat4 projectionMatrix;\n' + + 'in vec4 vertex;\n' + + 'out vec4 position;\n' + + 'void main (void)\n' + + '{\n' + + ' position = modelViewMatrix * vertex;\n' + + ' gl_Position = projectionMatrix * position;\n' + + '}\n'; + let baseFragShader = '' + + '#version 300 es\n' + + 'in lowp vec4 position;\n' + + 'layout(location = 0) out mediump vec4 fragColor;\n' + + 'void main (void)\n' + + '{\n' + + ' fragColor = position;\n' + + '}\n'; + window.vertShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertShader, baseVertShader); + gl.compileShader(vertShader); + shouldBe('gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)', 'true'); + window.fragShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragShader, baseFragShader); + gl.compileShader(fragShader); + shouldBe('gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)', 'true'); + window.program = gl.createProgram(); + gl.attachShader(program, vertShader); + gl.attachShader(program, fragShader); + gl.linkProgram(program); + shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)','true'); + shouldBe('gl.getFragDataLocation(program, "vertexColor")', '-1'); + shouldBe('gl.getFragDataLocation(program, "modelViewMatrix")', '-1'); + shouldBe('gl.getFragDataLocation(program, "projectionMatrix")', '-1'); + shouldBe('gl.getFragDataLocation(program, "position")', '-1'); + shouldBe('gl.getFragDataLocation(program, "fragColor")', '0'); + + debug(""); + debug("Test getActiveUniforms"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true'); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + + let numActiveUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + let blockIndex = gl.getUniformBlockIndex(program, "Transform"); + let uniformIndices = []; + for (let i = 0; i < numActiveUniforms; i++) + uniformIndices.push(i); + let types = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_TYPE); + let sizes = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_SIZE); + let blockIndices = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_BLOCK_INDEX); + let offsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + let arrayStrides = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_ARRAY_STRIDE); + let matrixStrides = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_MATRIX_STRIDE); + let rowMajors = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_IS_ROW_MAJOR); + for (let i = 0; i < numActiveUniforms; i++) { + if (types[i] != gl.FLOAT_MAT4 && types[i] != gl.FLOAT_MAT3) + testFailed("expected value: GL_FLOAT_MAT4 or GL_FLOAT_MAT3" + " actual value for UNIFORM_TYPE for uniform index[" + i + "]:" + wtu.glEnumToString(gl, types[i])); + if (sizes[i] != 1) + testFailed("expected value: 1" + " actual value for UNIFORM_SIZE for uniform index[" + i + "]:" + sizes[i]); + if (blockIndices[i] != blockIndex) + testFailed("expected value: 0" + " actual value for UNIFORM_BLOCK_INDEX for uniform index[" + i + "]:" + blockIndices[i]); + if (offsets[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_OFFSET for uniform index[" + i + "]:" + offsets[i]); + if (arrayStrides[i] != 0) + testFailed("expected value: 0" + " actual value for UNIFORM_ARRAY_STRIDE for uniform index[" + i + "]:" + arrayStrides[i]); + if (matrixStrides[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_MATRIX_STRIDE for uniform index[" + i + "]:" + matrixStrides[i]); + shouldBe(`"${typeof rowMajors[i]}"`, '"boolean"'); + if (rowMajors[i] != false) + testFailed("expected value: 0" + " actual value for UNIFORM_IS_ROW_MAJOR for uniform index[" + i + "]:" + rowMajors[i]); + } + + validArrayForPname = new Array( + gl.UNIFORM_TYPE, + gl.UNIFORM_SIZE, + gl.UNIFORM_BLOCK_INDEX, + gl.UNIFORM_OFFSET, + gl.UNIFORM_ARRAY_STRIDE, + gl.UNIFORM_MATRIX_STRIDE, + gl.UNIFORM_IS_ROW_MAJOR + ); + await testInvalidArgument( + "getActiveUniforms", + "pname", + validArrayForPname, + function(pname) { + return gl.getActiveUniforms(program, uniformIndices, pname); + } + ); + + debug(""); + debug("Test getUniformBlockIndex"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBe('gl.getUniformBlockIndex(program, "Transform")', '0'); + shouldBe('gl.getUniformBlockIndex(program, "u_modelViewMatrix")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(program, "normal")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(program, "u_normal")', 'gl.INVALID_INDEX'); + window.noUniformProgram = wtu.loadStandardProgram(gl); + gl.linkProgram(noUniformProgram); + shouldBeTrue('gl.getProgramParameter(noUniformProgram, gl.LINK_STATUS)'); + shouldBe('gl.getUniformBlockIndex(noUniformProgram, "u_modelViewProjMatrix")', 'gl.INVALID_INDEX'); + shouldBe('gl.getUniformBlockIndex(noUniformProgram, "u_normal")', 'gl.INVALID_INDEX'); + + debug(""); + debug("Test getActiveUniformBlockName"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBeEqualToString('gl.getActiveUniformBlockName(program, 0)', 'Transform'); + shouldBeNull('gl.getActiveUniformBlockName(program, -1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(program, 1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(program, gl.INVALID_INDEX)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + window.noLinkProgram = gl.createProgram(); + shouldBeFalse('gl.getProgramParameter(noLinkProgram, gl.LINK_STATUS)'); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.getActiveUniformBlockName(noLinkProgram, 0)'); + noUniformProgram = wtu.loadStandardProgram(gl); + gl.linkProgram(noUniformProgram); + shouldBeTrue('gl.getProgramParameter(noUniformProgram, gl.LINK_STATUS)'); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, -1)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, 0)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeNull('gl.getActiveUniformBlockName(noUniformProgram, gl.INVALID_INDEX)'); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + debug(""); + debug("Test getActiveUniformBlockParameter"); + program = wtu.loadUniformBlockProgram(gl); + gl.linkProgram(program); + shouldBeTrue('gl.getProgramParameter(program, gl.LINK_STATUS)'); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_BINDING)', '0'); + gl.uniformBlockBinding(program, 0, 1); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_BINDING)', '1'); + // The actual block data size can be bigger than 164, depending on the uniform block layout. + shouldBeTrue('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_DATA_SIZE) >= 164'); + shouldBe('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_ACTIVE_UNIFORMS)', '3'); + shouldBeTrue('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER)'); + shouldBeFalse('gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER)'); + let indices = gl.getActiveUniformBlockParameter(program, 0, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES); + for (let i = 0; i < 3; i++) { + if (indices[i] < 0) + testFailed("expected value >= 0" + " actual value for UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES for uniform index[" + i + "]:" + indices[i]); + } + validArrayForPname = new Array( + gl.UNIFORM_BLOCK_BINDING, + gl.UNIFORM_BLOCK_DATA_SIZE, + gl.UNIFORM_BLOCK_ACTIVE_UNIFORMS, + gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, + gl.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, + gl.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER + ); + await testInvalidArgument( + "getActiveUniformBlockParameter", + "pname", + validArrayForPname, + function(pname) { + return gl.getActiveUniformBlockParameter(program, 0, pname); + } + ); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + finishTest(); +})(); + +let successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js new file mode 100644 index 0000000000..97cad40868 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/gl-vertex-attrib.js @@ -0,0 +1,263 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("This test ensures WebGL implementations vertexAttrib can be set and read."); + +debug(""); +debug("Canvas.getContext"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + debug("Checking gl.vertexAttrib."); + + var numVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + for (var ii = 0; ii < numVertexAttribs; ++ii) { + gl.vertexAttrib1fv(ii, [1]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib1fv(ii, new Float32Array([-1])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2fv(ii, [1, 2]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2fv(ii, new Float32Array([1, -2])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '-2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3fv(ii, [1, 2, 3]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3fv(ii, new Float32Array([1, -2, 3])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '-2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib4fv(ii, [1, 2, 3, 4]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '4'); + + gl.vertexAttrib4fv(ii, new Float32Array([1, 2, -3, 4])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '-3'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '4'); + + gl.vertexAttrib1f(ii, 5); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '5'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib2f(ii, 6, 7); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '6'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib3f(ii, 7, 8, 9); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '8'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '9'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '1'); + + gl.vertexAttrib4f(ii, 6, 7, 8, 9); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Float32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '6'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '7'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '8'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '9'); + + if (contextVersion > 1) { + gl.vertexAttribI4i(ii, -1, 0, 1, 2); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4ui(ii, 0, 1, 2, 3); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + + gl.vertexAttribI4iv(ii, [-1, 0, 1, 2]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4iv(ii, new Int32Array([1, 0, -1, 2])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Int32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '-1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '2'); + + gl.vertexAttribI4uiv(ii, [0, 1, 2, 3]); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + + gl.vertexAttribI4uiv(ii, new Uint32Array([0, 2, 1, 3])); + shouldBeType('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)', 'Uint32Array'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[0]', '0'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[1]', '2'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[2]', '1'); + shouldBe('gl.getVertexAttrib(' + ii + ', gl.CURRENT_VERTEX_ATTRIB)[3]', '3'); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Checking out-of-range vertexAttrib index"); + gl.getVertexAttrib(numVertexAttribs, gl.CURRENT_VERTEX_ATTRIB); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, [1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, new Float32Array([-1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, [1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, new Float32Array([1, -2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, [1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, new Float32Array([1, -2, 3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, [1, 2, 3, 4]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, new Float32Array([1, 2, -3, 4])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1f(numVertexAttribs, 5); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2f(numVertexAttribs, 6, 7); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3f(numVertexAttribs, 7, 8, 9); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4f(numVertexAttribs, 6, 7, 8, 9); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + if (contextVersion > 1) { + gl.vertexAttribI4i(numVertexAttribs, -1, 0, 1, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4ui(numVertexAttribs, 0, 1, 2, 3); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, [-1, 0, 1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, new Int32Array([1, 0, -1, 2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, [0, 1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, new Uint32Array([0, 2, 1, 3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + } + + debug(""); + debug("Checking invalid array lengths"); + numVertexAttribs = numVertexAttribs - 1; + gl.vertexAttrib1fv(numVertexAttribs, []); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib1fv(numVertexAttribs, new Float32Array([])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, [1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib2fv(numVertexAttribs, new Float32Array([1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, [1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib3fv(numVertexAttribs, new Float32Array([1, -2])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, [1, 2, 3]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttrib4fv(numVertexAttribs, new Float32Array([1, 2, -3])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + if (contextVersion > 1) { + gl.vertexAttribI4iv(numVertexAttribs, [-1, 0, 1]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4iv(numVertexAttribs, new Int32Array([1, 0, -1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, [0, 1, 2]); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + + gl.vertexAttribI4uiv(numVertexAttribs, new Uint32Array([0, 2, 1])); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + } +} + +debug(""); +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js new file mode 100644 index 0000000000..edcad3b1f6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/instanceof-test.js @@ -0,0 +1,105 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +var wtu = WebGLTestUtils; +description(document.title); +debug("Tests that instanceof works on WebGL objects."); +debug(""); + +function checkGLError(message) { + var error = gl.getError(); + if (error != gl.NO_ERROR) { + wtu.error("Error: " + message + " caused " + wtu.glEnumToString(gl, error)); + } +} + +var gl = wtu.create3DContext("canvas", undefined, contextVersion); +if (contextVersion === 1) { + shouldBeTrue('gl instanceof WebGLRenderingContext'); +} else if (contextVersion === 2) { + shouldBeTrue('gl instanceof WebGL2RenderingContext'); +} + +shouldBeTrue('gl.createBuffer() instanceof WebGLBuffer'); +checkGLError("createBuffer") + +shouldBeTrue('gl.createFramebuffer() instanceof WebGLFramebuffer'); +checkGLError("createFramebuffer") + +shouldBeTrue('gl.createProgram() instanceof WebGLProgram'); +checkGLError("createProgram") + +shouldBeTrue('gl.createRenderbuffer() instanceof WebGLRenderbuffer'); +checkGLError("createRenderbuffer") + +shouldBeTrue('gl.createShader(gl.VERTEX_SHADER) instanceof WebGLShader'); +checkGLError("createShader") + +shouldBeTrue('gl.createTexture() instanceof WebGLTexture'); +checkGLError("createTexture") + +if (contextVersion > 1) { + shouldBeTrue('gl.createQuery() instanceof WebGLQuery'); + checkGLError("createQuery") + + shouldBeTrue('gl.createSampler() instanceof WebGLSampler'); + checkGLError("createSampler") + + shouldBeTrue('gl.createTransformFeedback() instanceof WebGLTransformFeedback'); + checkGLError("createTransformFeedback") + + shouldBeTrue('gl.createVertexArray() instanceof WebGLVertexArrayObject'); + checkGLError("createVertexArray") +} + +var program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['vPosition'], [0]); + +shouldBeTrue('gl.getUniformLocation(program, "color") instanceof WebGLUniformLocation'); +checkGLError("getUniformLocation") + +shouldBeTrue('gl.getActiveAttrib(program, 0) instanceof WebGLActiveInfo'); +checkGLError("getActiveAttrib") + +shouldBeTrue('gl.getActiveUniform(program, 0) instanceof WebGLActiveInfo'); +checkGLError("getActiveUniform") + +debug(""); +debug("Tests that those WebGL objects can not be constructed through new operator"); +debug(""); + +function shouldThrowWithNew(objectType, objectName) { + try { + new objectType; + testFailed('new ' + objectName + ' did not throw'); + } catch (e) { + testPassed('new ' + objectName + ' threw an error'); + } +} + +shouldThrowWithNew(window.WebGLRenderingContext, 'WebGLRenderingContext'); +shouldThrowWithNew(window.WebGLActiveInfo, 'WebGLActiveInfo'); +shouldThrowWithNew(window.WebGLBuffer, 'WebGLBuffer'); +shouldThrowWithNew(window.WebGLFramebuffer, 'WebGLFramebuffer'); +shouldThrowWithNew(window.WebGLProgram, 'WebGLProgram'); +shouldThrowWithNew(window.WebGLRenderbuffer, 'WebGLRenderbuffer'); +shouldThrowWithNew(window.WebGLShader, 'WebGLShader'); +shouldThrowWithNew(window.WebGLTexture, 'WebGLTexture'); +shouldThrowWithNew(window.WebGLUniformLocation, 'WebGLUniformLocation'); +shouldThrowWithNew(window.WebGLShaderPrecisionFormat, 'WebGLShaderPrecisionFormat'); +if (contextVersion > 1) { + shouldThrowWithNew(window.WebGLQuery, 'WebGLQuery'); + shouldThrowWithNew(window.WebGLSampler, 'WebGLSampler'); + shouldThrowWithNew(window.WebGLSync, 'WebGLSync'); + shouldThrowWithNew(window.WebGLTransformFeedback, 'WebGLTransformFeedback'); + shouldThrowWithNew(window.WebGLVertexArrayObject, 'WebGLVertexArrayObject'); +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js new file mode 100644 index 0000000000..b28c484843 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/invalid-vertex-attrib-test.js @@ -0,0 +1,129 @@ +var createInvalidAttribTestFn = (function() { + +async function testPreserveDrawingBufferTrue(gl, drawFn, clear) { + debug(''); + debug(`test preserveDrawingBuffer: true with ${drawFn.name} ${clear ? 'with' : 'without'} clear`); + + if (clear) { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + const skipTest = drawFn(gl); + if (skipTest) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + + await waitForComposite(); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function setupWebGL({ + webglVersion, + shadersFn, + attribs, +}) { + const positionBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuf); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + const indexBuf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); + return gl; +} + +function createInvalidAttribTestFn(gl) { + const vs = ` + attribute vec4 vPosition; + void main() + { + gl_Position = vPosition; + } + `; + + const fs = ` + precision mediump float; + void main() + { + gl_FragColor = vec4(1, 0, 0, 1); + } + ` + + const program = wtu.setupProgram(gl, [vs, fs], ["vPosition"]); + if (!program) { + debug(`program failed to compile: ${wtu.getLastError()}`); + } + + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1, -1, + 1, -1, + -1, 1, + -1, 1, + 1, -1, + 1, 1, + ]), gl.STATIC_DRAW); + + const indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, + new Uint8Array([0, 1, 2, 3, 4, 5]), + gl.STATIC_DRAW); + + return async function invalidAttribTestFn(drawFn) { + debug(''); + + // reset attribs + gl.bindBuffer(gl.ARRAY_BUFFER, null); + const numAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + for (let i = 0; i < numAttribs; ++i) { + gl.disableVertexAttribArray(i); + gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); + } + + debug(`test ${drawFn.name} draws with valid attributes`); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + gl.clearColor(0, 0, 0, 0,); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + + drawFn(gl); + + wtu.checkCanvas(gl, [255, 0, 0, 255], "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug(`test ${drawFn.name} generates INVALID_OPERATION draws with enabled attribute no buffer bound`); + gl.enableVertexAttribArray(1); + + gl.clearColor(0, 0, 0, 0,); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + + drawFn(gl); + + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should generate INVALID_OPERATION"); + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); + }; +} + +return createInvalidAttribTestFn; +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js new file mode 100644 index 0000000000..45e509f50a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/iterable-test.js @@ -0,0 +1,183 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +IterableTest = (function() { + + var wtu = WebGLTestUtils; + + function run(test, iterations) { + var target = iterations || 10; + var count = 0; + + function doNextTest() { + ++count; + debug("Test " + count + " of " + target); + var success = test(); + if (count < target && success !== false) { + wtu.dispatchPromise(doNextTest); + } else { + finishTest(); + } + } + + doNextTest(); + } + + // Creates a canvas and a texture then exits. There are + // no references to either so both should be garbage collected. + function createContextCreationAndDestructionTest() { + var textureSize = null; + + return function() { + var canvas = document.createElement("canvas"); + // This is safe for any device. See drawingBufferWidth in spec. + canvas.width = 2048; + canvas.height = 2048; + var gl = wtu.create3DContext(canvas); + if (textureSize === null) { + var maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE); + textureSize = Math.min(1024, maxTextureSize); + } + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, textureSize, textureSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, + null); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return true; + }; + } + + // Creates many small canvases and attaches them to the DOM. + // This tests an edge case discovered in Chrome where the creation of multiple + // WebGL contexts would eventually lead to context creation failure. + // (crbug.com/319265) The test does not require that old contexts remain + // valid, only that new ones can be created. + function createContextCreationTest() { + return function() { + var canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = 1; + + document.body.appendChild(canvas); + + var gl = wtu.create3DContext(canvas); + if (!gl) { + return false; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return true; + }; + } + + // Draws rectangle on a passed canvas with preserveDrawingBuffer + // and antialiasing ON, tests rect color on every iteration. + function createMultisampleCorruptionTest(gl) { + var lastContext = null; + // Allocate a read back buffer in advance and reuse it for all iterations + // to avoid memory issues because of late garbage collection. + var readBackBuf = new Uint8Array(gl.canvas.width * gl.canvas.height * 4); + + var program = wtu.loadStandardProgram(gl); + var uniforms = wtu.getUniformMap(gl, program); + gl.useProgram(program); + + gl.clearColor(1.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,2.5,0, 1.5,1.5,0, 2.5,1.5,0 ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + gl.vertexAttrib3f(1, 0.0, 0.0, 1.0); + + var identityMat = new Float32Array([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ]); + + gl.uniformMatrix4fv(uniforms.u_modelViewProjMatrix.location, false, identityMat); + + function test() { + var gl2 = wtu.create3DContext(null, {antialias: true}); + + gl2.canvas.width = gl2.canvas.height = 1024; + gl2.canvas.style.width = gl2.canvas.style.height = "1px"; + document.body.appendChild(gl2.canvas); + + gl2.clearColor(1.0, 0.0, 0.0, 1.0); + gl2.clear(gl2.COLOR_BUFFER_BIT); + + if(lastContext) { + gl.drawArrays(gl.TRIANGLES, 0, 3); + var msg = "Canvas should be red"; + wtu.checkCanvasRectColor(gl, + 0, 0, gl.canvas.width, gl.canvas.height, + [255, 0, 0, 255], null, + function() { + testPassed(msg); + }, + function() { + testFailed(msg); + return false; + }, + debug, readBackBuf); + document.body.removeChild(lastContext.canvas); + } + + lastContext = gl2; + return true; + }; + + // First pass does initialization + test(); + + return test; + } + + // Draws repeatedly to a large canvas with preserveDrawingBuffer enabled to + // try and provoke a context loss. + function createPreserveDrawingBufferLeakTest(gl) { + var contextActive = true; + gl.canvas.addEventListener("webglcontextlost", () => { + testFailed("Context was lost"); + contextActive = false; + }); + + function test() { + var x = Math.random() * gl.drawingBufferWidth; + var y = Math.random() * gl.drawingBufferHeight; + var width = Math.random() * (gl.drawingBufferWidth - x); + var height = Math.random() * (gl.drawingBufferHeight - y); + + gl.enable(gl.SCISSOR_TEST); + gl.scissor(x, y, width, height); + gl.clearColor(Math.random(), Math.random(), Math.random(), 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + + return contextActive; + }; + + return test; + } + + return { + run: run, + + createContextCreationAndDestructionTest: createContextCreationAndDestructionTest, + createContextCreationTest: createContextCreationTest, + createMultisampleCorruptionTest: createMultisampleCorruptionTest, + createPreserveDrawingBufferLeakTest: createPreserveDrawingBufferLeakTest + }; + +})(); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js b/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js new file mode 100644 index 0000000000..dfa7c02167 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/line-rendering-quality.js @@ -0,0 +1,163 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; +description("Verifies that lines, both aliased and antialiased, have acceptable quality."); + +let wtu = WebGLTestUtils; +let gl; + +let aa_fbo; + +function setupWebGL1Test(canvasId, useAntialiasing) { + gl = wtu.create3DContext(canvasId, { antialias: useAntialiasing }, contextVersion); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 1.0 setup"); +} + +function setupWebGL2Test(canvasId, useAntialiasing) { + gl = wtu.create3DContext(canvasId, { antialias: false }, contextVersion); + // In WebGL 2.0, we always allocate the back buffer without + // antialiasing. The only question is whether we allocate a + // framebuffer with a multisampled renderbuffer attachment. + aa_fbo = null; + if (useAntialiasing) { + aa_fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, aa_fbo); + let rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + let supported = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES); + // Prefer 4, then 8, then max. + let preferred = [4, 8]; + let allocated = false; + for (let value of preferred) { + if (supported.indexOf(value) >= 0) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, value, gl.RGBA8, + gl.drawingBufferWidth, gl.drawingBufferHeight); + allocated = true; + break; + } + } + if (!allocated) { + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, supported[supported.length - 1], + gl.RGBA8, gl.drawingBufferWidth, gl.drawingBufferHeight); + } + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 2.0 setup"); +} + +function setupLines() { + let prog = wtu.setupSimpleColorProgram(gl, 0); + let loc = gl.getUniformLocation(prog, 'u_color'); + if (loc == null) { + testFailed('Failed to fetch color uniform'); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of line program"); + gl.uniform4f(loc, 1.0, 1.0, 1.0, 1.0); + let buffer = gl.createBuffer(); + let scale = 0.5; + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -scale, -scale, 0.0, 1.0, + -scale, scale, 0.0, 1.0, + scale, scale, 0.0, 1.0, + scale, -scale, 0.0, 1.0, + -scale, -scale, 0.0, 1.0, + ]), gl.STATIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of buffer"); + gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of attribute array"); +} + +function renderLines(contextVersion, useAntialiasing) { + gl.clearColor(0.0, 0.0, 0.5, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.LINE_STRIP, 0, 5); + if (contextVersion == 2 && useAntialiasing) { + // Blit aa_fbo into the real back buffer. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, aa_fbo); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + let w = gl.drawingBufferWidth; + let h = gl.drawingBufferHeight; + gl.blitFramebuffer(0, 0, w, h, + 0, 0, w, h, + gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } +} + +function pixelAboveThreshold(arr, pixelIndex, threshold) { + return (arr[4 * pixelIndex + 0] >= threshold && + arr[4 * pixelIndex + 1] >= threshold && + arr[4 * pixelIndex + 2] >= threshold && + arr[4 * pixelIndex + 3] >= threshold); +} + +function checkLine(arr, threshold, direction) { + // Count number of crossings from below threshold to above (or equal + // to) threshold. Must be equal to 2. + + let numPixels = arr.length / 4; + let numUpCrossings = 0; + let numDownCrossings = 0; + for (let index = 0; index < numPixels - 1; ++index) { + let curPixel = pixelAboveThreshold(arr, index, threshold); + let nextPixel = pixelAboveThreshold(arr, index + 1, threshold); + if (!curPixel && nextPixel) { + ++numUpCrossings; + } else if (curPixel && !nextPixel) { + ++numDownCrossings; + } + } + if (numUpCrossings != numDownCrossings) { + testFailed('Found differing numbers of up->down and down->up transitions'); + } + if (numUpCrossings == 2) { + testPassed('Found 2 lines, looking in the ' + direction + ' direction'); + } else { + testFailed('Found ' + numUpCrossings + ' lines, looking in the ' + + direction + ' direction, expected 2'); + } +} + +function checkResults() { + // Check the middle horizontal and middle vertical line of the canvas. + let w = gl.drawingBufferWidth; + let h = gl.drawingBufferHeight; + let t = 100; + let arr = new Uint8Array(4 * w); + gl.readPixels(0, Math.floor(h / 2), + w, 1, gl.RGBA, gl.UNSIGNED_BYTE, arr); + checkLine(arr, t, 'horizontal'); + arr = new Uint8Array(4 * h); + gl.readPixels(Math.floor(w / 2), 0, + 1, h, gl.RGBA, gl.UNSIGNED_BYTE, arr); + checkLine(arr, t, 'vertical'); +} + +function runTest(contextVersion, canvasId, useAntialiasing) { + switch (contextVersion) { + case 1: { + setupWebGL1Test(canvasId, useAntialiasing); + break; + } + case 2: { + setupWebGL2Test(canvasId, useAntialiasing); + } + } + setupLines(); + renderLines(contextVersion, useAntialiasing); + checkResults(); +} + +function runTests() { + runTest(contextVersion, 'testbed', false); + runTest(contextVersion, 'testbed2', true); +} + +runTests(); +let successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js new file mode 100644 index 0000000000..1202b7868c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js @@ -0,0 +1,247 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +NoOverOptimizeOnUniformArrayTester = (function(){ + +var vshader = [ + "attribute vec4 a_position;", + "void main()", + "{", + " gl_Position = a_position;", + "}" +].join('\n'); + +var fshader_max = [ + "precision mediump float;", + "uniform vec4 colora[$(maxUniformVectors)];", + "void main()", + "{", + " gl_FragColor = vec4(colora[$(usedUniformVector)]);", + "}" +].join('\n'); + +var fshader_max_ab_ab = [ + "precision mediump float;", + "uniform vec4 $(decl1);", + "uniform vec4 $(decl2);", + "void main()", + "{", + "gl_FragColor = vec4($(usage1) + $(usage2));", + "}" +].join('\n'); + +// MaxInt32 is 2^32-1. We need +1 of that to test overflow conditions +var MaxInt32PlusOne = 4294967296; + +function setupTests(gl) { + var tests = []; + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + + // This test is to test drivers the have bugs related to optimizing + // an array of uniforms when only 1 of those uniforms is used. + tests.push({ + desc: "using last element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: maxUniformVectors - 1, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + tests.push({ + desc: "using first element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: 0, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + + // Generate test shaders. We're trying to force the driver to + // overflow from 1 array into the next if it optimizes. So for example if it was C + // + // int big[4]; + // int little[1]; + // big[5] = 124; + // + // Would end up setting little[0] instead of big. Some drivers optimize + // where if you only use say 'big[3]' it will actually only allocate just 1 element + // for big. + // + // But, some drivers have a bug where the fact that they optimized big to 1 element + // does not get passed down to glUniform so when setting the uniform 'big[3]' they + // overwrite memory. + // + // If the driver crashes, yea. We found a bug. We can block the driver. + // Otherwise we try various combinations so that setting 'little[0]' first + // and then setting all elements of 'big' we hope it will overwrite 'little[0]' + // which will show the bug and again we can block the driver. + // + // We don't know how the driver will order, in memory, the various uniforms + // or for that matter we don't even know if they will be contiguous in memory + // but to hopefully expose any bugs we try various combinations. + // + // It could be the compiler orders uniforms alphabetically. + // It could be it orders them in order of declaration. + // It could be it orders them in order of usage. + // + // We also test using only first element of big or just the last element of big. + // + for (var nameOrder = 0; nameOrder < 2; ++nameOrder) { + var name1 = nameOrder ? "colora" : "colorb"; + var name2 = nameOrder ? "colorb" : "colora"; + for (var last = 0; last < 2; ++last) { + var usedUniformVector = last ? maxUniformVectors - 2 : 0; + for (var declOrder = 0; declOrder < 2; ++declOrder) { + var bigName = declOrder ? name1 : name2; + var littleName = declOrder ? name2 : name1; + var decl1 = bigName + "[" + (maxUniformVectors - 1) + "]"; + var decl2 = littleName + "[1]"; + if (declOrder) { + var t = decl1; + decl1 = decl2; + decl2 = t; + } + for (var usageOrder = 0; usageOrder < 2; ++usageOrder) { + var usage1 = bigName + "[" + usedUniformVector + "]"; + var usage2 = littleName + "[0]"; + if (usageOrder) { + var t = usage1; + usage1 = usage2; + usage2 = t; + } + var fSrc = wtu.replaceParams(fshader_max_ab_ab, { + decl1: decl1, + decl2: decl2, + usage1: usage1, + usage2: usage2, + }); + var desc = "testing: " + name1 + ":" + name2 + " using " + (last ? "last" : "first") + + " creating uniforms " + decl1 + " " + decl2 + " and accessing " + usage1 + " " + usage2; + tests.push({ + desc: desc, + maxUniformVectors: maxUniformVectors - 1, + usedUniformVector: usedUniformVector, + source: fSrc, + color: [0, 0, 0, 1], + arrayName: bigName, + extraName: littleName, + }); + } + } + } + } + return tests; +}; + +function testUniformOptimizationIssues(test) { + debug(""); + debug(test.desc); + var fshader = test.source; + if (!fshader) { + fshader = wtu.replaceParams(fshader_max, test); + } + + var consoleElem = document.getElementById("console"); + wtu.addShaderSource( + consoleElem, "vertex shader", vshader); + wtu.addShaderSource( + consoleElem, "fragment shader", fshader); + + var program = wtu.loadProgram(gl, vshader, fshader); + gl.useProgram(program); + + var colorbLocation = gl.getUniformLocation(program, test.extraName + "[0]"); + if (colorbLocation) { + gl.uniform4fv(colorbLocation, [0, 1, 0, 0]); + } + + // Ensure that requesting an array uniform past MaxInt32PlusOne returns no uniform + var nameMaxInt32PlusOne = test.arrayName + "[" + (test.usedUniformVector + MaxInt32PlusOne) + "]"; + assertMsg(gl.getUniformLocation(program, nameMaxInt32PlusOne) === null, + "Requesting " + nameMaxInt32PlusOne + " uniform should return a null uniform location"); + + // Set just the used uniform + var name = test.arrayName + "[" + test.usedUniformVector + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + gl.uniform4fv(uniformLocation, test.color); + wtu.setupIndexedQuad(gl, 1); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Set all the unused uniforms + var locations = []; + var allRequiredUniformLocationsQueryable = true; + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var name = test.arrayName + "[" + ii + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + locations.push(uniformLocation); + if (ii == test.usedUniformVector) { + continue; + } + // Locations > usedUnformVector may not exist. + // Locations <= usedUniformVector MUST exist. + if (ii <= test.usedUniformVector && (uniformLocation === undefined || uniformLocation === null)) { + allRequiredUniformLocationsQueryable = false; + } + gl.uniform4fv(uniformLocation, [1, 0, 0, 1]); + } + if (allRequiredUniformLocationsQueryable) { + testPassed("allRequiredUniformLocationsQueryable is true."); + } + else { + testFailed("allRequiredUniformLocationsQueryable should be true. Was false."); + } + var positionLoc = gl.getAttribLocation(program, "a_position"); + wtu.setupIndexedQuad(gl, 1, positionLoc); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Check we can read & write each uniform. + // Note: uniforms past test.usedUniformVector might not exist. + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + gl.uniform4fv(locations[ii], [ii + 4, ii + 2, ii + 3, ii + 1]); + } + + var kEpsilon = 0.01; + var isSame = function(v1, v2) { + return Math.abs(v1 - v2) < kEpsilon; + }; + + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var location = locations[ii]; + if (location) { + var value = gl.getUniform(program, locations[ii]); + if (!isSame(value[0], ii + 4) || + !isSame(value[1], ii + 2) || + !isSame(value[2], ii + 3) || + !isSame(value[3], ii + 1)) { + testFailed("location: " + ii + " was not correct value"); + break; + } + } + } +} + +function runOneTest(gl, test) { + testUniformOptimizationIssues(test); +}; + +function runTests(gl, tests) { + debug(""); + debug("Test drivers don't over optimize unused array elements"); + + for (var ii = 0; ii < tests.length; ++ii) { + runOneTest(gl, tests[ii]); + } +}; + +return { + setupTests : setupTests, + runTests : runTests +}; + +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js b/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js new file mode 100644 index 0000000000..362023ce01 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/oes-texture-float-and-half-float-linear.js @@ -0,0 +1,151 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function testTexLinear(gl, extensionName, internalFormatWebgl2, pixelType) { + var wtu = WebGLTestUtils; + + // Before the extension is enabled + var extensionEnabled = false; + runTestSuite(extensionEnabled); + + if (!gl.getExtension(extensionName)) + testPassed("No " + extensionName + " support -- this is legal"); + else { + // After the extension is enabled + extensionEnabled = true; + runTestSuite(extensionEnabled); + } + + function runTestSuite(extensionEnabled) + { + var magF = [gl.NEAREST, gl.LINEAR]; + var minF = [gl.NEAREST, gl.LINEAR, gl.NEAREST_MIPMAP_NEAREST, gl.NEAREST_MIPMAP_LINEAR, gl.LINEAR_MIPMAP_NEAREST, gl.LINEAR_MIPMAP_LINEAR]; + var tex2DFShader = [ + 'uniform sampler2D tex;', + 'void main() {', + ' gl_FragData[0] = texture2D(tex, vec2(0.5, 0.5)) * vec4(4.0, 2.0, 2.0, 1);', + '}'].join('\n'); + + var positionVertexShader = [ + 'attribute vec4 vPosition;', + 'void main() {', + ' gl_Position = vPosition;', + '}'].join('\n'); + + var texCubeFShader = [ + 'uniform samplerCube tex;', + 'void main() {', + ' gl_FragColor = textureCube(tex, normalize(vec3(0.5, 0.5, 1))) * vec4(4.0, 2.0, 2.0, 1);', + '}'].join('\n'); + + var vs = wtu.loadShader(gl, positionVertexShader, gl.VERTEX_SHADER); + var fs_2d = wtu.loadShader(gl, tex2DFShader, gl.FRAGMENT_SHADER); + var fs_cube = wtu.loadShader(gl, texCubeFShader, gl.FRAGMENT_SHADER); + + // TEXTURE_2D + var program = wtu.setupProgram(gl, [vs, fs_2d]); + gl.useProgram(program); + wtu.setupUnitQuad(gl); + for (var kk = 0; kk < 2; ++kk) { + for (var ii = 0; ii < 6; ++ii) { + var linear = false; + if (magF[kk] == gl.LINEAR || (minF[ii] != gl.NEAREST && minF[ii] != gl.NEAREST_MIPMAP_NEAREST)) + linear = true; + var color = (!extensionEnabled && linear) ? [0, 0, 0, 255] : [255, 255, 255, 255]; + runEachTest(gl.TEXTURE_2D, magF[kk], minF[ii], linear, extensionEnabled, color); + } + } + + // TEXTURE_CUBE_MAP + var programCube = wtu.setupProgram(gl, [vs, fs_cube]); + gl.useProgram(programCube); + wtu.setupUnitQuad(gl); + for (var kk = 0; kk < 2; ++kk) { + for (var ii = 0; ii < 6; ++ii) { + var linear = false; + if (magF[kk] == gl.LINEAR || (minF[ii] != gl.NEAREST && minF[ii] != gl.NEAREST_MIPMAP_NEAREST)) + linear = true; + var color = (!extensionEnabled && linear) ? [0, 0, 0, 255] : [255, 255, 255, 255]; + runEachTest(gl.TEXTURE_CUBE_MAP, magF[kk], minF[ii], linear, extensionEnabled, color); + } + } + } + + function runEachTest(textureTarget, magFilter, minFilter, linear, extensionEnabled, expected) + { + const format = gl.RGBA; + let internalFormat = format; + if (wtu.isWebGL2(gl)) { + internalFormat = gl[internalFormatWebgl2]; + } + var numberOfChannels = 4; + debug(""); + debug("testing target: " + wtu.glEnumToString(gl,textureTarget) + + ", testing format: " + wtu.glEnumToString(gl, format) + + ", magFilter is: " + wtu.glEnumToString(gl, magFilter) + + ", minFilter is: " + wtu.glEnumToString(gl, minFilter) + + ", " + extensionName + " is " + (extensionEnabled ? "enabled": "not enabled") + ); + + // Generate data. + var width = 4; + var height = 4; + var canvas2d = document.createElement('canvas'); + canvas2d.width = width; + canvas2d.height = height; + var ctx2d = canvas2d.getContext('2d'); + var color = [64, 128, 128, 255]; + ctx2d.fillStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ")"; + ctx2d.fillRect(0, 0, width, height); + + var texture = gl.createTexture(); + gl.bindTexture(textureTarget, texture); + gl.texParameteri(textureTarget, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(textureTarget, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(textureTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + if (textureTarget == gl.TEXTURE_2D) { + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, gl[pixelType], canvas2d); + if (minFilter != gl.NEAREST && minFilter != gl.LINEAR) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors during texture setup"); + gl.generateMipmap(gl.TEXTURE_2D); + if (gl.getError() != gl.NO_ERROR) { + debug("generateMipmap failed for floating-point TEXTURE_2D -- this is legal -- skipping the rest of this test"); + return; + } + } + } else if (textureTarget == gl.TEXTURE_CUBE_MAP) { + var targets = [ + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + for (var tt = 0; tt < targets.length; ++tt) + gl.texImage2D(targets[tt], 0, internalFormat, format, gl[pixelType], canvas2d); + if (minFilter != gl.NEAREST && minFilter != gl.LINEAR) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors during texture setup"); + gl.generateMipmap(gl.TEXTURE_CUBE_MAP); + if (gl.getError() != gl.NO_ERROR) { + debug("generateMipmap failed for floating-point TEXTURE_CUBE_MAP -- this is legal -- skipping the rest of this test"); + return; + } + } + } + wtu.clearAndDrawUnitQuad(gl); + if (!linear) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with non-Linear filter should succeed with NO_ERROR no matter whether " + extensionName + " is enabled or not"); + } else if (!extensionEnabled) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with Linear filter should produce [0, 0, 0, 1.0] with NO_ERROR if " + extensionName + " isn't enabled"); + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, pixelType + " texture with Linear filter should succeed with NO_ERROR if " + extensionName + " is enabled"); + } + + wtu.checkCanvas(gl, expected, "should be " + expected[0] + "," + expected[1] + "," + expected[2] + "," + expected[3]); + } +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js new file mode 100644 index 0000000000..b25a7dd026 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/offscreencanvas-transfer-image-bitmap.js @@ -0,0 +1,57 @@ +function testTransferToImageBitmap(webglContextVersion, bitmap) { + var internalFormat = "RGBA"; + var pixelFormat = "RGBA"; + var pixelType = "UNSIGNED_BYTE"; + + var width = 32; + var height = 32; + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + var gl = WebGLTestUtils.create3DContext(canvas); + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + TexImageUtils.setupTexturedQuad(gl, internalFormat); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(gl.TEXTURE_2D, texture); + // Set up texture parameters + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + var targets = [gl.TEXTURE_2D]; + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], bitmap); + } + for (var tt = 0; tt < targets.length; ++tt) { + // Draw the triangles + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + var buf = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); + _checkCanvas(buf, width, height, webglContextVersion); + } +} + +function _checkCanvas(buf, width, height, webglContextVersion) +{ + for (var i = 0; i < width * height; i++) { + if (buf[i * 4] != 255 || buf[i * 4 + 1] != 255 || + buf[i * 4 + 2] != 0 || buf[i * 4 + 3] != 255) { + testFailed("OffscreenCanvas." + webglContextVersion + + ": This pixel should be [255, 255, 0, 255], but it is: [" + buf[i * 4] + ", " + + buf[i * 4 + 1] + ", " + buf[i * 4 + 2] + ", " + buf[i * 4 + 3] + "]."); + return; + } + } + testPassed("TransferToImageBitmap test on OffscreenCanvas." + webglContextVersion + " passed"); +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js new file mode 100644 index 0000000000..75e3496dfb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/out-of-bounds-test.js @@ -0,0 +1,321 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +var OutOfBoundsTest = (function() { + +var runCommonInvalidValueTests = function(callTemplate, gl, wtu, ext) { + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: -1, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: -1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: -1, type: 'gl.UNSIGNED_BYTE', offset: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_BYTE', offset: -1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: '0xffffffff', type: 'gl.UNSIGNED_BYTE', offset: 0})); +}; + +var setupProgramAndBindVertexArray = function(gl, wtu) { + var program = wtu.loadStandardProgram(gl); + + gl.useProgram(program); + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.enableVertexAttribArray(0); + + return program; +}; + +var setupProgram2 = function(gl, wtu) { + var vshader = [ + 'attribute vec3 aOne;', + 'attribute vec2 aTwo;', + 'void main() {', + ' gl_Position = vec4(aOne, 1.0) + vec4(aTwo, 0.0, 1.0);', + '}' + ].join('\n'); + + var fshader = [ + 'void main() {', + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);', + '}' + ].join('\n'); + + var program = wtu.setupProgram(gl, [vshader, fshader], [ "aOne", "aTwo" ]); + if (!program) { + testFailed("failed to create test program"); + } + return program; +}; + +var runDrawArraysTest = function(callTemplate, gl, wtu, ext) { + var program = setupProgramAndBindVertexArray(gl, wtu); + + debug("Test empty buffer") + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 1})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 1, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 100, count: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug("") + debug("Test buffer with 3 float vectors") + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 3})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 3, count: 2})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 100, count: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug("") + debug("Test buffer with interleaved (3+2) float vectors") + + setupProgram2(gl, wtu); + + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + // enough for 9 vertices, so 3 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*5), gl.STATIC_DRAW); + + // bind first 3 elements, with a stride of 5 float elements + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, 0); + // bind 2 elements, starting after the first 3; same stride of 5 float elements + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, 3*4); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9})); + + // negative values must generate INVALID_VALUE; they can never be valid + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: 0, count: -500})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: -200, count: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: -200, count: -500})); + + // 0xffffffff needs to convert to a 'long' IDL argument as -1, as per + // WebIDL 4.1.7. JS ToInt32(0xffffffff) == -1. Thus INVALID_VALUE. + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: 0, count: '0xffffffff'})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: '0xffffffff', count: '0xffffffff'})); + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {offset: '0xffffffff', count: 1})); + + // values that could otherwise be valid but aren't due to bindings generate + // INVALID_OPERATION + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: 0, count: 10000})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {offset: '0x7fffffff', count: 1})); +}; + +var runDrawElementsTest = function(callTemplate, gl, wtu, ext) { + var program = setupProgramAndBindVertexArray(gl, wtu); + var contextVersion = wtu.getDefault3DContextVersion(); + + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0.5,0, -0.5,-0.5,0, 0.5,-0.5,0 ]), gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + + debug(''); + debug('Test null index buffer'); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + + debug(''); + debug('Test empty index buffer'); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_BYTE', offset: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + + debug(''); + debug('Test buffer with 3 byte indexes'); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2 ]), gl.STATIC_DRAW); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 2})); + wtu.shouldGenerateGLError(gl, [gl.NO_ERROR, gl.INVALID_OPERATION], wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 0})); + runCommonInvalidValueTests(callTemplate, gl, wtu, ext); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 10000, type: 'gl.UNSIGNED_BYTE', offset: 10000})); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, (new Uint8Array([ 3, 0, 1, 2 ])).subarray(1), gl.STATIC_DRAW)'); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, new Uint8Array([ 3, 0, 1]))'); + var indexValidationError = wtu.shouldGenerateGLError(gl, [gl.INVALID_OPERATION, gl.NO_ERROR], wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 0, (new Uint8Array([ 3, 0, 1, 2 ])).subarray(1))'); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_BYTE', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_BYTE', offset: 0})); + + debug(''); + debug('Test buffer with interleaved (3+2) float vectors'); + + setupProgram2(gl, wtu); + + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + // enough for 9 vertices, so 3 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*5), gl.STATIC_DRAW); + + // bind first 3 elements, with a stride of 5 float elements + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 5*4, 0); + // bind 2 elements, starting after the first 3; same stride of 5 float elements + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 5*4, 3*4); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + var ebo = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + // For WebGL 2, PRIMITIVE_RESTART_FIXED_INDEX is always enabled. + // 0xffff will be handled as a primitive restart. + if (contextVersion <= 1) { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( + [ 0, 1, 2, + 1, 2, 0, + 2, 0, 1, + 201, 202, 203, + 0x7fff, 0x7fff, 0x7fff, + 0xffff, 0xffff, 0xffff ]), + gl.STATIC_DRAW); + } else { + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( + [ 0, 1, 2, + 1, 2, 0, + 2, 0, 1, + 201, 202, 203, + 0x7fff, 0x7fff, 0x7fff, + 0xffff - 1, 0xffff - 1, 0xffff - 1 ]), + gl.STATIC_DRAW); + } + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 9, type: 'gl.UNSIGNED_SHORT', offset: 0})); + + + // invalid operation with indices that would be valid with correct bindings + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 9, type: 'gl.UNSIGNED_SHORT', offset: 1000})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 12, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 15, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 18, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, indexValidationError, wtu.replaceParams(callTemplate, {count: 3, type: 'gl.UNSIGNED_SHORT', offset: 2*15})); + + // 0xffffffff needs to convert to a 'long' IDL argument as -1, as per + // WebIDL 4.1.7. JS ToInt32(0xffffffff) == -1. Thus INVALID_VALUE. + wtu.shouldGenerateGLError(gl, gl.INVALID_VALUE, wtu.replaceParams(callTemplate, {count: '0xffffffff', type: 'gl.UNSIGNED_SHORT', offset: 0})); + // offset is defined as GLintptr, which is long long in IDL (64-bit). + // 2^32 - 1 should not overflow, and only result in INVALID_OPERATION. + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 1, type: 'gl.UNSIGNED_SHORT', offset: '0xffffffff'})); + + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: '0x7fffffff', type: 'gl.UNSIGNED_SHORT', offset: 0})); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 0, type: 'gl.UNSIGNED_SHORT', offset: 0})); + + // invalid operation with offset that's not a multiple of the type size + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 1})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); + + // invalid operation if no buffer is bound to ELEMENT_ARRAY_BUFFER + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 0})); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + + debug(''); + debug('Test buffer setting attrib 0 to a buffer too small and disable it.'); + var smallVBO = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, smallVBO); + gl.bufferData(gl.ARRAY_BUFFER, 1, gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0x10); + gl.disableVertexAttribArray(0); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {count: 6, type: 'gl.UNSIGNED_SHORT', offset: 2})); +}; + +var runInstancedTest = function(callTemplate, gl, wtu, ext) { + setupProgram2(gl, wtu); + + // Initialize non-instanced attribute data. + // Enough for 9 vertices, so 3 triangles. + var vbo = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(9*3), gl.STATIC_DRAW); + + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + // Setup buffer for instanced attribute data. + var vbo2 = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo2); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(0); + gl.enableVertexAttribArray(1); + + debug('Test out-of-range instanced attributes'); + debug(''); + + debug('Test with an empty buffer for the instanced attribute'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 10000, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + + debug('Test with a buffer with 1 float for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(1), gl.STATIC_DRAW); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 10000, primcount: 0})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + + debug(''); + debug('Test with a buffer with 2 floats for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(2), gl.STATIC_DRAW); + debug('Divisor 1'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 1})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 2})); + debug('Divisor 3'); + ext.vertexAttribDivisorANGLE(1, 3); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 3})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 10000})); + + debug(''); + debug('Test with a buffer with 4 floats for the instanced attribute'); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(4), gl.STATIC_DRAW); + debug('Divisor 1'); + ext.vertexAttribDivisorANGLE(1, 1); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 2})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 3})); + debug('Divisor 2'); + ext.vertexAttribDivisorANGLE(1, 2); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 4})); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, wtu.replaceParams(callTemplate, {offset: 0, count: 9, primcount: 5})); +}; + +var runDrawArraysInstancedTest = function(callTemplate, gl, wtu, ext) { + runInstancedTest(callTemplate, gl, wtu, ext); +}; + +var runDrawElementsInstancedTest = function(callTemplate, gl, wtu, ext) { + var ebo = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array( + [ 0, 1, 2, + 5, 4, 3, + 6, 7, 8 ]), + gl.STATIC_DRAW); + callTemplate = wtu.replaceParams(callTemplate, {type: 'gl.UNSIGNED_BYTE', offset: '$(offset)', count: '$(count)', primcount: '$(primcount)'}); + runInstancedTest(callTemplate, gl, wtu, ext); +}; + +return { + runDrawArraysTest: runDrawArraysTest, + runDrawArraysInstancedTest: runDrawArraysInstancedTest, + runDrawElementsTest: runDrawElementsTest, + runDrawElementsInstancedTest: runDrawElementsInstancedTest +}; + +})(); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js new file mode 100644 index 0000000000..5de4dc88d8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ovr_multiview2_util.js @@ -0,0 +1,263 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +function createTextureWithNearestFiltering(target) +{ + let texture = gl.createTexture(); + gl.bindTexture(target, texture); + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +// Write a transformation matrix to elements of floatArray starting from index. +// The matrix transforms a unit square (-1 to 1) to a rectangle with the width scaleX and the left edge at offsetX. +function setupTranslateAndScaleXMatrix(floatArray, index, scaleX, offsetX) +{ + // x position is transformed according to this equation: scaleX * x0 + translateX = offsetX + // By substituting x0 with -1 (unit square x value for the left edge), we get the following: + let translateX = offsetX + scaleX; + + floatArray[index] = scaleX; + floatArray[index + 1] = 0.0; + floatArray[index + 2] = 0.0; + floatArray[index + 3] = 0.0; + + floatArray[index + 4] = 0.0; + floatArray[index + 5] = 1.0; + floatArray[index + 6] = 0.0; + floatArray[index + 7] = 0.0; + + floatArray[index + 8] = 0.0; + floatArray[index + 9] = 0.0; + floatArray[index + 10] = 1.0; + floatArray[index + 11] = 0.0; + + floatArray[index + 12] = translateX; + floatArray[index + 13] = 0.0; + floatArray[index + 14] = 0.0; + floatArray[index + 15] = 1.0; +} + +// Check the currently bound read framebuffer with dimensions x . +// The framebuffer should be divided into equally wide vertical strips, with the one indicated by +// colored with . The rest of the framebuffer should be colored transparent black. +// A two pixel wide region at each edge of the colored region is left unchecked to allow for some tolerance for rasterization. +function checkVerticalStrip(width, height, strips, coloredStripIndex, expectedStripColor, framebufferDescription) +{ + let colorRegionLeftEdge = (width / strips) * coloredStripIndex; + let colorRegionRightEdge = (width / strips) * (coloredStripIndex + 1); + if (coloredStripIndex > 0) { + wtu.checkCanvasRect(gl, 0, 0, colorRegionLeftEdge - 1, height, [0, 0, 0, 0], 'the left edge of ' + framebufferDescription + ' should be untouched'); + } + if (coloredStripIndex < strips - 1) { + wtu.checkCanvasRect(gl, colorRegionRightEdge + 1, 0, width - colorRegionRightEdge - 1, height, [0, 0, 0, 0], 'the right edge of ' + framebufferDescription + ' should be untouched'); + } + wtu.checkCanvasRect(gl, colorRegionLeftEdge + 1, 0, colorRegionRightEdge - colorRegionLeftEdge - 2, height, expectedStripColor, 'a thin strip in ' + framebufferDescription + ' should be colored ' + expectedStripColor); +} + +function getMultiviewPassthroughVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + + 'void main() {', + ' gl_Position = a_position;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +// This shader splits the viewport into equally sized vertical strips. +// The input quad defined by "a_position" is transformed to fill a different +// strip in each view. +function getMultiviewOffsetVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + + 'void main() {', + ' vec4 pos = a_position;', + " // Transform the quad to a thin vertical strip that's offset along the x axis according to the view id.", + ' pos.x = (pos.x * 0.5 + 0.5 + float(gl_ViewID_OVR)) * 2.0 / $(num_views).0 - 1.0;', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +// This shader transforms the incoming "a_position" with transforms for each +// view given in the uniform array "transform". +function getMultiviewRealisticUseCaseVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'uniform mat4 transform[$(num_views)];', + 'in vec4 a_position;', + + 'void main() {', + " // Transform the quad with the transformation matrix chosen according to gl_ViewID_OVR.", + ' vec4 pos = transform[gl_ViewID_OVR] * a_position;', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewColorFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'out vec4 my_FragColor;', + + 'void main() {', + ' uint mask = gl_ViewID_OVR + 1u;', + ' my_FragColor = vec4(((mask & 4u) != 0u) ? 1.0 : 0.0,', + ' ((mask & 2u) != 0u) ? 1.0 : 0.0,', + ' ((mask & 1u) != 0u) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewColorFragmentShaderForDrawBuffers(drawBuffers) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'out vec4 my_FragColor[$(drawBuffers)];', + + 'void main() {', + ' uint mask;']; + + for (let i = 0; i < drawBuffers; ++i) { + shaderCode.push(wtu.replaceParams(' mask = gl_ViewID_OVR + $(i)u;', {'i': i + 1})); + shaderCode.push(wtu.replaceParams(' my_FragColor[$(i)] = vec4(((mask & 4u) != 0u) ? 1.0 : 0.0,', {'i': i})); + shaderCode.push(' ((mask & 2u) != 0u) ? 1.0 : 0.0,'); + shaderCode.push(' ((mask & 1u) != 0u) ? 1.0 : 0.0,'); + shaderCode.push(' 1.0);'); + } + shaderCode.push('}'); + shaderCode = shaderCode.join('\n'); + return wtu.replaceParams(shaderCode, {'drawBuffers' : drawBuffers}); +} + +function getMultiviewVaryingVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'out float testVarying;', + + 'void main() {', + ' gl_Position = a_position;', + ' testVarying = float(gl_ViewID_OVR);', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewVaryingFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'in float testVarying;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' int mask = int(testVarying + 0.1) + 1;', + ' my_FragColor = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewFlatVaryingVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'flat out int testVarying;', + + 'void main() {', + ' gl_Position = a_position;', + ' testVarying = int(gl_ViewID_OVR);', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getMultiviewFlatVaryingFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'flat in int testVarying;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' int mask = testVarying + 1;', + ' my_FragColor = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + '}'].join('\n'); +} + +function getMultiviewInstancedVertexShader(views) { + let shaderCode = ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + + 'layout(num_views = $(num_views)) in;', + + 'in vec4 a_position;', + 'out vec4 color;', + + 'void main() {', + ' vec4 pos = a_position;', + " // Transform the quad to a thin vertical strip that's offset along the x axis according to the view id and instance id.", + ' pos.x = (pos.x * 0.5 + 0.5 + float(gl_ViewID_OVR) + float(gl_InstanceID)) * 2.0 / ($(num_views).0 * 2.0) - 1.0;', + ' int mask = gl_InstanceID + 1;', + ' color = vec4(((mask & 4) != 0) ? 1.0 : 0.0,', + ' ((mask & 2) != 0) ? 1.0 : 0.0,', + ' ((mask & 1) != 0) ? 1.0 : 0.0,', + ' 1.0);', + ' gl_Position = pos;', + '}'].join('\n'); + return wtu.replaceParams(shaderCode, {'num_views': views}); +} + +function getInstanceColorFragmentShader() { + return ['#version 300 es', + '#extension GL_OVR_multiview2 : require', + 'precision highp float;', + + 'in vec4 color;', + 'out vec4 my_FragColor;', + + 'void main() {', + ' my_FragColor = color;', + '}'].join('\n'); +} + +function getExpectedColor(view) { + var mask = (view + 1); + return [(mask & 4) ? 255 : 0, (mask & 2) ? 255 : 0, (mask & 1) ? 255 : 0, 255]; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js b/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js new file mode 100644 index 0000000000..561c1385af --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/shader-with-non-reserved-words.js @@ -0,0 +1,664 @@ +/* +Copyright (c) 2022 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +async function testNonReservedWords(part, numParts) { + + description(`test shaders using reserved words as identifiers compile ${part} of ${numParts}`); + + const DXWords = [ + "Buffer", + "double", + "uint", + "half", + "dword", + "string", + "texture", + "pixelshader", + "vertexshader", + "switch", + "min16float", + "min10float", + "min16int", + "min12int", + "min16uint", + "vector", + "matrix", + "float2", + "float3", + "float4", + "float1x1", + "float1x2", + "float1x3", + "float1x4", + "float2x1", + "float2x2", + "float2x3", + "float2x4", + "float3x1", + "float3x2", + "float3x3", + "float3x4", + "float4x1", + "float4x2", + "float4x3", + "float4x4", + "int1x1", + "int1x2", + "int1x3", + "int1x4", + "int2x1", + "int2x2", + "int2x3", + "int2x4", + "int3x1", + "int3x2", + "int3x3", + "int3x4", + "int4x1", + "int4x2", + "int4x3", + "int4x4", + "double1x1", + "double1x2", + "double1x3", + "double1x4", + "double2x1", + "double2x2", + "double2x3", + "double2x4", + "double3x1", + "double3x2", + "double3x3", + "double3x4", + "double4x1", + "double4x2", + "double4x3", + "double4x4", + "abort", + "abs", + "acos", + "all", + "AllMemoryBarrier", + "AllMemoryBarrierWithGroupSync", + "any", + "asdouble", + "asfloat", + "asin", + "asint", + "asint", + "asuint", + "asuint", + "atan", + "atan2", + "ceil", + "clamp", + "clip", + "cos", + "cosh", + "countbits", + "cross", + "D3DCOLORtoUBYTE4", + "ddx", + "ddx_coarse", + "ddx_fine", + "ddy", + "ddy_coarse", + "ddy_fine", + "degrees", + "determinant", + "DeviceMemoryBarrier", + "DeviceMemoryBarrierWithGroupSync", + "distance", + "dot", + "dst", + "errorf", + "EvaluateAttributeAtCentroid", + "EvaluateAttributeAtSample", + "EvaluateAttributeSnapped", + "exp", + "exp2", + "f16tof32", + "f32tof16", + "faceforward", + "firstbithigh", + "firstbitlow", + "floor", + "fma", + "fmod", + "frac", + "frexp", + "fwidth", + "GetRenderTargetSampleCount", + "GetRenderTargetSamplePosition", + "GroupMemoryBarrier", + "GroupMemoryBarrierWithGroupSync", + "InterlockedAdd", + "InterlockedAnd", + "InterlockedCompareExchange", + "InterlockedCompareStore", + "InterlockedExchange", + "InterlockedMax", + "InterlockedMin", + "InterlockedOr", + "InterlockedXor", + "isfinite", + "isinf", + "isnan", + "ldexp", + "length", + "lerp", + "lit", + "log", + "log10", + "log2", + "mad", + "max", + "min", + "modf", + "msad4", + "mul", + "noise", + "normalize", + "pow", + "printf", + "Process2DQuadTessFactorsAvg", + "Process2DQuadTessFactorsMax", + "Process2DQuadTessFactorsMin", + "ProcessIsolineTessFactors", + "ProcessQuadTessFactorsAvg", + "ProcessQuadTessFactorsMax", + "ProcessQuadTessFactorsMin", + "ProcessTriTessFactorsAvg", + "ProcessTriTessFactorsMax", + "ProcessTriTessFactorsMin", + "radians", + "rcp", + "reflect", + "refract", + "reversebits", + "round", + "rsqrt", + "saturate", + "sign", + "sin", + "sincos", + "sinh", + "smoothstep", + "sqrt", + "step", + "tan", + "tanh", + "tex1D", + "tex1D", + "tex1Dbias", + "tex1Dgrad", + "tex1Dlod", + "tex1Dproj", + "tex2D", + "tex2D", + "tex2Dbias", + "tex2Dgrad", + "tex2Dlod", + "tex2Dproj", + "tex3D", + "tex3D", + "tex3Dbias", + "tex3Dgrad", + "tex3Dlod", + "tex3Dproj", + "texCUBE", + "texCUBE", + "texCUBEbias", + "texCUBEgrad", + "texCUBElod", + "texCUBEproj", + "transpose", + "trunc" + ]; + + const GLSL_4_20_11_words = [ + "attribute", + "const", + "uniform", + "varying", + "coherent", + "volatile", + "restrict", + "readonly", + "writeonly", + "atomic_uint", + "layout", + "centroid", + "flat", + "smooth", + "noperspective", + "patch", + "sample", + "break", + "continue", + "do", + "for", + "while", + "switch", + "case", + "default", + "if", + "else", + "subroutine", + "in", + "out", + "inout", + "float", + "double", + "int", + "void", + "bool", + "true", + "false", + "invariant", + "discard", + "return", + "mat2", + "mat3", + "mat4", + "dmat2", + "dmat3", + "dmat4", + "mat2x2", + "mat2x3", + "mat2x4", + "dmat2x2", + "dmat2x3", + "dmat2x4", + "mat3x2", + "mat3x3", + "mat3x4", + "dmat3x2", + "dmat3x3", + "dmat3x4", + "mat4x2", + "mat4x3", + "mat4x4", + "dmat4x2", + "dmat4x3", + "dmat4x4", + "vec2", + "vec3", + "vec4", + "ivec2", + "ivec3", + "ivec4", + "bvec2", + "bvec3", + "bvec4", + "dvec2", + "dvec3", + "dvec4", + "uint", + "uvec2", + "uvec3", + "uvec4", + "lowp", + "mediump", + "highp", + "precision", + "sampler1D", + "sampler2D", + "sampler3D", + "samplerCube", + "sampler1DShadow", + "sampler2DShadow", + "samplerCubeShadow", + "sampler1DArray", + "sampler2DArray", + "sampler1DArrayShadow", + "sampler2DArrayShadow", + "isampler1D", + "isampler2D", + "isampler3D", + "isamplerCube", + "isampler1DArray", + "isampler2DArray", + "usampler1D", + "usampler2D", + "usampler3D", + "usamplerCube", + "usampler1DArray", + "usampler2DArray", + "sampler2DRect", + "sampler2DRectShadow", + "isampler2DRect", + "usampler2DRect", + "samplerBuffer", + "isamplerBuffer", + "usamplerBuffer", + "sampler2DMS", + "isampler2DMS", + "usampler2DMS", + "sampler2DMSArray", + "isampler2DMSArray", + "usampler2DMSArray", + "samplerCubeArray", + "samplerCubeArrayShadow", + "isamplerCubeArray", + "usamplerCubeArray", + "image1D", + "iimage1D", + "uimage1D", + "image2D", + "iimage2D", + "uimage2D", + "image3D", + "iimage3D", + "uimage3D", + "image2DRect", + "iimage2DRect", + "uimage2DRect", + "imageCube", + "iimageCube", + "uimageCube", + "imageBuffer", + "iimageBuffer", + "uimageBuffer", + "image1DArray", + "iimage1DArray", + "uimage1DArray", + "image2DArray", + "iimage2DArray", + "uimage2DArray", + "imageCubeArray", + "iimageCubeArray", + "uimageCubeArray", + "image2DMS", + "iimage2DMS", + "uimage2DMS", + "image2DMSArray", + "iimage2DMSArray", + "uimage2DMSArray", + "struct" + ]; + + const GLSL_4_20_11_future_words = [ + "common", + "partition", + "active", + "asm", + "class", + "union", + "enum", + "typedef", + "template", + "this", + "packed", + "resource", + "goto", + "inline", + "noinline", + "public", + "static", + "extern", + "external", + "interface", + "long", + "short", + "half", + "fixed", + "unsigned", + "superp", + "input", + "output", + "hvec2", + "hvec3", + "hvec4", + "fvec2", + "fvec3", + "fvec4", + "sampler3DRect", + "filter", + "sizeof", + "cast", + "namespace", + "using", + "row_major" + ]; + + const GLSL_1_0_17_words = [ + "attribute", + "const", + "uniform", + "varying", + "break", + "continue", + "do", + "for", + "while", + "if", + "else", + "in", + "out", + "inout", + "float", + "int", + "void", + "bool", + "true", + "false", + "lowp", + "mediump", + "highp", + "precision", + "invariant", + "discard", + "return", + "mat2", + "mat3", + "mat4", + "vec2", + "vec3", + "vec4", + "ivec2", + "ivec3", + "ivec4", + "bvec2", + "bvec3", + "bvec4", + "sampler2D", + "samplerCube", + "struct" + ] + + const GLSL_1_0_17_FutureWords = [ + "asm", + "class", + "union", + "enum", + "typedef", + "template", + "this", + "packed", + "goto", + "switch", + "default", + "inline", + "noinline", + "volatile", + "public", + "static", + "extern", + "external", + "interface", + "flat", + "long", + "short", + "double", + "half", + "fixed", + "unsigned", + "superp", + "input", + "output", + "hvec2", + "hvec3", + "hvec4", + "dvec2", + "dvec3", + "dvec4", + "fvec2", + "fvec3", + "fvec4", + "sampler1D", + "sampler3D", + "sampler1DShadow", + "sampler2DShadow", + "sampler2DRect", + "sampler3DRect", + "sampler2DRectShadow", + "sizeof", + "cast", + "namespace", + "using" + ]; + + const allBadWords = [ + ...DXWords, + ...GLSL_4_20_11_words, + ...GLSL_4_20_11_future_words, + ]; + const numWordsPerPart = Math.ceil(allBadWords.length / numParts); + const firstWordNdx = numWordsPerPart * (part - 1); + const badWords = allBadWords.slice(firstWordNdx, firstWordNdx + numWordsPerPart); + debug(`running tests for words ${firstWordNdx} to ${firstWordNdx + badWords.length - 1} of ${allBadWords.length}`); + + const shaders = { + vertexShader0: ` +struct $replaceMe { + vec4 $replaceMe; +}; +struct Foo { + $replaceMe $replaceMe; +}; +attribute vec4 position; +void main() +{ + Foo f; + f.$replaceMe.$replaceMe = position; + gl_Position = f.$replaceMe.$replaceMe; +} +`, + fragmentShader0: ` +precision mediump float; +vec4 $replaceMe() { + return vec4(0,1,0,1); +} +void main() +{ + gl_FragColor = $replaceMe(); +} +`, + vertexShader1: ` +attribute vec4 $replaceMe; +void main() +{ + gl_Position = $replaceMe; +} +`, + fragmentShader1: ` +precision mediump float; +vec4 foo(vec4 $replaceMe) { + return $replaceMe; +} +void main() +{ + gl_FragColor = foo(vec4(1,0,1,1)); +} +`, + vertexShader2: ` +varying vec4 $replaceMe; +attribute vec4 position; +void main() +{ + gl_Position = position; + $replaceMe = position; +} +`, + fragmentShader2: ` +precision mediump float; +varying vec4 $replaceMe; +void main() +{ + gl_FragColor = $replaceMe; +} +`, + vertexShader3: ` +attribute vec4 position; +void main() +{ + gl_Position = position; +} +`, + fragmentShader3: ` +precision mediump float; +uniform vec4 $replaceMe; +void main() +{ + gl_FragColor = $replaceMe; +} +`, + }; + + const wtu = WebGLTestUtils; + const gl = wtu.create3DContext(); + const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + + const reservedWords = new Set([ + ...GLSL_1_0_17_words, + ...GLSL_1_0_17_FutureWords, + ]); + + const checkedWords = new Set(); + + const src = []; + for (let ii = 0; ii < 4; ++ii) { + const vSrc = shaders[`vertexShader${ii}`]; + const fSrc = shaders[`fragmentShader${ii}`]; + src.push({vSrc: vSrc, fSrc: fSrc}); + } + + for (const badWord of badWords) { + testWord(badWord); + await wait(); + } + finishTest(); + + function testWord(word) { + if (reservedWords.has(word) || checkedWords.has(word)) { + return; + } + checkedWords.add(word); + debug(""); + debug(`testing: ${word}`); + + for (let ii = 0; ii < src.length; ++ii) { + const vs = src[ii].vSrc.replace(/\$replaceMe/g, word); + const fs = src[ii].fSrc.replace(/\$replaceMe/g, word); + + let success = true; + const program = wtu.loadProgram(gl, vs, fs, function(msg) { + debug(msg); + success = false; + }, true); + if (success) { + testPassed(`shader with: '${word}' compiled`); + } else { + testFailed(`shader with: '${word}' failed to compile`); + } + if (program) { + gl.deleteProgram(program); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no GL errors"); + } + } +} \ No newline at end of file diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js new file mode 100644 index 0000000000..792f832451 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas-sub-rectangle.js @@ -0,0 +1,292 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var realRedColor = [255, 0, 0]; + var realGreenColor = [0, 255, 0]; + var realBlueColor = [0, 0, 255]; + var realCyanColor = [0, 255, 255]; + var redColor = realRedColor; + var greenColor = realGreenColor; + var blueColor = realBlueColor; + var cyanColor = realCyanColor; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking a sub-rectangle of a canvas (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + + // The sub-rectangle tests only apply to WebGL 2.0 for the + // time being, though the tests for the WebGL 1.0 + // format/internal format/type combinations are generated into + // conformance/textures/. + if (wtu.getDefault3DContextVersion() < 2) { + debug('Test only applies to WebGL 2.0'); + finishTest(); + return; + } + + gl = wtu.create3DContext("example", { preserveDrawingBuffer: true }); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.createElement('canvas'); + runTest(canvas2d, setupSourceCanvas2D, '2D-rendered canvas'); + + var canvasWebGL = document.createElement('canvas'); + runTest(canvasWebGL, setupSourceCanvasWebGL, 'WebGL-rendered canvas'); + + finishTest(); + } + + function fillStyle2D(ctx, color) { + ctx.fillStyle = 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')'; + } + + function setupSourceCanvas2D(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('2d'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + fillStyle2D(ctx, realRedColor); + ctx.fillRect(0, 0, halfWidth, halfHeight); + fillStyle2D(ctx, realGreenColor); + ctx.fillRect(halfWidth, 0, width - halfWidth, halfHeight); + fillStyle2D(ctx, realBlueColor); + ctx.fillRect(0, halfHeight, halfWidth, height - halfHeight); + fillStyle2D(ctx, realCyanColor); + ctx.fillRect(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + } + + function clearColorWebGL(ctx, color) { + ctx.clearColor(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + function setupSourceCanvasWebGL(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('webgl'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + + ctx.viewport(0, 0, width, height); + ctx.enable(ctx.SCISSOR_TEST); + // OpenGL origin is lower-left + ctx.scissor(0, 0, halfWidth, halfHeight); + clearColorWebGL(ctx, realBlueColor); + ctx.scissor(halfWidth, 0, width - halfWidth, halfHeight); + clearColorWebGL(ctx, realCyanColor); + ctx.scissor(0, halfHeight, halfWidth, height - halfHeight); + clearColorWebGL(ctx, realRedColor); + ctx.scissor(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + clearColorWebGL(ctx, realGreenColor); + } + + function runOneIteration(sourceDescription, useTexSubImage2D, flipY, + canvas, canvasSize, canvasSetupFunction, + sourceSubRectangle, expected, + bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + } + debug(''); + debug('Testing ' + sourceDescription + ' with ' + + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ', flipY=' + flipY + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + // Initialize the contents of the source canvas. + var width = canvasSize[0]; + var height = canvasSize[1]; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + canvas.width = width; + canvas.height = height; + canvasSetupFunction(canvas); + + // Upload the source canvas to the texture and draw it to a quad. + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // In this test, this is always specified. It's currently WebGL 2.0-specific. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + // Upload the image into the texture + var uploadWidth = sourceSubRectangle[2]; + var uploadHeight = sourceSubRectangle[3]; + for (var tt = 0; tt < targets.length; ++tt) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + uploadWidth, uploadHeight, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + uploadWidth, uploadHeight, + gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + uploadWidth, uploadHeight, 0, + gl[pixelFormat], gl[pixelType], canvas); + } + } + + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + + // The tests are constructed to upload a single solid color + // out of the canvas. + var outputCanvasWidth = gl.drawingBufferWidth; + var outputCanvasHeight = gl.drawingBufferHeight; + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + var msg = 'should be ' + expected; + wtu.checkCanvasRect(gl, 0, 0, outputCanvasWidth, outputCanvasHeight, expected, msg); + } + } + + function runTest(canvas, canvasSetupFunction, sourceDescription) + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D, program, canvas, canvasSetupFunction, sourceDescription); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_CUBE_MAP, program, canvas, canvasSetupFunction, sourceDescription); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + + function runTestOnBindingTarget(bindingTarget, program, canvas, canvasSetupFunction, sourceDescription) { + var cases = [ + // Small canvas cases. Expected that these won't be + // GPU-accelerated in most browsers' implementations. + { expected: redColor, flipY: false, size: [2, 2], subRect: [0, 0, 1, 1] }, + { expected: greenColor, flipY: false, size: [2, 2], subRect: [1, 0, 1, 1] }, + { expected: blueColor, flipY: false, size: [2, 2], subRect: [0, 1, 1, 1] }, + { expected: cyanColor, flipY: false, size: [2, 2], subRect: [1, 1, 1, 1] }, + { expected: redColor, flipY: true, size: [2, 2], subRect: [0, 1, 1, 1] }, + { expected: greenColor, flipY: true, size: [2, 2], subRect: [1, 1, 1, 1] }, + { expected: blueColor, flipY: true, size: [2, 2], subRect: [0, 0, 1, 1] }, + { expected: cyanColor, flipY: true, size: [2, 2], subRect: [1, 0, 1, 1] }, + + // Larger canvas cases. Expected that these will be + // GPU-accelerated in most browsers' implementations. + // Changes will be gladly accepted to trigger more + // browsers' heuristics to accelerate these canvases. + { expected: redColor, flipY: false, size: [384, 384], subRect: [ 0, 0, 192, 192] }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192] }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [ 0, 192, 192, 192] }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 192, 192, 192] }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [ 0, 0, 192, 192] }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192] }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [ 0, 192, 192, 192] }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 192, 192, 192] }, + + ]; + + for (var i in cases) { + runOneIteration(sourceDescription, false, cases[i].flipY, + canvas, cases[i].size, canvasSetupFunction, + cases[i].subRect, + cases[i].expected, bindingTarget, program); + runOneIteration(sourceDescription, true, cases[i].flipY, + canvas, cases[i].size, canvasSetupFunction, + cases[i].subRect, + cases[i].expected, bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js new file mode 100644 index 0000000000..a96eeb9de0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-canvas.js @@ -0,0 +1,468 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var whiteColor = [255, 255, 255, 255]; + var redColor = [255, 0, 0, 255]; + var greenColor = [0, 255, 0, 255]; + var semiTransparentRedColor = [127, 0, 0, 127]; + var semiTransparentGreenColor = [0, 127, 0, 127]; + var repeatCount; + + function replicateRedChannel(color) + { + color[1] = color[0]; + color[2] = color[0]; + } + + function zapColorChannels(color) + { + color[0] = 0; + color[1] = 0; + color[2] = 0; + } + + function setAlphaChannelTo1(color) + { + color[3] = 255; + } + + function replicateAllRedChannels() + { + replicateRedChannel(redColor); + replicateRedChannel(semiTransparentRedColor); + replicateRedChannel(greenColor); + replicateRedChannel(semiTransparentGreenColor); + } + + function setAllAlphaChannelsTo1() + { + setAlphaChannelTo1(redColor); + setAlphaChannelTo1(semiTransparentRedColor); + setAlphaChannelTo1(greenColor); + setAlphaChannelTo1(semiTransparentGreenColor); + } + + function repeatCountForTextureFormat(internalFormat, pixelFormat, pixelType) + { + // There were bugs in early WebGL 1.0 implementations when repeatedly uploading canvas + // elements into textures. In response, this test was changed into a regression test by + // repeating all of the cases multiple times. Unfortunately, this means that adding a new + // case above significantly increases the run time of the test suite. The problem is made + // even worse by the addition of many more texture formats in WebGL 2.0. + // + // Doing repeated runs with just a couple of WebGL 1.0's supported texture formats acts as a + // sufficient regression test for the old bugs. For this reason the test has been changed to + // only repeat for those texture formats. + if ((internalFormat == 'RGBA' && pixelFormat == 'RGBA' && pixelType == 'UNSIGNED_BYTE') || + (internalFormat == 'RGB' && pixelFormat == 'RGB' && pixelType == 'UNSIGNED_BYTE')) { + return 4; + } + + return 1; + } + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + repeatCount = repeatCountForTextureFormat(internalFormat, pixelFormat, pixelType); + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + // Zap green and blue channels. + whiteColor[1] = 0; + whiteColor[2] = 0; + greenColor[1] = 0; + semiTransparentGreenColor[1] = 0; + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.RG: + case gl.RG_INTEGER: + // Zap blue channel. + whiteColor[2] = 0; + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.LUMINANCE: + // Replicate red channels. + replicateAllRedChannels(); + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + case gl.ALPHA: + // Red, green and blue channels are all 0.0. + zapColorChannels(redColor); + zapColorChannels(semiTransparentRedColor); + zapColorChannels(greenColor); + zapColorChannels(semiTransparentGreenColor); + zapColorChannels(whiteColor); + break; + case gl.LUMINANCE_ALPHA: + // Replicate red channels. + replicateAllRedChannels(); + break; + case gl.RGB: + case gl.RGB_INTEGER: + // Alpha channel is 1.0. + setAllAlphaChannelsTo1(); + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + semiTransparentRedColor = wtu.sRGBToLinear(semiTransparentRedColor); + semiTransparentGreenColor = wtu.sRGBToLinear(semiTransparentGreenColor); + break; + case gl.RGBA8UI: + // For int and uint textures, TexImageUtils outputs the maximum value (in this case, + // 255) for the alpha channel all the time because of differences in behavior when + // sampling integer textures with and without alpha channels. Since changing this + // behavior may have large impact across the test suite, leave it as is for now. + setAllAlphaChannelsTo1(); + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = "#ff0000"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function setCanvasToSemiTransparentRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.clearRect(0, 0, width, height); + ctx.fillStyle = "rgba(127, 0, 0, 0.5)"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "rgba(0, 127, 0, 0.5)"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function drawTextInCanvas(ctx, bindingTarget) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, width, height); + ctx.font = '20pt Arial'; + ctx.fillStyle = 'black'; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText("1234567890", width / 2, height / 4); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257SemiTransparent(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToSemiTransparentRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMinSemiTransparent(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToSemiTransparentRedGreen(ctx); + } + + function runOneIteration(canvas, useTexSubImage2D, flipY, semiTransparent, program, bindingTarget, opt_texture, opt_fontTest) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + + ' source object type: ' + objType + + (opt_fontTest ? " with fonts" : " with" + (semiTransparent ? " semi-transparent" : "") + " red-green")); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + // Initialize the texture to black first + if (useTexSubImage2D) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], canvas.width, canvas.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], canvas); + } + } + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + var top = flipY ? 0 : (height - halfHeight); + var bottom = flipY ? (height - halfHeight) : 0; + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + if (opt_fontTest) { + // check half is a solid color. + wtu.checkCanvasRect( + gl, 0, top, width, halfHeight, + whiteColor, + "should be white"); + // check other half is not a solid color. + wtu.checkCanvasRectColor( + gl, 0, bottom, width, halfHeight, + whiteColor, 0, + function() { + testFailed("font missing"); + }, + function() { + testPassed("font rendered"); + }, + debug); + } else { + var localRed = semiTransparent ? semiTransparentRedColor : redColor; + var localGreen = semiTransparent ? semiTransparentGreenColor : greenColor; + + // Allow a tolerance for premultiplication/unmultiplication, especially for texture + // formats with lower bit depths. + var tolerance = 0; + if (semiTransparent) { + tolerance = 3; + if (pixelType == 'UNSIGNED_SHORT_5_6_5' || internalFormat == 'RGB565') { + tolerance = 6; + } else if (pixelType == 'UNSIGNED_SHORT_4_4_4_4' || internalFormat == 'RGBA4') { + tolerance = 9; + } else if (pixelType == 'UNSIGNED_SHORT_5_5_5_1' || internalFormat == 'RGB5_A1') { + // Semi-transparent values are allowed to convert to either 1 or 0 for this + // single-bit alpha format per OpenGL ES 3.0.5 section 2.1.6.2, "Conversion + // from Floating-Point to Normalized Fixed-Point". Ignore alpha for these + // tests. + tolerance = 6; + localRed = localRed.slice(0, 3); + localGreen = localGreen.slice(0, 3); + } else if (internalFormat == 'RGB10_A2') { + // The alpha channel is too low-resolution for any meaningful comparisons. + localRed = localRed.slice(0, 3); + localGreen = localGreen.slice(0, 3); + } + } + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, localRed, + "shouldBe " + localRed, tolerance); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, localGreen, + "shouldBe " + localGreen, tolerance); + } + + if (!useTexSubImage2D && pixelFormat == "RGBA") { + if (pixelType == "FLOAT") { + // Attempt to set a pixel in the texture to ensure the texture was + // actually created with floats. Regression test for http://crbug.com/484968 + var pixels = new Float32Array([1000.0, 1000.0, 1000.0, 1000.0]); + gl.texSubImage2D(targets[tt], 0, 0, 0, 1, 1, gl[pixelFormat], gl[pixelType], pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture should be backed by floats"); + } else if (pixelType == "HALF_FLOAT_OES" || pixelType == "HALF_FLOAT") { + // Attempt to set a pixel in the texture to ensure the texture was + // actually created with half-floats. Regression test for http://crbug.com/484968 + var halfFloatTenK = 0x70E2; // Half float 10000 + var pixels = new Uint16Array([halfFloatTenK, halfFloatTenK, halfFloatTenK, halfFloatTenK]); + gl.texSubImage2D(targets[tt], 0, 0, 0, 1, 1, gl[pixelFormat], gl[pixelType], pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture should be backed by half-floats"); + } + } + } + + if (false) { + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + function runTest() + { + var canvas = document.createElement('canvas'); + + var cases = [ + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasToMin }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasToMinSemiTransparent }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasTo257x257 }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: canvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasTo257x257SemiTransparent }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: true, font: false }, + ]; + + // The font tests don't work with ALPHA-only textures since they draw to the color channels. + if (internalFormat != 'ALPHA') { + cases = cases.concat([ + { canvas: canvas, sub: false, flipY: true, semiTransparent: false, font: true, init: drawTextInCanvas }, + { canvas: canvas, sub: false, flipY: false, semiTransparent: false, font: true }, + { canvas: canvas, sub: true, flipY: true, semiTransparent: false, font: true }, + { canvas: canvas, sub: true, flipY: false, semiTransparent: false, font: true }, + ]); + } + + if (window.OffscreenCanvas) { + var offscreenCanvas = new OffscreenCanvas(1, 1); + cases = cases.concat([ + { canvas: offscreenCanvas, sub: false, flipY: true, semiTransparent: false, font: false, init: setCanvasToMin }, + { canvas: offscreenCanvas, sub: false, flipY: false, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: true, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: false, semiTransparent: false, font: false }, + { canvas: offscreenCanvas, sub: false, flipY: true, semiTransparent: true, font: false, init: setCanvasToMinSemiTransparent }, + { canvas: offscreenCanvas, sub: false, flipY: false, semiTransparent: true, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: true, semiTransparent: true, font: false }, + { canvas: offscreenCanvas, sub: true, flipY: false, semiTransparent: true, font: false }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = repeatCount; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + var imageDataBefore = null; + if (c.init) { + c.init(c.canvas.getContext('2d'), bindingTarget); + } + texture = runOneIteration(c.canvas, c.sub, c.flipY, c.semiTransparent, program, bindingTarget, texture, c.font); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + // While we are working with Canvases, it's really unlikely that + // waiting for composition will change anything here, and it's much + // slower, so just dispatchPromise. If we want to test with composites, + // we should test a more narrow subset of tests. + wtu.dispatchPromise(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + runTexImageTest(gl.TEXTURE_CUBE_MAP).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js new file mode 100644 index 0000000000..f434b9834c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-blob.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + async function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from a Blob (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + debug('*** Running tests against red-green-semi-transparent.png ***'); + let response = await fetch(resourcePath + "red-green-semi-transparent.png"); + let blob = await response.blob(); + await runImageBitmapTest(blob, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + debug('*** Running tests against red-green-128x128-linear-profile.jpg ***'); + response = await fetch(resourcePath + "red-green-128x128-linear-profile.jpg"); + blob = await response.blob(); + // This test requires a huge tolerance because browsers - at least currently - vary + // widely in the colorspace conversion results for this image. + let tolerance = 120; + await runImageBitmapTest(blob, 1.0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false, tolerance); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js new file mode 100644 index 0000000000..08ee96c0a1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-canvas.js @@ -0,0 +1,74 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLCanvasElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var testCanvas = document.createElement('canvas'); + var ctx = testCanvas.getContext("2d"); + setCanvasToMin(ctx); + runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + setCanvasTo257x257(ctx); + return runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + }).then(() => { + finishTest(); + }); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "rgba(255, 0, 0, 1)"; + ctx.fillRect(0, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fillRect(halfWidth, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 1)"; + ctx.fillRect(0, halfHeight, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 0.5)"; + ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight); + } + + function setCanvasToMin(ctx) { + ctx.canvas.width = 2; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257(ctx) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js new file mode 100644 index 0000000000..09821d8b3f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-bitmap.js @@ -0,0 +1,56 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an ImageBitmap (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + createImageBitmap(imageData, {imageOrientation: "none", premultiplyAlpha: "none"}) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + return createImageBitmap(imageData); + }).then( bitmap => { + return runImageBitmapTest(bitmap, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + }, () => { + testFailed("createImageBitmap(imageData) should succeed."); + }).then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js new file mode 100644 index 0000000000..dd24721fe4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image-data.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + runImageBitmapTest(imageData, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js new file mode 100644 index 0000000000..1e4b18129e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-image.js @@ -0,0 +1,46 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLImageElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var image = new Image(); + image.onload = function() { + bufferedLogToConsole("Source image has been loaded"); + runImageBitmapTest(image, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false) + .then(() => { + finishTest(); + }); + } + image.src = resourcePath + "red-green-semi-transparent.png"; + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js new file mode 100644 index 0000000000..14cf4628be --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js @@ -0,0 +1,83 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageBitmap created from an HTMLVideoElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + finishTest(); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, async function() { + await runImageBitmapTest(video, 1, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, false); + runNextVideo(); + }); + } + runNextVideo(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js new file mode 100644 index 0000000000..8486b0b659 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-data.js @@ -0,0 +1,240 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imageData = null; + var blackColor = [0, 0, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.getElementById("texcanvas"); + var context2d = canvas2d.getContext("2d"); + imageData = context2d.createImageData(2, 2); + var data = imageData.data; + data[0] = 255; + data[1] = 0; + data[2] = 0; + data[3] = 255; + data[4] = 255; + data[5] = 0; + data[6] = 0; + data[7] = 0; + data[8] = 0; + data[9] = 255; + data[10] = 0; + data[11] = 255; + data[12] = 0; + data[13] = 255; + data[14] = 0; + data[15] = 0; + + runTest(); + } + + function runOneIteration(useTexSubImage2D, flipY, premultiplyAlpha, + sourceSubRectangle, expected, + bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + } + debug(''); + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' and premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], imageData); + } + } else { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], imageData.width, imageData.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], imageData); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + + var top = 0; + var bottom = height - halfHeight; + var left = 0; + var right = width - halfWidth; + + var tl, tr, bl, br; + if (expected.length == 1) { + tl = tr = bl = br = expected[0]; + } else { + tl = expected[0]; + tr = expected[1]; + bl = expected[2]; + br = expected[3]; + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + wtu.checkCanvasRect(gl, left, top, halfWidth, halfHeight, tl, "shouldBe " + tl); + wtu.checkCanvasRect(gl, right, top, halfWidth, halfHeight, tr, "shouldBe " + tr); + wtu.checkCanvasRect(gl, left, bottom, halfWidth, halfHeight, bl, "shouldBe " + bl); + wtu.checkCanvasRect(gl, right, bottom, halfWidth, halfHeight, br, "shouldBe " + br); + } + } + + function runTest() + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D, program); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_CUBE_MAP, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(bindingTarget, program) { + var k = blackColor; + var r = redColor; + var g = greenColor; + var cases = [ + { expected: [r, r, g, g], flipY: false, premultiplyAlpha: false, sub: false }, + { expected: [r, r, g, g], flipY: false, premultiplyAlpha: false, sub: true }, + { expected: [r, k, g, k], flipY: false, premultiplyAlpha: true, sub: false }, + { expected: [r, k, g, k], flipY: false, premultiplyAlpha: true, sub: true }, + { expected: [g, g, r, r], flipY: true, premultiplyAlpha: false, sub: false }, + { expected: [g, g, r, r], flipY: true, premultiplyAlpha: false, sub: true }, + { expected: [g, k, r, k], flipY: true, premultiplyAlpha: true, sub: false }, + { expected: [g, k, r, k], flipY: true, premultiplyAlpha: true, sub: true }, + ]; + + if (wtu.getDefault3DContextVersion() > 1) { + var morecases = []; + // Make 2 copies of the original case: top left and bottom right 1x1 rectangles + for (var i = 0; i < cases.length; i++) { + for (var subX = 0; subX <= 1; subX++) { + var subY = subX == 0 ? 1 : 0; + // shallow-copy cases[i] into newcase + var newcase = Object.assign({}, cases[i]); + newcase.expected = [cases[i].expected[subY * 2 + subX]]; + newcase.sourceSubRectangle = [subX, subY, 1, 1]; + morecases.push(newcase); + } + } + cases = cases.concat(morecases); + } + + for (var i in cases) { + runOneIteration(cases[i].sub, cases[i].flipY, cases[i].premultiplyAlpha, + cases[i].sourceSubRectangle, cases[i].expected, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js new file mode 100644 index 0000000000..f3802ba777 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image.js @@ -0,0 +1,257 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.png", runTest); + } + + function runOneIteration(image, useTexSubImage2D, flipY, topColor, bottomColor, + sourceSubRectangle, bindingTarget, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with ' + image.width + 'x' + image.height + ' flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], image); + } + } else { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], image.width, image.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], image); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + } + + function runTestOnImage(image) { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + + if (wtu.getDefault3DContextVersion() > 1) { + cases = cases.concat([ + { sub: false, flipY: false, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: false, flipY: true, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: false, flipY: true, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: true, flipY: false, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: true, flipY: true, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 0, 1, 1] }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: greenColor, + sourceSubRectangle: [0, 1, 1, 1] }, + { sub: true, flipY: true, topColor: redColor, bottomColor: redColor, + sourceSubRectangle: [0, 1, 1, 1] }, + ]); + } + + var program = tiu.setupTexturedQuad(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + cases[i].sourceSubRectangle, + gl.TEXTURE_2D, program); + } + // cube map texture must be square. + if (image.width != image.height) + return; + // Skip sub-rectangle tests for cube map textures for the moment. + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + for (var i in cases) { + if (!cases[i].sourceSubRectangle) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + undefined, + gl.TEXTURE_CUBE_MAP, program); + } + } + } + + function runTest(image) + { + runTestOnImage(image); + + imgCanvas = document.createElement("canvas"); + imgCanvas.width = 2; + imgCanvas.height = 2; + var imgCtx = imgCanvas.getContext("2d"); + var imgData = imgCtx.createImageData(2, 2); + imgData.data[0] = redColor[0]; + imgData.data[1] = redColor[1]; + imgData.data[2] = redColor[2]; + imgData.data[3] = 255; + imgData.data[4] = redColor[0]; + imgData.data[5] = redColor[1]; + imgData.data[6] = redColor[2]; + imgData.data[7] = 255; + imgData.data[8] = greenColor[0]; + imgData.data[9] = greenColor[1]; + imgData.data[10] = greenColor[2]; + imgData.data[11] = 255; + imgData.data[12] = greenColor[0]; + imgData.data[13] = greenColor[1]; + imgData.data[14] = greenColor[2]; + imgData.data[15] = 255; + imgCtx.putImageData(imgData, 0, 0); + + // apparently Image is different than . + var newImage = new Image(); + newImage.onload = function() { + runTest2(newImage); + }; + newImage.onerror = function() { + testFailed("Creating image from canvas failed. Image src: " + this.src); + finishTest(); + }; + newImage.src = imgCanvas.toDataURL(); + } + + function runTest2(image) { + runTestOnImage(image); + + wtu.makeImageFromCanvas(imgCanvas, function() { + runTest3(this); + }); + } + + function runTest3(image) { + runTestOnImage(image); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js new file mode 100644 index 0000000000..5cc6a8283e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-svg-image.js @@ -0,0 +1,140 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking SVG image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.svg", runTest); + } + + function runOneIteration(image, useTexSubImage2D, flipY, topColor, bottomColor, bindingTarget, program) + { + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP')); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (useTexSubImage2D) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], image.width, image.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], image); + } + } + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + } + + function runTest(image) + { + var program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_2D, program); + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_CUBE_MAP, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(image, bindingTarget, program) { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js new file mode 100644 index 0000000000..6e8bcf96e9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js @@ -0,0 +1,291 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + // Test each format separately because many browsers implement each + // differently. Some might be GPU accelerated, some might not. Etc... + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage2D and texSubImage2D code paths taking video elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function runOneIteration(videoElement, unpackColorSpace, useTexSubImage2D, flipY, topColorName, bottomColorName, sourceSubRectangle, program, bindingTarget) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + unpackColorSpaceString = ''; + if (unpackColorSpace) { + unpackColorSpaceString = ' unpackColorSpace=' + unpackColorSpace; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString + unpackColorSpaceString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle target color space. + if (unpackColorSpace) { + gl.unpackColorSpace = unpackColorSpace; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the videoElement into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + // Initialize the texture to black first + if (useTexSubImage2D) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + } else { + // Initialize the texture to black first + if (useTexSubImage2D) { + var width = videoElement.videoWidth; + var height = videoElement.videoHeight; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + width = Math.max(width, height); + height = width; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + width, height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], videoElement); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + // Compute the test colors. This test only tests RGB (not A). + const topColor = wtu.colorAsSampledWithInternalFormat( + wtu.namedColorInColorSpace(topColorName, unpackColorSpace), + internalFormat).slice(0, 3); + const bottomColor = wtu.colorAsSampledWithInternalFormat( + wtu.namedColorInColorSpace(bottomColorName, unpackColorSpace), + internalFormat).slice(0, 3); + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + const tolerance = Math.max(6, tiu.tolerance(internalFormat, pixelFormat, pixelType)); + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + } + + function runTest(videoElement) + { + var cases = [ + { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Green' }, + { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Red' }, + { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Green' }, + { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Red' }, + ]; + + if (wtu.getDefault3DContextVersion() > 1) { + cases = cases.concat([ + { sub: false, flipY: false, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: false, flipY: true, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: true, flipY: false, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: true, flipY: true, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 16, 40, 32] }, + { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Green', + sourceSubRectangle: [20, 80, 40, 32] }, + { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Red', + sourceSubRectangle: [20, 80, 40, 32] }, + ]); + } + + cases = tiu.crossProductTestCasesWithUnpackColorSpaces( + cases, tiu.unpackColorSpacesToTest(gl)); + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // Cube map texture must be square but video is not square. + if (!cases[i].sub) { + break; + } + // Skip sub-rectangle tests for cube map textures for the moment. + if (cases[i].sourceSubRectangle) { + break; + } + } + runOneIteration(video, cases[i].unpackColorSpace, cases[i].sub, cases[i].flipY, + cases[i].topColor, + cases[i].bottomColor, + cases[i].sourceSubRectangle, + program, bindingTarget); + } + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + runTexImageTest(gl.TEXTURE_CUBE_MAP).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js new file mode 100644 index 0000000000..acb8768051 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-webgl-canvas.js @@ -0,0 +1,298 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +"use strict"; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0, 255]; + var greenColor = [0, 255, 0, 255]; + var repeatCount; + + function shouldRepeatTestForTextureFormat(internalFormat, pixelFormat, pixelType) + { + // There were bugs in early WebGL 1.0 implementations when repeatedly uploading canvas + // elements into textures. In response, this test was changed into a regression test by + // repeating all of the cases multiple times. Unfortunately, this means that adding a new + // case above significantly increases the run time of the test suite. The problem is made + // even worse by the addition of many more texture formats in WebGL 2.0. + // + // Doing repeated runs with just a couple of WebGL 1.0's supported texture formats acts as a + // sufficient regression test for the old bugs. For this reason the test has been changed to + // only repeat for those texture formats. + return ((internalFormat == 'RGBA' && pixelFormat == 'RGBA' && pixelType == 'UNSIGNED_BYTE') || + (internalFormat == 'RGB' && pixelFormat == 'RGB' && pixelType == 'UNSIGNED_BYTE')); + } + + async function init() + { + description('Verify texImage2D and texSubImage2D code paths taking webgl canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + return; + } + + repeatCount = (shouldRepeatTestForTextureFormat(internalFormat, pixelFormat, pixelType) ? 4 : 1); + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + await runTest(); + } + + function setCanvasToRedGreen(ctx, hasAlpha) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + + ctx.viewport(0, 0, width, height); + + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 0, width, halfHeight); + if (hasAlpha) { + ctx.clearColor(1.0, 0, 0, 1.0); + } else { + // The WebGL implementation is responsible for making all + // alpha values appear as though they were 1.0. + ctx.clearColor(1.0, 0, 0, 0.0); + } + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, halfHeight, width, height - halfHeight); + if (hasAlpha) { + ctx.clearColor(0.0, 1.0, 0, 1.0); + } else { + // The WebGL implementation is responsible for making all + // alpha values appear as though they were 1.0. + ctx.clearColor(0.0, 1.0, 0, 0.0); + } + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.disable(ctx.SCISSOR_TEST); + } + + function setCanvasTo257x257(ctx, bindingTarget, hasAlpha) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx, hasAlpha); + } + + function setCanvasToMin(ctx, bindingTarget, hasAlpha) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx, hasAlpha); + } + + function runOneIteration(canvas, useTexSubImage2D, alpha, flipY, program, bindingTarget, opt_texture) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + else if (canvas.parentNode) + objType = 'canvas attached to DOM'; + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + ' with alpha=' + + alpha + ' flipY=' + flipY + ' source object: ' + objType + + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + ' with red-green'); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors before pixelStorei setup"); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_FLIP_Y_WEBGL"); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_PREMULTIPLY_ALPHA_WEBGL"); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors after setting UNPACK_COLORSPACE_CONVERSION_WEBGL"); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + // Initialize the texture to black first + if (useTexSubImage2D) { + gl.texImage2D(targets[tt], 0, gl[internalFormat], canvas.width, canvas.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], canvas); + } + } + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + var top = flipY ? (height - halfHeight) : 0; + var bottom = flipY ? 0 : (height - halfHeight); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, + "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, + "shouldBe " + greenColor); + } + + if (false) { + var ma = wtu.makeImageFromCanvas(canvas); + document.getElementById("console").appendChild(ma); + + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + async function runTest() + { + for (let alpha of [ true, false ]) { + let ctx = wtu.create3DContext(null, { alpha:alpha }); + let canvas = ctx.canvas; + // Note: We use preserveDrawingBuffer:true to prevent canvas + // visibility from interfering with the tests. + let visibleCtx = wtu.create3DContext(null, { preserveDrawingBuffer:true, alpha:alpha }); + if (!visibleCtx) { + testFailed("context does not exist"); + return; + } + let visibleCanvas = visibleCtx.canvas; + let descriptionNode = document.getElementById("description"); + document.body.insertBefore(visibleCanvas, descriptionNode); + + let cases = [ + { sub: false, flipY: true, ctx: ctx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: ctx }, + { sub: true, flipY: true, ctx: ctx }, + { sub: true, flipY: false, ctx: ctx }, + { sub: false, flipY: true, ctx: ctx, init: setCanvasTo257x257 }, + { sub: false, flipY: false, ctx: ctx }, + { sub: true, flipY: true, ctx: ctx }, + { sub: true, flipY: false, ctx: ctx }, + { sub: false, flipY: true, ctx: visibleCtx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: visibleCtx }, + { sub: true, flipY: true, ctx: visibleCtx }, + { sub: true, flipY: false, ctx: visibleCtx }, + ]; + + if (window.OffscreenCanvas) { + let offscreen = new OffscreenCanvas(1, 1); + let offscreenCtx = wtu.create3DContext(offscreen, { alpha:alpha }); + cases = cases.concat([ + { sub: false, flipY: true, ctx: offscreenCtx, init: setCanvasToMin }, + { sub: false, flipY: false, ctx: offscreenCtx }, + { sub: true, flipY: true, ctx: offscreenCtx }, + { sub: true, flipY: false, ctx: offscreenCtx }, + ]); + } + + async function runTexImageTest(bindingTarget) { + let program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + let count = repeatCount; + let caseNdx = 0; + let texture = undefined; + while (true) { + let c = cases[caseNdx]; + if (c.init) { + c.init(c.ctx, bindingTarget, alpha); + } + texture = runOneIteration(c.ctx.canvas, c.sub, alpha, c.flipY, program, bindingTarget, texture); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) + return; + } + await wtu.dispatchPromise(function() {}); + } + } + + await runTexImageTest(gl.TEXTURE_2D); + await runTexImageTest(gl.TEXTURE_CUBE_MAP); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + + return function() { + init().then(function(val) { + finishTest(); + }); + }; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js new file mode 100644 index 0000000000..85f763a0de --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas-sub-rectangle.js @@ -0,0 +1,287 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var realRedColor = [255, 0, 0]; + var realGreenColor = [0, 255, 0]; + var realBlueColor = [0, 0, 255]; + var realCyanColor = [0, 255, 255]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blueColor = [0, 0, 255]; + var cyanColor = [0, 255, 255]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking a sub-rectangle of a canvas (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var canvas2d = document.createElement('canvas'); + runTest(canvas2d, setupSourceCanvas2D, '2D-rendered canvas'); + + var canvasWebGL = document.createElement('canvas'); + runTest(canvasWebGL, setupSourceCanvasWebGL, 'WebGL-rendered canvas'); + + finishTest(); + } + + function uploadCanvasToTexture(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight) + { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = canvas.width; + var uploadHeight = canvas.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], canvas); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], canvas); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + } + + function fillStyle2D(ctx, color) { + ctx.fillStyle = 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')'; + } + + function setupSourceCanvas2D(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('2d'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + fillStyle2D(ctx, realRedColor); + ctx.fillRect(0, 0, halfWidth, halfHeight); + fillStyle2D(ctx, realGreenColor); + ctx.fillRect(halfWidth, 0, width - halfWidth, halfHeight); + fillStyle2D(ctx, realBlueColor); + ctx.fillRect(0, halfHeight, halfWidth, height - halfHeight); + fillStyle2D(ctx, realCyanColor); + ctx.fillRect(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + } + + function clearColorWebGL(ctx, color) { + ctx.clearColor(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + } + + function setupSourceCanvasWebGL(canvas) { + var width = canvas.width; + var height = canvas.height; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + + var ctx = canvas.getContext('webgl'); + // Always use the same pattern for this test: four quadrants: + // red green + // blue cyan + // Handle odd-sized canvases + + ctx.viewport(0, 0, width, height); + ctx.enable(ctx.SCISSOR_TEST); + // OpenGL origin is lower-left + ctx.scissor(0, 0, halfWidth, halfHeight); + clearColorWebGL(ctx, realBlueColor); + ctx.scissor(halfWidth, 0, width - halfWidth, halfHeight); + clearColorWebGL(ctx, realCyanColor); + ctx.scissor(0, halfHeight, halfWidth, height - halfHeight); + clearColorWebGL(ctx, realRedColor); + ctx.scissor(halfWidth, halfHeight, width - halfWidth, height - halfHeight); + clearColorWebGL(ctx, realGreenColor); + } + + function runOneIteration(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, + rTextureCoord, expectedColor, program, + canvasSize, canvasSetupFunction, sourceDescription) + { + debug(''); + debug('Testing ' + sourceDescription + ' with ' + + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ', flipY=' + flipY + ', bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ', sourceSubRectangle=' + sourceSubRectangle + + ', depth=' + depth + + (unpackImageHeight ? ', unpackImageHeight=' + unpackImageHeight : '') + + ', rTextureCoord=' + rTextureCoord); + + // Initialize the contents of the source canvas. + var width = canvasSize[0]; + var height = canvasSize[1]; + var halfWidth = Math.floor(width / 2); + var halfHeight = Math.floor(height / 2); + canvas.width = width; + canvas.height = height; + canvasSetupFunction(canvas); + + uploadCanvasToTexture(canvas, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight); + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check the rendered canvas + wtu.checkCanvasRect(gl, 0, 0, canvasSize[0], canvasSize[1], expectedColor, "shouldBe " + expectedColor); + } + + function runTest(canvas, canvasSetupFunction, sourceDescription) + { + var cases = [ + // Small canvas cases. Expected that these won't be + // GPU-accelerated in most browsers' implementations. + + // No UNPACK_IMAGE_HEIGHT specified. + { expected: redColor, flipY: false, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [4, 4], subRect: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [4, 4], subRect: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0 }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { expected: redColor, flipY: false, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [4, 4], subRect: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [4, 4], subRect: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0 }, + + // Larger canvas cases. Expected that these will be + // GPU-accelerated in most browsers' implementations. + // Changes will be gladly accepted to trigger more + // browsers' heuristics to accelerate these canvases. + + // No UNPACK_IMAGE_HEIGHT specified. + { expected: redColor, flipY: false, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [0, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 0, 192, 192], depth: 2, rTextureCoord: 1.0 }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { expected: redColor, flipY: false, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: blueColor, flipY: false, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: blueColor, flipY: true, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: redColor, flipY: true, size: [384, 384], subRect: [0, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: greenColor, flipY: false, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: cyanColor, flipY: false, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + { expected: cyanColor, flipY: true, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 0.0 }, + { expected: greenColor, flipY: true, size: [384, 384], subRect: [192, 0, 96, 96], depth: 2, unpackImageHeight: 192, rTextureCoord: 1.0 }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(canvas, false, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + runOneIteration(canvas, true, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + } + + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(canvas, false, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + runOneIteration(canvas, true, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].subRect, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].expected, + program, cases[i].size, canvasSetupFunction, sourceDescription); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js new file mode 100644 index 0000000000..c41cc4e897 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-canvas.js @@ -0,0 +1,226 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var whiteColor = [255, 255, 255, 255]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + whiteColor = [255, 0, 0, 255]; + greenColor = [0, 0, 0]; + break; + case gl.RG: + case gl.RG_INTEGER: + whiteColor = [255, 255, 0, 255]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "#ff0000"; + ctx.fillRect(0, 0, width, halfHeight); + ctx.fillStyle = "#00ff00"; + ctx.fillRect(0, halfHeight, width, height - halfHeight); + } + + function drawTextInCanvas(ctx, bindingTarget) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, width, height); + ctx.font = '20pt Arial'; + ctx.fillStyle = 'black'; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText("1234567890", width / 2, height / 4); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + ctx.canvas.width = 2; + } else { + ctx.canvas.width = 1; + } + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function runOneIteration(canvas, flipY, program, bindingTarget, opt_texture, opt_fontTest) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + debug('Testing ' + ' with flipY=' + flipY + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ' source object: ' + objType + ' canvas size: ' + canvas.width + 'x' + canvas.height + (opt_fontTest ? " with fonts" : " with red-green")); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], canvas.width, canvas.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, canvas.width, canvas.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], canvas); + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var top = flipY ? 0 : (height - halfHeight); + var bottom = flipY ? (height - halfHeight) : 0; + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + if (opt_fontTest) { + // check half is a solid color. + wtu.checkCanvasRect( + gl, 0, top, width, halfHeight, + whiteColor, + "should be white"); + // check other half is not a solid color. + wtu.checkCanvasRectColor( + gl, 0, bottom, width, halfHeight, + whiteColor, 0, + function() { + testFailed("font missing"); + }, + function() { + testPassed("font renderered"); + }, + debug); + } else { + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, + "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, + "shouldBe " + greenColor); + } + + return texture; + } + + function runTest(canvas) + { + var canvas = document.createElement('canvas'); + + var cases = [ + { canvas: canvas, flipY: true, font: false, init: setCanvasToMin }, + { canvas: canvas, flipY: false, font: false }, + { canvas: canvas, flipY: true, font: false, init: setCanvasTo257x257 }, + { canvas: canvas, flipY: false, font: false }, + { canvas: canvas, flipY: true, font: true, init: drawTextInCanvas }, + { canvas: canvas, flipY: false, font: true }, + ]; + + if (window.OffscreenCanvas) { + var offscreenCanvas = new OffscreenCanvas(1, 1); + cases = cases.concat([ + { canvas: offscreenCanvas, flipY: true, font: false, init: setCanvasToMin }, + { canvas: offscreenCanvas, flipY: false, font: false }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { // TEXTURE_2D_ARRAY + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = 4; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + if (c.init) { + c.init(c.canvas.getContext('2d'), bindingTarget); + } + texture = runOneIteration(c.canvas, c.flipY, program, bindingTarget, texture, c.font); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + wtu.waitForComposite(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js new file mode 100644 index 0000000000..b55bdcb143 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-blob.js @@ -0,0 +1,48 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from a Blob (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", resourcePath + "red-green-semi-transparent.png"); + xhr.responseType = 'blob'; + xhr.onload = function() { + var blob = xhr.response; + runImageBitmapTest(blob, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + }; + xhr.send(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js new file mode 100644 index 0000000000..805298b377 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-canvas.js @@ -0,0 +1,74 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLCanvasElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var testCanvas = document.createElement('canvas'); + var ctx = testCanvas.getContext("2d"); + setCanvasToMin(ctx); + runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + setCanvasTo257x257(ctx); + return runImageBitmapTest(testCanvas, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + }).then(() => { + finishTest(); + }); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + ctx.fillStyle = "rgba(255, 0, 0, 1)"; + ctx.fillRect(0, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; + ctx.fillRect(halfWidth, 0, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 1)"; + ctx.fillRect(0, halfHeight, halfWidth, halfHeight); + ctx.fillStyle = "rgba(0, 255, 0, 0.5)"; + ctx.fillRect(halfWidth, halfHeight, halfWidth, halfHeight); + } + + function setCanvasToMin(ctx) { + ctx.canvas.width = 2; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function setCanvasTo257x257(ctx) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js new file mode 100644 index 0000000000..44c9ffa378 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-bitmap.js @@ -0,0 +1,56 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an ImageBitmap (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + createImageBitmap(imageData, {imageOrientation: "none", premultiplyAlpha: "none"}) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + return createImageBitmap(imageData); + }).then( bitmap => { + return runImageBitmapTest(bitmap, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + }, () => { + testFailed("createImageBitmap(imageData) should succeed."); + }).then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js new file mode 100644 index 0000000000..cca0358e17 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image-data.js @@ -0,0 +1,49 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var imageData = new ImageData(new Uint8ClampedArray( + [255, 0, 0, 255, + 255, 0, 0, 0, + 0, 255, 0, 255, + 0, 255, 0, 0]), + 2, 2); + + runImageBitmapTest(imageData, 0, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js new file mode 100644 index 0000000000..0f1121bbb9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-image.js @@ -0,0 +1,45 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLImageElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var image = new Image(); + image.onload = function() { + runImageBitmapTest(image, 0.5, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true) + .then(() => { + finishTest(); + }); + } + image.src = resourcePath + "red-green-semi-transparent.png"; + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js new file mode 100644 index 0000000000..a268f7d8d5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js @@ -0,0 +1,83 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageBitmap created from an HTMLVideoElement (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + if(!window.createImageBitmap || !window.ImageBitmap) { + finishTest(); + return; + } + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + finishTest(); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, async function() { + await runImageBitmapTest(video, 1, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, true); + runNextVideo(); + }); + } + runNextVideo(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js new file mode 100644 index 0000000000..bedf51a96a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-data.js @@ -0,0 +1,259 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imageData = null; + var blackColor = [0, 0, 0]; + var originalPixels = (function() { + // (red|green|blue|cyan)(opaque|transparent) + var ro = [255, 0, 0, 255]; var rt = [255, 0, 0, 0]; + var go = [0, 255, 0, 255]; var gt = [0, 255, 0, 0]; + var bo = [0, 0, 255, 255]; var bt = [0, 0, 255, 0]; + var co = [0, 255, 255, 255]; var ct = [0, 255, 255, 0]; + return [ro, rt, go, gt, + ro, rt, go, gt, + bo, bt, co, ct, + bo, bt, co, ct]; + })(); + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking ImageData (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + gl.disable(gl.BLEND); + + var canvas2d = document.getElementById("texcanvas"); + var context2d = canvas2d.getContext("2d"); + imageData = context2d.createImageData(4, 4); + var data = imageData.data; + for (var i = 0; i < originalPixels.length; i++) { + data.set(originalPixels[i], 4 * i); + } + + runTest(); + } + + function runOneIteration(useTexSubImage3D, flipY, premultiplyAlpha, bindingTarget, + depth, sourceSubRectangle, rTexCoord, program) + { + var expected = simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord); + var sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ', sourceSubRectangle=' + sourceSubRectangle; + sourceSubRectangleString += ', rTexCoord=' + rTexCoord; + } + debug(''); + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, premultiplyAlpha); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = imageData.width; + var uploadHeight = imageData.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], imageData); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], imageData); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + + var tl = expected[0][0]; + var tr = expected[0][1]; + var bl = expected[1][0]; + var br = expected[1][1]; + + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed("Shader incorrectly set up; couldn't find uRCoord uniform"); + return; + } + gl.uniform1f(rCoordLocation, rTexCoord); + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + + var top = 0; + var bottom = height - halfHeight; + var left = 0; + var right = width - halfWidth; + + debug("Checking pixel values"); + debug("Expecting: " + expected); + var expectedH = expected.length; + var expectedW = expected[0].length; + var texelH = Math.floor(gl.canvas.height / expectedH); + var texelW = Math.floor(gl.canvas.width / expectedW); + // For each entry of the expected[][] array, check the appropriate + // canvas rectangle for correctness. + for (var row = 0; row < expectedH; row++) { + var y = row * texelH; + for (var col = 0; col < expectedW; col++) { + var x = col * texelW; + var val = expected[row][col]; + wtu.checkCanvasRect(gl, x, y, texelW, texelH, val, "should be " + val); + } + } + } + + function runTest() + { + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_3D, program); + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTarget(gl.TEXTURE_2D_ARRAY, program); + + debug(""); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function simulate(flipY, premultiplyAlpha, depth, sourceSubRectangle, rTexCoord) { + var ro = [255, 0, 0]; var rt = premultiplyAlpha ? [0, 0, 0] : [255, 0, 0]; + var go = [0, 255, 0]; var gt = premultiplyAlpha ? [0, 0, 0] : [0, 255, 0]; + var bo = [0, 0, 255]; var bt = premultiplyAlpha ? [0, 0, 0] : [0, 0, 255]; + var co = [0, 255, 255]; var ct = premultiplyAlpha ? [0, 0, 0] : [0, 255, 255]; + var expected = [[ro, rt, go, gt], + [ro, rt, go, gt], + [bo, bt, co, ct], + [bo, bt, co, ct]]; + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++) { + expected[row][col][1] = 0; // zero the green channel + } + } + // fall-through + case gl.RG: + case gl.RG_INTEGER: + for (var row = 0; row < 4; row++) { + for (var col = 0; col < 4; col++) { + expected[row][col][2] = 0; // zero the blue channel + } + } + break; + default: + break; + } + + if (flipY) { + expected.reverse(); + } + + if (sourceSubRectangle) { + let expected2 = []; + for (var row = 0; row < sourceSubRectangle[3]; row++) { + expected2[row] = []; + for (var col = 0; col < sourceSubRectangle[2]; col++) { + expected2[row][col] = + expected[sourceSubRectangle[1] + row + rTexCoord * sourceSubRectangle[3]][sourceSubRectangle[0] + col]; + } + } + expected = expected2; + } + + return expected; + } + + function runTestOnBindingTarget(bindingTarget, program) { + var rects = [ + undefined, + [0, 0, 2, 2], + [2, 0, 2, 2], + ]; + var dbg = false; // Set to true for debug output images + if (dbg) { + (function() { + debug(""); + debug("Original ImageData (transparent pixels appear black):"); + var cvs = document.createElement("canvas"); + cvs.width = 4; + cvs.height = 4; + cvs.style.width = "32px"; + cvs.style.height = "32px"; + cvs.style.imageRendering = "pixelated"; + cvs.style.background = "#000"; + var ctx = cvs.getContext("2d"); + ctx.putImageData(imageData, 0, 0); + var output = document.getElementById("console"); + output.appendChild(cvs); + })(); + } + for (const sub of [false, true]) { + for (const flipY of [false, true]) { + for (const premul of [false, true]) { + for (let irect = 0; irect < rects.length; irect++) { + var rect = rects[irect]; + let depth = rect ? 2 : 1; + for (let rTexCoord = 0; rTexCoord < depth; rTexCoord++) { + // TODO: add tests for UNPACK_IMAGE_HEIGHT. + runOneIteration(sub, flipY, premul, bindingTarget, + depth, rect, rTexCoord, program); + if (dbg) { + debug("Actual:"); + var img = document.createElement("img"); + img.src = gl.canvas.toDataURL("image/png"); + var output = document.getElementById("console"); + output.appendChild(img); + } + } + } + } + } + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js new file mode 100644 index 0000000000..034bd8ab46 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image.js @@ -0,0 +1,260 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blueColor = [0, 0, 255]; + var cyanColor = [0, 255, 255]; + var imageURLs = [resourcePath + "red-green.png", + resourcePath + "red-green-blue-cyan-4x4.png"]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + blueColor = [0, 0, 0]; + cyanColor = [0, 0, 0]; + break; + + case gl.RG: + case gl.RG_INTEGER: + blueColor = [0, 0, 0]; + cyanColor = [0, 255, 0]; + break; + + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadImagesAsync(imageURLs, runTest); + } + + function uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight) + { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = image.width; + var uploadHeight = image.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the image into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], image); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], image); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture upload"); + } + + function runRedGreenTest(image) { + function runOneIteration(image, useTexSubImage3D, flipY, bindingTarget, topColor, bottomColor, program) + { + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY')); + + uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, 1); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, gl.TEXTURE_3D, + cases[i].topColor, cases[i].bottomColor, program); + } + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, cases[i].sub, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].topColor, cases[i].bottomColor, program); + } + } + + function runRedGreenBlueCyanTest(image) { + function runOneIteration(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, + rTextureCoord, topColor, bottomColor, program) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + unpackImageHeightString = ''; + if (unpackImageHeight) { + unpackImageHeightString = ' unpackImageHeight=' + unpackImageHeight; + } + debug('Testing ' + (useTexSubImage3D ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + sourceSubRectangleString + ' depth=' + depth + unpackImageHeightString + + ' rTextureCoord=' + rTextureCoord); + + uploadImageToTexture(image, useTexSubImage3D, flipY, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight); + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + var cases = [ + // No UNPACK_IMAGE_HEIGHT specified. + { flipY: false, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 0.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 2, 2], depth: 2, rTextureCoord: 1.0, + topColor: greenColor, bottomColor: greenColor }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { flipY: false, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: blueColor, bottomColor: blueColor }, + { flipY: true, sourceSubRectangle: [0, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 0.0, + topColor: cyanColor, bottomColor: cyanColor }, + { flipY: true, sourceSubRectangle: [2, 0, 1, 1], depth: 2, unpackImageHeight: 2, rTextureCoord: 1.0, + topColor: greenColor, bottomColor: greenColor }, + ]; + + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, false, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + runOneIteration(image, true, cases[i].flipY, gl.TEXTURE_3D, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + } + + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + for (var i in cases) { + runOneIteration(image, false, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + runOneIteration(image, true, cases[i].flipY, gl.TEXTURE_2D_ARRAY, + cases[i].depth, cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, cases[i].rTextureCoord, + cases[i].topColor, cases[i].bottomColor, + program); + } + } + + function runTest(imageMap) + { + runRedGreenTest(imageMap[imageURLs[0]]); + runRedGreenBlueCyanTest(imageMap[imageURLs[1]]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js new file mode 100644 index 0000000000..09a108dee8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-svg-image.js @@ -0,0 +1,104 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var imgCanvas; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking SVG image elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + wtu.loadTexture(gl, resourcePath + "red-green.svg", runTest); + } + + function runOneIteration(image, flipY, topColor, bottomColor, bindingTarget, program) + { + debug('Testing ' + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY')); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + // Upload the image into the texture + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], image.width, image.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, image.width, image.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], image); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor); + } + + function runTest(image) + { + var program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_3D, program); + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTarget(image, gl.TEXTURE_2D_ARRAY, program); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + } + + function runTestOnBindingTarget(image, bindingTarget, program) { + var cases = [ + { flipY: true, topColor: redColor, bottomColor: greenColor }, + { flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + for (var i in cases) { + runOneIteration(image, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + bindingTarget, program); + } + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js new file mode 100644 index 0000000000..0c2c40e8a5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js @@ -0,0 +1,244 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + // Test each format separately because many browsers implement each + // differently. Some might be GPU accelerated, some might not. Etc... + var videos = [ + { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, + { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, + { src: resourcePath + "red-green.webmvp8.webm", type: 'video/webm; codecs="vp8, vorbis"', }, + { src: resourcePath + "red-green.theora.ogv", type: 'video/ogg; codecs="theora, vorbis"', }, + ]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking video elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function runOneIteration(videoElement, flipY, useTexSubImage3D, topColor, bottomColor, program, bindingTarget, + depth, sourceSubRectangle, unpackImageHeight, rTextureCoord) + { + debug('Testing ' + + (useTexSubImage3D ? "texSubImage3D" : "texImage3D") + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + (sourceSubRectangle ? ', sourceSubRectangle=' + sourceSubRectangle : '') + + (unpackImageHeight ? ', unpackImageHeight=' + unpackImageHeight : '') + + ', depth=' + depth + + ', rTextureCoord=' + rTextureCoord); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); + var uploadWidth = videoElement.width; + var uploadHeight = videoElement.height; + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + uploadWidth = sourceSubRectangle[2]; + uploadHeight = sourceSubRectangle[3]; + } + if (unpackImageHeight) { + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight); + } + // Upload the videoElement into the texture + if (useTexSubImage3D) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], + uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, + uploadWidth, uploadHeight, depth, + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage3D(bindingTarget, 0, gl[internalFormat], + uploadWidth, uploadHeight, depth, 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var rCoordLocation = gl.getUniformLocation(program, 'uRCoord'); + if (!rCoordLocation) { + testFailed('Shader incorrectly set up; couldn\'t find uRCoord uniform'); + return; + } + gl.uniform1f(rCoordLocation, rTextureCoord); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + const tolerance = 6; + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + + function runTest(videoElement) + { + var cases = [ + // No UNPACK_IMAGE_HEIGHT specified. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 5, rTextureCoord: 0, + topColor: redColor, bottomColor: redColor }, + // Note that an rTextureCoord of 4.0 satisfies the need to + // have it be >= 1.0 for the TEXTURE_3D case, and also its + // use as an index in the TEXTURE_2D_ARRAY case. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 5, rTextureCoord: 4, + topColor: greenColor, bottomColor: greenColor }, + { flipY: false, sourceSubRectangle: [24, 48, 32, 32], depth: 1, rTextureCoord: 0, + topColor: greenColor, bottomColor: redColor }, + { flipY: true, sourceSubRectangle: [24, 48, 32, 32], depth: 1, rTextureCoord: 0, + topColor: redColor, bottomColor: greenColor }, + + // Use UNPACK_IMAGE_HEIGHT to skip some pixels. + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 2, unpackImageHeight: 64, rTextureCoord: 0, + topColor: redColor, bottomColor: redColor }, + { flipY: false, sourceSubRectangle: [32, 16, 16, 16], depth: 2, unpackImageHeight: 64, rTextureCoord: 1, + topColor: greenColor, bottomColor: greenColor }, + ]; + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.type); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + runOneIteration(video, cases[i].flipY, false, + cases[i].topColor, cases[i].bottomColor, + program, bindingTarget, cases[i].depth, + cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, + cases[i].rTextureCoord); + runOneIteration(video, cases[i].flipY, true, + cases[i].topColor, cases[i].bottomColor, + program, bindingTarget, cases[i].depth, + cases[i].sourceSubRectangle, + cases[i].unpackImageHeight, + cases[i].rTextureCoord); + } + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js new file mode 100644 index 0000000000..fe14b0c8eb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-webgl-canvas.js @@ -0,0 +1,212 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + + function init() + { + description('Verify texImage3D and texSubImage3D code paths taking webgl canvas elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runTest(); + } + + function setCanvasToRedGreen(ctx) { + var width = ctx.canvas.width; + var height = ctx.canvas.height; + var halfHeight = Math.floor(height / 2); + + ctx.viewport(0, 0, width, height); + + ctx.enable(ctx.SCISSOR_TEST); + ctx.scissor(0, 0, width, halfHeight); + ctx.clearColor(1.0, 0, 0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.scissor(0, halfHeight, width, height - halfHeight); + ctx.clearColor(0.0, 1.0, 0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT); + ctx.disable(ctx.SCISSOR_TEST); + } + + function setCanvasTo257x257(ctx, bindingTarget) { + ctx.canvas.width = 257; + ctx.canvas.height = 257; + setCanvasToRedGreen(ctx); + } + + function setCanvasToMin(ctx, bindingTarget) { + ctx.canvas.width = 1; + ctx.canvas.height = 2; + setCanvasToRedGreen(ctx); + } + + function runOneIteration(canvas, flipY, program, bindingTarget, opt_texture) + { + var objType = 'canvas'; + if (canvas.transferToImageBitmap) + objType = 'OffscreenCanvas'; + else if (canvas.parentNode) + objType = 'canvas attached to DOM'; + debug('Testing flipY=' + flipY + ' object type: ' + objType + + ' bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY') + + ' canvas size: ' + canvas.width + 'x' + canvas.height + ' with red-green'); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + if (!opt_texture) { + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + } else { + var texture = opt_texture; + } + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + wtu.failIfGLError(gl, 'gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);'); + + // Upload the image into the texture + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], canvas.width, canvas.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(bindingTarget, 0, 0, 0, 0, canvas.width, canvas.height, 1 /* depth */, + gl[pixelFormat], gl[pixelType], canvas); + + var width = gl.canvas.width; + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var top = flipY ? (height - halfHeight) : 0; + var bottom = flipY ? 0 : (height - halfHeight); + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 255, 0, 255]); + + // Check the top and bottom halves and make sure they have the right color. + debug("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, 0, bottom, width, halfHeight, redColor, "shouldBe " + redColor); + debug("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, 0, top, width, halfHeight, greenColor, "shouldBe " + greenColor); + + if (false) { + var ma = wtu.makeImageFromCanvas(canvas); + document.getElementById("console").appendChild(ma); + + var m = wtu.makeImageFromCanvas(gl.canvas); + document.getElementById("console").appendChild(m); + document.getElementById("console").appendChild(document.createElement("hr")); + } + + return texture; + } + + function runTest() + { + var ctx = wtu.create3DContext(); + var canvas = ctx.canvas; + // Note: We use preserveDrawingBuffer:true to prevent canvas + // visibility from interfering with the tests. + var visibleCtx = wtu.create3DContext(null, { preserveDrawingBuffer:true }); + var visibleCanvas = visibleCtx.canvas; + var descriptionNode = document.getElementById("description"); + document.body.insertBefore(visibleCanvas, descriptionNode); + + var cases = [ + { flipY: true, ctx: ctx, init: setCanvasToMin }, + { flipY: false, ctx: ctx }, + { flipY: true, ctx: ctx, init: setCanvasTo257x257 }, + { flipY: false, ctx: ctx }, + { flipY: true, ctx: visibleCtx, init: setCanvasToMin}, + { flipY: false, ctx: visibleCtx }, + ]; + + if (window.OffscreenCanvas) { + var offscreen = new OffscreenCanvas(1, 1); + var offscreenCtx = wtu.create3DContext(offscreen); + cases = cases.concat([ + { flipY: true, ctx: offscreenCtx, init: setCanvasToMin }, + { flipY: false, ctx: offscreenCtx }, + ]); + } + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var count = 4; + var caseNdx = 0; + var texture = undefined; + function runNextTest() { + var c = cases[caseNdx]; + if (c.init) { + c.init(c.ctx, bindingTarget); + } + texture = runOneIteration(c.ctx.canvas, c.flipY, program, bindingTarget, texture); + // for the first 2 iterations always make a new texture. + if (count < 2) { + gl.deleteTexture(texture); + texture = undefined; + } + ++caseNdx; + if (caseNdx == cases.length) { + caseNdx = 0; + --count; + if (!count) { + resolve("SUCCESS"); + return; + } + } + wtu.waitForComposite(runNextTest); + } + runNextTest(); + }); + } + + runTexImageTest(gl.TEXTURE_3D).then(function(val) { + runTexImageTest(gl.TEXTURE_2D_ARRAY).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js new file mode 100644 index 0000000000..f37f12fe91 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-utils.js @@ -0,0 +1,865 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +var TexImageUtils = (function() { + + "use strict"; + + var wtu = WebGLTestUtils; + + /** + * A vertex shader for a single texture. + * @type {string} + */ + var simpleTextureVertexShaderES3 = [ + '#version 300 es', + 'in vec4 vPosition;', + 'in vec2 texCoord0;', + 'out vec2 texCoord;', + 'void main() {', + ' gl_Position = vPosition;', + ' texCoord = texCoord0;', + '}'].join('\n'); + + /** + * A fragment shader for a single unsigned integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler2D tex;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, texCoord);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single signed integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler2D tex;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, texCoord);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single cube map unsigned integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleCubeMapUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usamplerCube tex;', + 'uniform highp int face;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + // Transform [0, 1] -> [-1, 1] + ' vec2 texC2 = (texCoord * 2.) - 1.;', + // Transform 2d tex coord. to each face of TEXTURE_CUBE_MAP coord. + ' vec3 texCube = vec3(0., 0., 0.);', + ' if (face == 34069) {', // TEXTURE_CUBE_MAP_POSITIVE_X + ' texCube = vec3(1., -texC2.y, -texC2.x);', + ' } else if (face == 34070) {', // TEXTURE_CUBE_MAP_NEGATIVE_X + ' texCube = vec3(-1., -texC2.y, texC2.x);', + ' } else if (face == 34071) {', // TEXTURE_CUBE_MAP_POSITIVE_Y + ' texCube = vec3(texC2.x, 1., texC2.y);', + ' } else if (face == 34072) {', // TEXTURE_CUBE_MAP_NEGATIVE_Y + ' texCube = vec3(texC2.x, -1., -texC2.y);', + ' } else if (face == 34073) {', // TEXTURE_CUBE_MAP_POSITIVE_Z + ' texCube = vec3(texC2.x, -texC2.y, 1.);', + ' } else if (face == 34074) {', // TEXTURE_CUBE_MAP_NEGATIVE_Z + ' texCube = vec3(-texC2.x, -texC2.y, -1.);', + ' }', + ' uvec4 data = texture(tex, texCube);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single cube map signed integer texture. + * @type {string} + */ + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simpleCubeMapIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isamplerCube tex;', + 'uniform highp int face;', + 'in vec2 texCoord;', + 'out vec4 fragData;', + 'void main() {', + // Transform [0, 1] -> [-1, 1] + ' vec2 texC2 = (texCoord * 2.) - 1.;', + // Transform 2d tex coord. to each face of TEXTURE_CUBE_MAP coord. + ' vec3 texCube = vec3(0., 0., 0.);', + ' if (face == 34069) {', // TEXTURE_CUBE_MAP_POSITIVE_X + ' texCube = vec3(1., -texC2.y, -texC2.x);', + ' } else if (face == 34070) {', // TEXTURE_CUBE_MAP_NEGATIVE_X + ' texCube = vec3(-1., -texC2.y, texC2.x);', + ' } else if (face == 34071) {', // TEXTURE_CUBE_MAP_POSITIVE_Y + ' texCube = vec3(texC2.x, 1., texC2.y);', + ' } else if (face == 34072) {', // TEXTURE_CUBE_MAP_NEGATIVE_Y + ' texCube = vec3(texC2.x, -1., -texC2.y);', + ' } else if (face == 34073) {', // TEXTURE_CUBE_MAP_POSITIVE_Z + ' texCube = vec3(texC2.x, -texC2.y, 1.);', + ' } else if (face == 34074) {', // TEXTURE_CUBE_MAP_NEGATIVE_Z + ' texCube = vec3(-texC2.x, -texC2.y, -1.);', + ' }', + ' ivec4 data = texture(tex, texCube);', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + var simple3DTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump sampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' fragData = vec4(texture(tex, vec3(texCoord, uRCoord)).rgb, 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D unsigned integer texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple3DUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 3D signed integer texture. + * @type {string} + */ + // Note that the tex coordinate r (the uniform uRCoord) is set to 0.0 by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple3DIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler3D tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + var simple2DArrayTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump sampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' fragData = vec4(texture(tex, vec3(texCoord, uRCoord)).rgb, 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY unsigned integer texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple2DArrayUintTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump usampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' uvec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + /** + * A fragment shader for a single 2D_ARRAY signed integer texture. + * @type {string} + */ + // Note that the first image in the array (selected by the uniform + // uRCoord) is used by default. + // Note we always output 1.0 for alpha because if the texture does not contain + // alpha channel, sampling returns 1; for RGBA textures, sampling returns [0,255]. + var simple2DArrayIntTextureFragmentShaderES3 = [ + '#version 300 es', + 'precision mediump float;', + 'uniform mediump isampler2DArray tex;', + 'in vec2 texCoord;', + 'uniform float uRCoord;', + 'out vec4 fragData;', + 'void main() {', + ' ivec4 data = texture(tex, vec3(texCoord, uRCoord));', + ' fragData = vec4(float(data[0])/255.0,', + ' float(data[1])/255.0,', + ' float(data[2])/255.0,', + ' 1.0);', + '}'].join('\n'); + + + /** + * Creates a simple texture vertex shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleTextureVertexShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleTextureVertexShaderES3, gl.VERTEX_SHADER); + }; + + /** + * Creates a simple unsigned integer texture fragment shader. + * Output is scaled by 1/255 to bring the result into normalized float range. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple signed integer texture fragment shader. + * Output is scaled by 1/255 to bring the result into normalized float range. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple cube map unsigned integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleCubeMapUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleCubeMapUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple cube map signed integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimpleCubeMapIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simpleCubeMapIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D unsigned integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 3D signed integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple3DIntTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple3DIntTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 2D_ARRAY texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple2DArrayTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple2DArrayTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple 2D_ARRAY integer texture fragment shader. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLShader} + */ + var setupSimple2DArrayUintTextureFragmentShader = function(gl) { + return WebGLTestUtils.loadShader(gl, simple2DArrayUintTextureFragmentShaderES3, gl.FRAGMENT_SHADER); + }; + + /** + * Creates a simple unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimpleUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimpleIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple cube map unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleCubeMapUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl); + var fs = setupSimpleCubeMapUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple cube map signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimpleCubeMapIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl); + var fs = setupSimpleCubeMapIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 3D signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple3DIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple3DIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY unsigned integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayUintTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayUintTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a simple 2D_ARRAY signed integer texture program. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {number} opt_positionLocation The attrib location for position. + * @param {number} opt_texcoordLocation The attrib location for texture coords. + * @return {WebGLProgram} + */ + var setupSimple2DArrayIntTextureProgram = function(gl, opt_positionLocation, opt_texcoordLocation) + { + opt_positionLocation = opt_positionLocation || 0; + opt_texcoordLocation = opt_texcoordLocation || 1; + var vs = setupSimpleTextureVertexShader(gl), + fs = setupSimple2DArrayIntTextureFragmentShader(gl); + if (!vs || !fs) { + return null; + } + var program = WebGLTestUtils.setupProgram( + gl, + [vs, fs], + ['vPosition', 'texCoord0'], + [opt_positionLocation, opt_texcoordLocation]); + if (!program) { + gl.deleteShader(fs); + gl.deleteShader(vs); + } + gl.useProgram(program); + return program; + }; + + /** + * Creates a program and buffers for rendering a unsigned integer textured quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupUintTexturedQuad = function(gl) { + var program = setupSimpleUintTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a signed integer textured quad. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupIntTexturedQuad = function(gl) { + var program = setupSimpleIntTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a textured quad with + * a cube map unsigned integer texture. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupUintTexturedQuadWithCubeMap = function(gl) + { + var program = setupSimpleCubeMapUintTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Creates a program and buffers for rendering a textured quad with + * a cube map signed integer texture. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @return {!WebGLProgram} + */ + var setupIntTexturedQuadWithCubeMap = function(gl) + { + var program = setupSimpleCubeMapIntTextureProgram(gl); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Does the GL internal format represent an unsigned integer format + * texture? + * @return {boolean} + */ + var isUintFormat = function(internalFormat) + { + return (internalFormat == "R8UI" || internalFormat == "RG8UI" || internalFormat == "RGB8UI" || internalFormat == "RGBA8UI" || + internalFormat == "R16UI" || internalFormat == "RG16UI" || internalFormat == "RGB16UI" || internalFormat == "RGBA16UI" || + internalFormat == "R32UI" || internalFormat == "RG32UI" || internalFormat == "RGB32UI" || internalFormat == "RGBA32UI"); + }; + + /** + * Does the GL internal format represent an signed integer format + * texture? + * @return {boolean} + */ + var isIntFormat = function(internalFormat) + { + return (internalFormat == "R8I" || internalFormat == "RG8I" || internalFormat == "RGB8I" || internalFormat == "RGBA8I" || + internalFormat == "R16I" || internalFormat == "RG16I" || internalFormat == "RGB16I" || internalFormat == "RGBA16I" || + internalFormat == "R32I" || internalFormat == "RG32I" || internalFormat == "RGB32I" || internalFormat == "RGBA32I"); + }; + + /** + * Createa a program and buffers for rendering a textured quad for + * tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuad = function(gl, internalFormat) + { + if (isUintFormat(internalFormat)) + return setupUintTexturedQuad(gl); + if (isIntFormat(internalFormat)) + return setupIntTexturedQuad(gl); + return wtu.setupTexturedQuad(gl); + }; + + /** + * Createa a program and buffers for rendering a textured quad with + * a cube map for tex-image-and-sub-image tests. Handle selection of + * correct program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + function setupTexturedQuadWithCubeMap(gl, internalFormat) + { + if (isUintFormat(internalFormat)) + return setupUintTexturedQuadWithCubeMap(gl); + if (isIntFormat(internalFormat)) + return setupIntTexturedQuadWithCubeMap(gl); + return wtu.setupTexturedQuadWithCubeMap(gl); + } + + /** + * Createa a program and buffers for rendering a textured quad with a 3D texture + * for tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuadWith3D = function(gl, internalFormat) + { + var program; + if (isUintFormat(internalFormat)) + program = setupSimple3DUintTextureProgram(gl); + else if (isIntFormat(internalFormat)) + program = setupSimple3DIntTextureProgram(gl); + else + program = setupSimple3DTextureProgram(gl); + var uRCoordLoc = gl.getUniformLocation(program, 'uRCoord'); + gl.uniform1f(uRCoordLoc, 0.0); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Createa a program and buffers for rendering a textured quad with a 2D_ARRAY + * texture for tex-image-and-sub-image tests. Handle selection of correct + * program to handle texture format. + * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use. + * @param {string} internalFormat The internal format for texture to be tested. + */ + var setupTexturedQuadWith2DArray = function(gl, internalFormat) + { + var program; + if (isUintFormat(internalFormat)) + program = setupSimple2DArrayUintTextureProgram(gl); + else if (isIntFormat(internalFormat)) + program = setupSimple2DArrayIntTextureProgram(gl); + else + program = setupSimple2DArrayTextureProgram(gl); + var uRCoordLoc = gl.getUniformLocation(program, 'uRCoord'); + gl.uniform1f(uRCoordLoc, 0.0); + wtu.setupUnitQuad(gl); + return program; + }; + + /** + * Return a list of unpack color spaces to test, supported by the specified + * WebGLRenderingContext. + */ + var unpackColorSpacesToTest = function(gl) + { + if ('unpackColorSpace' in gl) + return ['srgb', 'display-p3']; + else + return [undefined]; + } + + /** + * For each entry in unpackColorSpaces, duplicate all of cases, adding an + * unpackColorSpace key with its value set to that entry to each case. + */ + var crossProductTestCasesWithUnpackColorSpaces = function(testCaseList, unpackColorSpaces) + { + var testCaseWithUnpackColorSpace = function(testCase, colorSpace) + { + return {...testCase, ...{unpackColorSpace:colorSpace}}; + } + var listOfTestCaseLists = unpackColorSpaces.map(colorSpace => + testCaseList.map(testCase => testCaseWithUnpackColorSpace(testCase, colorSpace))); + return listOfTestCaseLists.flat(); + } + + /** + * Given given an internalformat, format, and type, return the tolerance + * that should be used when comparing an input 8-bit value to one that has + * been truncated through the specified formats. + */ + var tolerance = function(internalformat, format, type) { + function typeTolerance(type) { + switch(type) { + case 'UNSIGNED_SHORT_5_6_5': + case 'UNSIGNED_SHORT_5_5_5_1': + return 255 / 31; + case 'UNSIGNED_SHORT_4_4_4_4': + return 255 / 15; + break; + default: + return 1; + } + }; + function formatTolerance(format) { + switch(format) { + case 'RGB565': + case 'RGB5_A1': + return 255/31; + case 'RGBA4': + return 255/15; + default: + return 1; + } + }; + return Math.max(formatTolerance(internalformat), + formatTolerance(format), + typeTolerance(type)); + } + + return { + setupTexturedQuad: setupTexturedQuad, + setupTexturedQuadWithCubeMap: setupTexturedQuadWithCubeMap, + setupTexturedQuadWith3D: setupTexturedQuadWith3D, + setupTexturedQuadWith2DArray: setupTexturedQuadWith2DArray, + unpackColorSpacesToTest: unpackColorSpacesToTest, + crossProductTestCasesWithUnpackColorSpaces: crossProductTestCasesWithUnpackColorSpaces, + tolerance: tolerance + }; + +}()); diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js new file mode 100644 index 0000000000..8faedf9eaa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-with-image-bitmap-utils.js @@ -0,0 +1,435 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + + +function runOneIterationImageBitmapTest(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + var halfRed = [128, 0, 0]; + var halfGreen = [0, 128, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blackColor = [0, 0, 0]; + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + halfRed = [128, 128, 128]; + halfGreen = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + halfRed = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + halfRed = wtu.sRGBToLinear(halfRed); + halfGreen = wtu.sRGBToLinear(halfGreen); + break; + default: + break; + } + + var str; + if (optionsVal.is3D) { + str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_3D ? 'TEXTURE_3D' : 'TEXTURE_2D_ARRAY'); + } else { + str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=' + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP'); + } + debug(str); + bufferedLogToConsole(str); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + var targets = [bindingTarget]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + + bufferedLogToConsole("Start uploading the image into a texture"); + // Upload the image into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (optionsVal.is3D) { + gl.texImage3D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage3D(targets[tt], 0, 0, 0, 0, bitmap.width, bitmap.height, 1, + gl[pixelFormat], gl[pixelType], bitmap); + } else { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage2D(targets[tt], 0, gl[internalFormat], bitmap.width, bitmap.height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], bitmap); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], bitmap); + } + } + } + bufferedLogToConsole("Uploading into texture completed"); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var quarterWidth = Math.floor(halfWidth / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var quarterHeight = Math.floor(halfHeight / 2); + + var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight); + var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight; + var left = quarterWidth; + var right = halfWidth + quarterWidth / 2; + + var tl = redColor; + var tr = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor; + var bl = greenColor; + var br = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor; + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0); + let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)"; + bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, left, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, right, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance); + } + bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, left, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, right, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance); + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); +} + +function resetUnpackParams(gl) +{ + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.pixelStorei(gl.UNPACK_ROW_LENGTH, 0); + gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0); +} + +function runOneIterationImageBitmapTestSubSource(useTexSubImage, bindingTarget, program, bitmap, flipY, premultiplyAlpha, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + var halfRed = [128, 0, 0]; + var halfGreen = [0, 128, 0]; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var blackColor = [0, 0, 0]; + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + case gl.LUMINANCE: + case gl.LUMINANCE_ALPHA: + redColor = [255, 255, 255]; + greenColor = [0, 0, 0]; + halfRed = [128, 128, 128]; + halfGreen = [0, 0, 0]; + break; + case gl.ALPHA: + redColor = [0, 0, 0]; + greenColor = [0, 0, 0]; + halfRed = [0, 0, 0]; + halfGreen = [0, 0, 0]; + break; + default: + break; + } + + switch (gl[internalFormat]) { + case gl.SRGB8: + case gl.SRGB8_ALPHA8: + halfRed = wtu.sRGBToLinear(halfRed); + halfGreen = wtu.sRGBToLinear(halfGreen); + break; + default: + break; + } + + var str; + if (optionsVal.is3D) { + str = 'Testing ' + (useTexSubImage ? 'texSubImage3D' : 'texImage3D') + '[SubSource]' + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=TEXTURE_3D'; + } else { + str = 'Testing ' + (useTexSubImage ? 'texSubImage2D' : 'texImage2D') + '[SubSource]' + + ' with flipY=' + flipY + ', premultiplyAlpha=' + premultiplyAlpha + + ', bindingTarget=TEXTURE_2D'; + } + debug(str); + bufferedLogToConsole(str); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Enable writes to the RGBA channels + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + var srcTL = redColor; + var srcTR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfRed : (optionsVal.alpha == 1) ? redColor : blackColor) : redColor; + var srcBL = greenColor; + var srcBR = premultiplyAlpha ? ((optionsVal.alpha == 0.5) ? halfGreen : (optionsVal.alpha == 1) ? greenColor : blackColor) : greenColor; + + var tl, tr, bl, br; + + bufferedLogToConsole("Start uploading the image into a texture"); + // Upload the image into the texture + if (optionsVal.is3D) { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], null); + // Only upload the left half image to the right half texture. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.texSubImage3D(bindingTarget, 0, bitmap.width / 2, 0, 0, bitmap.width / 2, bitmap.height, 1, + gl[pixelFormat], gl[pixelType], bitmap); + tl = blackColor; + tr = srcTL; + bl = blackColor; + br = srcBL; + } else { + // Only upload the bottom middle quarter image + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2); + gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0); + gl.texImage3D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height / 2, 1 /* depth */, 0, + gl[pixelFormat], gl[pixelType], bitmap); + if (!flipY) { + tl = srcBL; + tr = srcBR; + bl = srcBL; + br = srcBR; + } else { + tl = srcTL; + tr = srcTR; + bl = srcTL; + br = srcTR; + } + } + } else { + if (useTexSubImage) { + // Initialize the texture to black first + gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width, bitmap.height, 0, + gl[pixelFormat], gl[pixelType], null); + // Only upload the left half image to the right half texture. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + gl.texSubImage2D(bindingTarget, 0, bitmap.width / 2, 0, bitmap.width / 2, bitmap.height, + gl[pixelFormat], gl[pixelType], bitmap); + tl = blackColor; + tr = srcTL; + bl = blackColor; + br = srcBL; + } else { + // Only upload the right bottom image. + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, bitmap.width / 2); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, bitmap.height / 2); + gl.texImage2D(bindingTarget, 0, gl[internalFormat], bitmap.width / 2, bitmap.height / 2, 0, + gl[pixelFormat], gl[pixelType], bitmap); + resetUnpackParams(gl); + if (!flipY) { + tl = srcBR; + tr = srcBR; + bl = srcBR; + br = srcBR; + } else { + tl = srcTR; + tr = srcTR; + bl = srcTR; + br = srcTR; + } + } + } + bufferedLogToConsole("Uploading into texture completed"); + + var width = gl.canvas.width; + var halfWidth = Math.floor(width / 2); + var quarterWidth = Math.floor(halfWidth / 2); + var height = gl.canvas.height; + var halfHeight = Math.floor(height / 2); + var quarterHeight = Math.floor(halfHeight / 2); + + var top = flipY ? quarterHeight : (height - halfHeight + quarterHeight); + var bottom = flipY ? (height - halfHeight + quarterHeight) : quarterHeight; + + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + + // Check the top pixel and bottom pixel and make sure they have + // the right color. + // For right side, check pixels closer to left to avoid border in the video tests. + let skipAlphaTests = (premultiplyAlpha === undefined && optionsVal.alpha != 1.0); + let skipStr = " (Skipping checking right pixel since premultiplyAlpha was undefined and alpha != 1.0)"; + bufferedLogToConsole("Checking " + (flipY ? "top" : "bottom")); + wtu.checkCanvasRect(gl, quarterWidth, bottom, 2, 2, tl, "shouldBe " + tl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, bottom, 2, 2, tr, "shouldBe " + tr + " +/-" + tolerance, tolerance); + } + bufferedLogToConsole("Checking " + (flipY ? "bottom" : "top")); + wtu.checkCanvasRect(gl, quarterWidth, top, 2, 2, bl, "shouldBe " + bl + " +/-" + tolerance, tolerance); + if (skipAlphaTests) { + bufferedLogToConsole(skipStr); + } else { + wtu.checkCanvasRect(gl, halfWidth + quarterWidth / 2, top, 2, 2, br, "shouldBe " + br + " +/-" + tolerance, tolerance); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); +} + +function runTestOnBindingTargetImageBitmap(bindingTarget, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance) +{ + cases.forEach(x => { + runOneIterationImageBitmapTest(x.sub, bindingTarget, program, x.bitmap, + x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + }); + + if (wtu.getDefault3DContextVersion() <= 1 || + (bindingTarget == gl.TEXTURE_CUBE_MAP || bindingTarget == gl.TEXTURE_2D_ARRAY)) + { + // Skip testing source sub region on TEXTURE_CUBE_MAP and TEXTURE_2D_ARRAY on WebGL2 to save + // running time. + return; + } + + cases.forEach(x => { + runOneIterationImageBitmapTestSubSource(x.sub, bindingTarget, program, x.bitmap, + x.bitmap.flipY, x.bitmap.premultiply, optionsVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + }); +} + +function runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, tolerance) +{ + var cases = []; + bitmaps.forEach(bitmap => { + cases.push({bitmap: bitmap, sub: false}); + cases.push({bitmap: bitmap, sub: true}); + }); + + var optionsVal = {alpha: alphaVal, is3D: is3D}; + var program; + if (is3D) { + program = tiu.setupTexturedQuadWith3D(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_3D, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } else { + program = tiu.setupTexturedQuad(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } + + // cube map texture must be square + if (bitmaps[0].width == bitmaps[0].height) { + if (is3D) { + program = tiu.setupTexturedQuadWith2DArray(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_2D_ARRAY, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + runTestOnBindingTargetImageBitmap(gl.TEXTURE_CUBE_MAP, program, cases, optionsVal, + internalFormat, pixelFormat, pixelType, gl, tiu, wtu, tolerance); + } + } +} + +function runImageBitmapTest(source, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance) +{ + if (opt_tolerance === undefined) { + opt_tolerance = 10; + } + var p1 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "premultiply"}) + .then(cur => { cur.flipY = false; cur.premultiply = true; return cur; }); + var p2 = createImageBitmap(source, {imageOrientation: "none", premultiplyAlpha: "none"}) + .then(cur => { cur.flipY = false; cur.premultiply = false; return cur; }); + var p3 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "premultiply"}) + .then(cur => { cur.flipY = true; cur.premultiply = true; return cur; }); + var p4 = createImageBitmap(source, {imageOrientation: "flipY", premultiplyAlpha: "none"}) + .then(cur => { cur.flipY = true; cur.premultiply = false; return cur; }); + return Promise.all([p1, p2, p3, p4]) + .catch( () => { + testPassed("createImageBitmap with options may be rejected if it is not supported. Retrying without options."); + // The ImageBitmap's premultiplyAlpha setting will implicitly be + // "default", and per spec: + // https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#cropped-to-the-source-rectangle-with-formatting + // this value is implementation-dependent (either premultiplied or + // not). Skip testing the quadrants which have alpha != 1.0. + var p = createImageBitmap(source) + .then(cur => { cur.flipY = false; cur.premultiply = undefined; return cur; }); + return Promise.all([p]); + }).then( bitmaps => { + bufferedLogToConsole("All createImageBitmap promises are resolved"); + runImageBitmapTestInternal(bitmaps, alphaVal, internalFormat, pixelFormat, pixelType, gl, tiu, wtu, is3D, opt_tolerance); + }, (e) => { + // This will fail here when running from file:// instead of https://. + testFailed("createImageBitmap(source) failed: \"" + e.message + "\""); + }); +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js new file mode 100644 index 0000000000..22261afdb4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-input-validation.js @@ -0,0 +1,563 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This test relies on the surrounding web page defining a variable +// "contextVersion" which indicates what version of WebGL it's running +// on -- 1 for WebGL 1.0, 2 for WebGL 2.0, etc. + +"use strict"; +description("Validate tex functions input parameters"); + +var wtu = WebGLTestUtils; +var gl = null; +var tex = null; +var error = 0; + +shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, contextVersion)"); +shouldBeNonNull("tex = gl.createTexture()"); +gl.bindTexture(gl.TEXTURE_2D, tex); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +function enumToString(value) { + return wtu.glEnumToString(gl, value); +} + +function testTexParameter(testCase) { + var msg = "paramName: " + enumToString(testCase.pname); + error = testCase.expectedError; + gl.texParameteri(testCase.target, testCase.pname, testCase.param); + wtu.glErrorShouldBe(gl, error, msg); + gl.texParameterf(testCase.target, testCase.pname, testCase.param); + wtu.glErrorShouldBe(gl, error, msg); +} + +function testGetTexParameter(testCase) { + var msg = "paramName: " + enumToString(testCase.pname); + error = testCase.expectedError; + gl.getTexParameter(testCase.target, testCase.pname); + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexImage2D(testCase) { + var level = 0; + var width = 16; + var height = 16; + var msg = " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type) + + " border: " + testCase.border; + + gl.texImage2D(testCase.target, level, testCase.internalFormat, width, height, testCase.border, testCase.format, testCase.type, null); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexSubImage2D(testCase) { + var level = 0; + var xoffset = 0; + var yoffset = 0; + var width = 16; + var height = 16; + var msg = " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type); + var array = new Uint8Array(width * height * 4); + + gl.texSubImage2D(testCase.target, level, xoffset, yoffset, width, height, testCase.format, testCase.type, array); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyTexImage2D(testCase) { + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var msg = " colorBufferFormat: " + enumToString(testCase.colorBufferFormat) + + " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " border: " + testCase.border; + + gl.renderbufferStorage(gl.RENDERBUFFER, testCase.colorBufferFormat, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + gl.copyTexImage2D(testCase.target, level, testCase.internalFormat, x, y, width, height, testCase.border); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyTexSubImage2D(testCase) { + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var xoffset = 0; + var yoffset = 0; + var border = 0; + var type = gl.UNSIGNED_BYTE; + var msg = " colorBufferFormat: " + enumToString(testCase.colorBufferFormat) + + " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target); + + gl.renderbufferStorage(gl.RENDERBUFFER, testCase.colorBufferFormat, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + gl.texImage2D(testCase.target, level, testCase.internalFormat, xoffset + width, yoffset + height, border, testCase.internalFormat, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + gl.copyTexSubImage2D(testCase.target, level, xoffset, yoffset, x, y, width, height); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testCopyFromInternalFBO(testCase) { + var target = gl.TEXTURE_2D; + var level = 0; + var x = 0; + var y = 0; + var width = 16; + var height = 16; + var xoffset = 0; + var yoffset = 0; + var border = 0; + var type = gl.UNSIGNED_BYTE; + var msg = " colorBufferFormat: " + enumToString(testCase.contextAlpha ? gl.RGBA : gl.RGB) + + " internalFormat: " + enumToString(testCase.internalFormat); + + if (testCase.contextAlpha) { + gl = wtu.create3DContext(null, { alpha: true }, contextVersion); + } else { + gl = wtu.create3DContext(null, { alpha: false }, contextVersion); + } + shouldBeNonNull("gl"); + shouldBeNonNull("tex = gl.createTexture()"); + gl.bindTexture(target, tex); + if (testCase.subImage) { + gl.texImage2D(target, level, testCase.internalFormat, xoffset + width, yoffset + height, border, testCase.internalFormat, type, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.copyTexImage2D(target, level, testCase.internalFormat, x, y, width, height, border); + } + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +// Only for WebGL2.0. +function testTexImage3D(testCase) { + var level = 0; + var width = 16; + var height = 16; + var depth = 16; + var msg = " internalFormat: " + enumToString(testCase.internalFormat) + + " target: " + enumToString(testCase.target) + + " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type) + + " border: " + testCase.border; + + gl.texImage3D(testCase.target, level, testCase.internalFormat, width, height, depth, testCase.border, testCase.format, testCase.type, null); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + +function testTexSubImage3D(testCase) { + var level = 0; + var xoffset = 0; + var yoffset = 0; + var zoffset = 0; + var width = 16; + var height = 16; + var depth = 16; + var msg = " format: " + enumToString(testCase.format) + + " type: " + enumToString(testCase.type); + var array = new Uint8Array(width * height * depth * 4); + + gl.texSubImage3D(testCase.target, level, xoffset, yoffset, zoffset, width, height, depth, testCase.format, testCase.type, array); + error = testCase.expectedError; + wtu.glErrorShouldBe(gl, error, msg); +} + + +// Start checking. + +debug(""); +debug("Checking TexParameter: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: 0x0DE0, // GL_TEXTURE_1D + pname: gl.TEXTURE_WRAP_T, + param: gl.REPEAT, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + param: 0x2900, // GL_CLAMP + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + param: gl.REPEAT, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x813A, // GL_TEXTURE_MIN_LOD + param: 0, + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x8E42, // GL_TEXTURE_SWIZZLE_R + param: 0x1903, // GL_RED + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: 0x8072, // GL_TEXTURE_WRAP_R + param: 0x2900, // GL_CLAMP + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexParameter(testCases[ii]); +} + +debug(""); +debug("Checking GetTexParameter: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: 0x0DE0, // GL_TEXTURE_1D + pname: gl.TEXTURE_WRAP_T, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + pname: gl.TEXTURE_WRAP_T, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x813A, // GL_TEXTURE_MIN_LOD + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + pname: 0x8E42, // GL_TEXTURE_SWIZZLE_R + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testGetTexParameter(testCases[ii]); +} + +debug(""); +debug("Checking TexImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +var testCases = [ + { target: 0x8064, // GL_PROXY_TEXTURE_2D + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + internalFormat: 0x1903, // GL_RED + border: 0, + format: 0x1903, // GL_RED + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_VALUE, gl.INVALID_OPERATION] }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 1, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_VALUE }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGB, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_ENUM } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage2D(testCases[ii]); +} + +debug(""); +debug("Checking TexSubImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion < 2) { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + format: 0x1903, // GL_RED + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_OPERATION] }, + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.BYTE, + expectedError: [gl.INVALID_ENUM, gl.INVALID_OPERATION] } + ]); +} else { + testCases = testCases.concat([ + { target: gl.TEXTURE_2D, + format: gl.RED, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexSubImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTexImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +var colorBuffer = null; +var fbo = null; + +shouldBeNonNull("fbo = gl.createFramebuffer()"); +gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); +shouldBeNonNull("colorBuffer = gl.createRenderbuffer()"); +gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer); +gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +testCases = [ + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: 0x8054, // GL_RGB16 + border: 0, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + border: 1, + expectedError: gl.INVALID_VALUE }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + border: 0, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGB, + border: 0, + expectedError: gl.NO_ERROR } +]; + +if (contextVersion > 1) { + testCases = testCases.concat([ + { target: gl.TEXTURE_3D, + colorBufferFormat: gl.RGB5_A1, + internalFormat: gl.RGBA, + border: 0, + expectedError: gl.INVALID_ENUM } + ]); +} + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyTexImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTexSubImage2D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB5_A1, + internalFormat: gl.RGBA, + expectedError: gl.NO_ERROR }, + { target: gl.TEXTURE_2D, + colorBufferFormat: gl.RGB565, + internalFormat: gl.RGBA, + expectedError: gl.INVALID_OPERATION } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyTexSubImage2D(testCases[ii]); +} + +debug(""); +debug("Checking CopyTex{Sub}Image2D: copy from WebGL internal framebuffer"); + +testCases = [ + { contextAlpha: true, + internalFormat: gl.RGBA, + subImage: false, + expectedError: gl.NO_ERROR }, + { contextAlpha: false, + internalFormat: gl.RGBA, + subImage: false, + expectedError: gl.INVALID_OPERATION }, + { contextAlpha: true, + internalFormat: gl.RGBA, + subImage: true, + expectedError: gl.NO_ERROR }, + { contextAlpha: false, + internalFormat: gl.RGBA, + subImage: true, + expectedError: gl.INVALID_OPERATION } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testCopyFromInternalFBO(testCases[ii]); +} + +if (contextVersion > 1) { +// Create new texture for testing api of WebGL 2.0. +shouldBeNonNull("tex = gl.createTexture()"); +gl.bindTexture(gl.TEXTURE_3D, tex); +wtu.glErrorShouldBe(gl, gl.NO_ERROR); + +debug(""); +debug("Checking TexImage3D: a set of inputs that are valid in GL but invalid in WebGL"); + +var testCases = [ + { target: 0x8070, // GL_PROXY_TEXTURE_3D + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGB, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_OPERATION }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.BYTE, + expectedError: gl.INVALID_OPERATION}, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage3D(testCases[ii]); +} + +debug(""); +debug("Checking TexImage3D: bad target, internalformats, formats, types"); + +var testCases = [ + { target: gl.TEXTURE_2D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RG, + border: 0, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: [gl.INVALID_VALUE, gl.INVALID_OPERATION]}, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RG8, + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + internalFormat: gl.RGBA, + border: 0, + format: gl.RGBA, + type: gl.INT, + expectedError: gl.INVALID_OPERATION}, +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexImage3D(testCases[ii]); +} + +debug(""); +debug("Checking TexSubImage3D: a set of inputs that are valid in GL but invalid in WebGL"); + +testCases = [ + { target: gl.TEXTURE_3D, + format: 0x80E0, // GL_BGR + type: gl.UNSIGNED_BYTE, + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: 0x8032, // GL_UNSIGNED_BYTE_3_3_2 + expectedError: gl.INVALID_ENUM }, + { target: gl.TEXTURE_3D, + format: gl.RGBA, + type: gl.UNSIGNED_BYTE, + expectedError: gl.NO_ERROR } +]; + +for (var ii = 0; ii < testCases.length; ++ii) { + testTexSubImage3D(testCases[ii]); +} + +} + +var successfullyParsed = true; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js b/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js new file mode 100644 index 0000000000..a80da8023d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/texture-corner-case-videos.js @@ -0,0 +1,299 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This block needs to be outside the onload handler in order for this +// test to run reliably in WebKit's test harness (at least the +// Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 +initTestingHarness(); + +var old = debug; +var debug = function(msg) { + bufferedLogToConsole(msg); + old(msg); +}; + +function generateTest(desc, + internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion, + videos) { + var wtu = WebGLTestUtils; + var tiu = TexImageUtils; + var gl = null; + var c2d = null; + var successfullyParsed = false; + var redColor = [255, 0, 0]; + var greenColor = [0, 255, 0]; + var currentTolerance = 0; + + function init() + { + description(desc + ' (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); + + // Set the default context version while still allowing the webglVersion URL query string to override it. + wtu.setDefault3DContextVersion(defaultContextVersion); + gl = wtu.create3DContext("example"); + + // Subsume 2D canvas tests into this test case since they usually go down similar code paths and + // these tests are usually already set up to run with hardware accelerated video decoding. + c2d = document.getElementById("c2d").getContext("2d"); + + if (!prologue(gl)) { + finishTest(); + return; + } + + switch (gl[pixelFormat]) { + case gl.RED: + case gl.RED_INTEGER: + greenColor = [0, 0, 0]; + break; + default: + break; + } + + gl.clearColor(0,0,0,1); + gl.clearDepth(1); + + runAllTests(); + } + + function runOneIteration(videoElement, useTexSubImage2D, flipY, topColor, bottomColor, sourceSubRectangle, program, bindingTarget) + { + sourceSubRectangleString = ''; + if (sourceSubRectangle) { + sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; + } + debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + + ' with flipY=' + flipY + ' bindingTarget=' + + (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + + sourceSubRectangleString); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + // Disable any writes to the alpha channel + gl.colorMask(1, 1, 1, 0); + var texture = gl.createTexture(); + // Bind the texture to texture unit 0 + gl.bindTexture(bindingTarget, texture); + // Set up texture parameters + gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // Set up pixel store parameters + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + var targets = [gl.TEXTURE_2D]; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; + } + // Handle the source sub-rectangle if specified (WebGL 2.0 only) + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); + } + // Upload the videoElement into the texture + for (var tt = 0; tt < targets.length; ++tt) { + if (sourceSubRectangle) { + // Initialize the texture to black first + if (useTexSubImage2D) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], + sourceSubRectangle[2], sourceSubRectangle[3], 0, + gl[pixelFormat], gl[pixelType], videoElement); + } + } else { + // Initialize the texture to black first + if (useTexSubImage2D) { + var width = videoElement.videoWidth; + var height = videoElement.videoHeight; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // cube map texture must be square. + width = Math.max(width, height); + height = width; + } + gl.texImage2D(targets[tt], 0, gl[internalFormat], + width, height, 0, + gl[pixelFormat], gl[pixelType], null); + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], videoElement); + } + } + } + + if (sourceSubRectangle) { + gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); + gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); + } + + var c = document.createElement("canvas"); + c.width = 16; + c.height = 16; + c.style.border = "1px solid black"; + var ctx = c.getContext("2d"); + ctx.drawImage(videoElement, 0, 0, 16, 16); + document.body.appendChild(c); + + var loc; + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + loc = gl.getUniformLocation(program, "face"); + } + + for (var tt = 0; tt < targets.length; ++tt) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + gl.uniform1i(loc, targets[tt]); + } + // Draw the triangles + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + // Check a few pixels near the top and bottom and make sure they have + // the right color. + var tolerance = currentTolerance; + debug("Checking lower left corner"); + wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + + // Expose bug http://crbug.com/733172. + if (sourceSubRectangle) { + // Skip sub-rectangle tests for cube map textures for the moment. + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + continue; + } + gl.texSubImage2D(targets[tt], 0, 0, 0, + sourceSubRectangle[2], sourceSubRectangle[3], + gl[pixelFormat], gl[pixelType], videoElement); + } else { + gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + } + } + + function runCanvas2DTest(videoElement, topColor, bottomColor) + { + debug('Testing with 2D canvas'); + + var canvas = c2d.canvas; + + // Draw the video to the 2D canvas context. + c2d.drawImage(videoElement, 0, 0, canvas.width, canvas.height); + + // Check a few pixels near the top and bottom and make sure they have + // the right color. + // Origin is upper left in 2D canvas context. + var tolerance = currentTolerance; + debug("Checking lower left corner"); + wtu.checkCanvasRect(c2d, 4, canvas.height - 8, 2, 2, bottomColor, + "shouldBe " + bottomColor, tolerance); + debug("Checking upper left corner"); + wtu.checkCanvasRect(c2d, 4, 4, 2, 2, topColor, + "shouldBe " + topColor, tolerance); + } + + function runAllTests() + { + var cases = [ + { sub: false, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: false, flipY: false, topColor: greenColor, bottomColor: redColor }, + { sub: true, flipY: true, topColor: redColor, bottomColor: greenColor }, + { sub: true, flipY: false, topColor: greenColor, bottomColor: redColor }, + ]; + + function runTexImageTest(bindingTarget) { + var program; + if (bindingTarget == gl.TEXTURE_2D) { + program = tiu.setupTexturedQuad(gl, internalFormat); + } else { + program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); + } + + return new Promise(function(resolve, reject) { + var videoNdx = 0; + var video; + function runNextVideo() { + if (video) { + video.pause(); + } + + if (videoNdx == videos.length) { + resolve("SUCCESS"); + return; + } + + var info = videos[videoNdx++]; + debug(""); + debug("testing: " + info.comment); + debug("video type: " + info.type); + // Default to tolerance of 5. + currentTolerance = info.tolerance || 5; + debug("tolerance: " + currentTolerance); + video = document.createElement("video"); + video.muted = true; + var canPlay = true; + if (!video.canPlayType) { + testFailed("video.canPlayType required method missing"); + runNextVideo(); + return; + } + + if(!video.canPlayType(info.type).replace(/no/, '')) { + debug(info.type + " unsupported; skipping test"); + runNextVideo(); + return; + }; + + document.body.appendChild(video); + video.type = info.type; + video.src = info.src; + wtu.startPlayingAndWaitForVideo(video, runTest); + } + function runTest() { + for (var i in cases) { + if (bindingTarget == gl.TEXTURE_CUBE_MAP) { + // Cube map texture must be square but video is not square. + if (!cases[i].sub) { + break; + } + // Skip sub-rectangle tests for cube map textures for the moment. + if (cases[i].sourceSubRectangle) { + break; + } + } + runOneIteration(video, cases[i].sub, cases[i].flipY, + cases[i].topColor, cases[i].bottomColor, + cases[i].sourceSubRectangle, + program, bindingTarget); + } + runCanvas2DTest(video, redColor, greenColor); + runNextVideo(); + } + runNextVideo(); + }); + } + + runTexImageTest(gl.TEXTURE_2D).then(function(val) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); + finishTest(); + }); + } + + return init; +} diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js new file mode 100644 index 0000000000..2e1b79a677 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-test-cases.js @@ -0,0 +1,73 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// The "name" attribute is a concession to browsers which don't +// implement the "name" property on function objects. +var testCases = + [ {name: "Float32Array", + unsigned: false, + integral: false, + elementSizeInBytes: 4, + testValues: [ -500.5, 500.5 ], + expectedValues: [ -500.5, 500.5 ] + }, + {name: "Float64Array", + unsigned: false, + integral: false, + elementSizeInBytes: 8, + testValues: [ -500.5, 500.5 ], + expectedValues: [ -500.5, 500.5 ] + }, + {name: "Int8Array", + unsigned: false, + integral: true, + elementSizeInBytes: 1, + testValues: [ -128, 127, -129, 128 ], + expectedValues: [ -128, 127, 127, -128 ] + }, + {name: "Int16Array", + unsigned: false, + integral: true, + elementSizeInBytes: 2, + testValues: [ -32768, 32767, -32769, 32768 ], + expectedValues: [ -32768, 32767, 32767, -32768 ] + }, + {name: "Int32Array", + unsigned: false, + integral: true, + elementSizeInBytes: 4, + testValues: [ -2147483648, 2147483647, -2147483649, 2147483648 ], + expectedValues: [ -2147483648, 2147483647, 2147483647, -2147483648 ] + }, + {name: "Uint8Array", + unsigned: true, + integral: true, + elementSizeInBytes: 1, + testValues: [ 0, 255, -1, 256 ], + expectedValues: [ 0, 255, 255, 0 ] + }, + {name: "Uint8ClampedArray", + unsigned: true, + integral: true, + elementSizeInBytes: 1, + testValues: [ 0, 255, -1, 256 ], + expectedValues: [ 0, 255, 0, 255 ] + }, + {name: "Uint16Array", + unsigned: true, + integral: true, + elementSizeInBytes: 2, + testValues: [ 0, 65535, -1, 65536 ], + expectedValues: [ 0, 65535, 65535, 0 ] + }, + {name: "Uint32Array", + unsigned: true, + integral: true, + elementSizeInBytes: 4, + testValues: [ 0, 4294967295, -1, 4294967296 ], + expectedValues: [ 0, 4294967295, 4294967295, 0 ] + } + ]; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js new file mode 100644 index 0000000000..c361a0e8a2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/typed-array-worker.js @@ -0,0 +1,72 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +function constructTypedArray(type, data) { + if (type == 'Int8Array') { + return new Int8Array(data); + } else if (type == 'Uint8Array') { + return new Uint8Array(data); + } else if (type == 'Uint8ClampedArray') { + return new Uint8ClampedArray(data); + } else if (type == 'Int16Array') { + return new Int16Array(data); + } else if (type == 'Uint16Array') { + return new Uint16Array(data); + } else if (type == 'Int32Array') { + return new Int32Array(data); + } else if (type == 'Uint32Array') { + return new Uint32Array(data); + } else if (type == 'Float32Array') { + return new Float32Array(data); + } else if (type == 'Float64Array') { + return new Float64Array(data); + } +} + +function constructDataView(subType, elementSizeInBytes, data) { + var setter = "set" + subType; + var byteOffset = 0; + var buffer = new ArrayBuffer(elementSizeInBytes * data.length); + var dataView = new DataView(buffer); + for (var ii = 0; ii < data.length; ++ii) { + dataView[setter](byteOffset, data[ii]); + byteOffset += elementSizeInBytes; + } + return dataView; +} + +onmessage = function(event) { + var message = event.data; + if (message.command == 'copy' || + message.command == 'transfer' || + message.command == 'copyBuffer' || + message.command == 'transferBuffer') { + var view; + if (message.type != 'DataView') { + view = constructTypedArray(message.type, message.data); + } else { + view = constructDataView(message.subType, message.elementSizeInBytes, message.data); + } + var valueToSend; + if (message.command == 'copy' || + message.command == 'transfer') { + valueToSend = view; + } else { + valueToSend = view.buffer; + } + var transferablesToSend = undefined; + if (message.command == 'transfer' || + message.command == 'transferBuffer') { + transferablesToSend = [ view.buffer ]; + } + postMessage(valueToSend, transferablesToSend); + } else if (message.command == 'pong') { + postMessage(message.data, message.transferables); + } else if (message.command == 'ignore') { + } else { + postMessage('error: unknown message'); + } +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js new file mode 100644 index 0000000000..6fad5520f2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-compressed-texture-size-limit.js @@ -0,0 +1,226 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +'use strict'; + +var runCompressedTextureSizeLimitTest = function(maxArrayBufferSizeBytes, positiveCubeMapMaxSize) { + + function numLevelsFromSize(size) { + var levels = 0; + while ((size >> levels) > 0) { + ++levels; + } + return levels; + } + + // More formats can be added here when more texture compression extensions are enabled in WebGL. + var validFormats = { + COMPRESSED_RGB_S3TC_DXT1_EXT : 0x83F0, + COMPRESSED_RGBA_S3TC_DXT1_EXT : 0x83F1, + COMPRESSED_RGBA_S3TC_DXT3_EXT : 0x83F2, + COMPRESSED_RGBA_S3TC_DXT5_EXT : 0x83F3, + }; + + // format specific restrictions for COMPRESSED_RGB_S3TC_DXT1_EXT and COMPRESSED_RGBA_S3TC_DXT1_EXT + // on the byteLength of the ArrayBufferView, pixels + function func1 (width, height) + { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; + } + + // format specific restrictions for COMPRESSED_RGBA_S3TC_DXT3_EXT and COMPRESSED_RGBA_S3TC_DXT5_EXT + // on the byteLength of the ArrayBufferView, pixels + function func2 (width, height) + { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; + } + + var wtu = WebGLTestUtils; + var gl = wtu.create3DContext("example"); + var tests = [ + // More tests can be added here when more texture compression extensions are enabled in WebGL. + // Level 0 image width and height must be a multiple of the sizeStep. + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGB_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, + ]; + + // Note: We expressly only use 2 textures because first a texture will be defined + // using all mip levels of 1 format, then for a moment it will have mixed formats which + // may uncover bugs. + var targets = [ + { target: gl.TEXTURE_2D, + maxSize: gl.getParameter(gl.MAX_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [gl.TEXTURE_2D] + }, + { target: gl.TEXTURE_CUBE_MAP, + maxSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [ + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z + ] + } + ]; + + function getSharedArrayBufferSize() { + var sharedArrayBufferSize = 0; + for (var tt = 0; tt < tests.length; ++tt) { + var test = tests[tt]; + for (var trg = 0; trg < targets.length; ++trg) { + var t = targets[trg]; + var bufferSizeNeeded; + if (t.target === gl.TEXTURE_CUBE_MAP) { + var positiveTestSize = Math.min(2048, t.maxSize); + bufferSizeNeeded = test.func(positiveTestSize, positiveTestSize); + } else { + bufferSizeNeeded = test.func(t.maxSize, test.sizeStep); + } + if (bufferSizeNeeded > sharedArrayBufferSize) { + sharedArrayBufferSize = bufferSizeNeeded; + } + bufferSizeNeeded = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // ArrayBuffers can be at most 4GB (minus 1 byte). + if (bufferSizeNeeded > sharedArrayBufferSize && bufferSizeNeeded <= maxArrayBufferSizeBytes) { + sharedArrayBufferSize = bufferSizeNeeded; + } + } + } + return sharedArrayBufferSize; + } + + // Share an ArrayBuffer among tests to avoid too many large allocations + var sharedArrayBuffer = new ArrayBuffer(getSharedArrayBufferSize()); + + gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + + var trg = 0; + var tt = 0; + runNextTest(); + + function runNextTest() { + var t = targets[trg]; + + if (tt == 0) { + var tex = t.tex; + gl.bindTexture(t.target, tex); + + debug(""); + debug("max size for " + wtu.glEnumToString(gl, t.target) + ": " + t.maxSize); + } + + var test = tests[tt]; + testFormatType(t, test); + ++tt; + if (tt == tests.length) { + tt = 0; + ++trg; + if (trg == targets.length) { + finishTest(); + return; + } + } + wtu.dispatchPromise(runNextTest); + } + + function testFormatType(t, test) { + var positiveTestSize = t.maxSize; + var positiveTestOtherDimension = test.sizeStep; + if (t.target === gl.TEXTURE_CUBE_MAP) { + // Can't always test the maximum size since that can cause OOM: + positiveTestSize = Math.min(positiveCubeMapMaxSize, t.maxSize); + // Cube map textures need to be square: + positiveTestOtherDimension = positiveTestSize; + } + var positiveTestLevels = numLevelsFromSize(positiveTestSize); + var numLevels = numLevelsFromSize(t.maxSize); + debug(""); + debug("num levels: " + numLevels + ", levels used in positive test: " + positiveTestLevels); + + debug(""); + + // Query the extension and store globally so shouldBe can access it + var ext = wtu.getExtensionWithKnownPrefixes(gl, test.extension); + if (ext) { + + testPassed("Successfully enabled " + test.extension + " extension"); + + for (var j = 0; j < t.targets.length; ++j) { + var target = t.targets[j]; + debug(""); + debug(wtu.glEnumToString(gl, target) + " " + wtu.glEnumToString(ext, test.format)); + + // positive test + var size = positiveTestSize; + var otherDimension = positiveTestOtherDimension; + for (var i = 0; i < positiveTestLevels; i++) { + var pixels = new test.dataType(sharedArrayBuffer, 0, test.func(size, otherDimension)); + gl.compressedTexImage2D(target, i, test.format, size, otherDimension, 0, pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture should generate NO_ERROR." + + "level is " + i + ", size is " + size + "x" + otherDimension); + size /= 2; + otherDimension /= 2; + if (otherDimension < 1) { + otherDimension = 1; + } + } + + var numLevels = numLevelsFromSize(t.maxSize); + + // out of bounds tests + + // width or height out of bounds + if (t.target != gl.TEXTURE_CUBE_MAP) { + // only width out of bounds + var wideAndShortDataSize = test.func(t.maxSize + test.sizeStep, test.sizeStep); + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, wideAndShortDataSize); + gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (test.sizeStep)); + + // only height out of bounds + var narrowAndTallDataSize = test.func(test.sizeStep, t.maxSize + test.sizeStep); + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, narrowAndTallDataSize); + gl.compressedTexImage2D(target, 0, test.format, test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "height out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); + } + + // both width and height out of the maximum bounds simultaneously + var squareDataSize = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // this check assumes that each element is 1 byte + if (squareDataSize > sharedArrayBuffer.byteLength) { + testPassed("Unable to test square texture larger than maximum size due to ArrayBuffer size limitations -- this is legal"); + } else { + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, squareDataSize); + gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width and height out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); + } + + // level out of bounds + var pixelsNegativeTest2 = new test.dataType(sharedArrayBuffer, 0, test.func(256, 256)); + gl.compressedTexImage2D(target, numLevels, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "level out of bounds: should generate INVALID_VALUE." + + " level is " + numLevels + ", size is 256x256"); + //width and height out of bounds for specified level + gl.compressedTexImage2D(target, numLevels - 1, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width or height out of bounds for specified level: should generate INVALID_VALUE." + + " level is " + (numLevels - 1) + ", size is 256x256"); + } + } + else { + testPassed("No " + test.extension + " extension support -- this is legal"); + } + } + +}; diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js new file mode 100644 index 0000000000..ebd0c7ba68 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-draw-buffers-utils.js @@ -0,0 +1,69 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ + +// This file contains utilities shared between tests for the WEBGL_draw_buffers extension and multiple draw buffers functionality in WebGL 2.0. + +'use strict'; + +var WebGLDrawBuffersUtils = function(gl, ext) { + + var getMaxUsableColorAttachments = function() { + var maxDrawingBuffers; + var maxColorAttachments; + if (ext) { + // EXT_draw_buffers + maxDrawingBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + maxColorAttachments = gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL); + } else { + // WebGL 2.0 + maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS); + maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + } + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + return Math.min(maxDrawingBuffers, maxColorAttachments, maxUniformVectors); + }; + + var makeColorAttachmentArray = function(size) { + var array = [] + for (var ii = 0; ii < size; ++ii) { + array.push(gl.COLOR_ATTACHMENT0 + ii); + } + return array; + } + + var checkProgram = wtu.setupTexturedQuad(gl); + + var checkAttachmentsForColorFn = function(attachments, colorFn) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.useProgram(checkProgram); + attachments.forEach(function(attachment, index) { + gl.bindTexture(gl.TEXTURE_2D, attachment.texture); + wtu.clearAndDrawUnitQuad(gl); + var expectedColor = colorFn(attachment, index); + var tolerance = 0; + expectedColor.forEach(function(v) { + if (v != 0 && v != 255) { + tolerance = 8; + } + }); + wtu.checkCanvas(gl, expectedColor, "attachment " + index + " should be " + expectedColor.toString(), tolerance); + }); + debug(""); + }; + + var checkAttachmentsForColor = function(attachments, color) { + checkAttachmentsForColorFn(attachments, function(attachment, index) { + return color || attachment.color; + }); + }; + + return { + getMaxUsableColorAttachments: getMaxUsableColorAttachments, + makeColorAttachmentArray: makeColorAttachmentArray, + checkAttachmentsForColorFn: checkAttachmentsForColorFn, + checkAttachmentsForColor: checkAttachmentsForColor + }; +}; -- cgit v1.2.3