diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/reading')
6 files changed, 1503 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/00_test_list.txt new file mode 100644 index 0000000000..d5fe8b664e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/00_test_list.txt @@ -0,0 +1,5 @@ +--min-version 2.0.1 format-r11f-g11f-b10f.html +read-pixels-from-fbo-test.html +--min-version 2.0.1 read-pixels-from-rgb8-into-pbo-bug.html +read-pixels-into-pixel-pack-buffer.html +read-pixels-pack-parameters.html diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/format-r11f-g11f-b10f.html b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/format-r11f-g11f-b10f.html new file mode 100644 index 0000000000..5534a6884f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/format-r11f-g11f-b10f.html @@ -0,0 +1,266 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Test Format R11F_G11F_B10F</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="20" height="20"> </canvas> +<script id="vshader" type="x-shader/x-vertex"> +attribute vec2 pos; +attribute vec2 texCoord0; +varying vec2 texCoord; + +void main() { + gl_Position = vec4(pos, 0.0, 1.0); + texCoord = texCoord0; +} +</script> + +<script id="fshader" type="x-shader/x-fragment"> +precision mediump float; +uniform vec3 u_color; +uniform vec3 u_tol; +uniform sampler2D u_tex; +varying vec2 texCoord; + +void main() { + vec4 sample = texture2D(u_tex, texCoord); + vec3 rgb = sample.xyz; + if (abs(rgb[0] - u_color[0]) > u_tol[0] || + abs(rgb[1] - u_color[1]) > u_tol[1] || + abs(rgb[2] - u_color[2]) > u_tol[2]) { + gl_FragColor = vec4(1, 0, 0, 1); + } else { + gl_FragColor = vec4(0, 1, 0, 1); + } +} +</script> +<script> +"use strict"; +description("This tests format R11F_G11F_B10F works as expected"); +debug("MacOSX driver bug. See https://github.com/KhronosGroup/WebGL/issues/1832"); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, undefined, 2); + +var testValues = [100, 1000, 2047, 2500, 4095, 5000, + 8191, 8192, 10000, 16383, 16384]; + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + if (gl.getExtension("EXT_color_buffer_float")) { + testPassed("Extension EXT_color_buffer_float is available"); + + testRenderbufferReadback(4, 4); + testTextureReadback(4, 4); + testTextureSampling(4, 4); + } else { + testPassed("Extension EXT_color_buffer_float is unavailable - this is legal"); + } +} + +function setupColor(testR, testG, testB, value) { + var data = new Float32Array(4); + data[0] = testR ? value : 0; + data[1] = testG ? value : 0; + data[2] = testB ? value : 0; + data[3] = 1; // Doesn't really matter for RGB formats. + return data; +} + +// The definition of <Unsinged 11-Bit Floating-Point Number> in GLES 3.0.4: +// https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf#nameddest=section-2.1.3 +// The definition of <Unsinged 10-Bit Floating-Point Number> in GLES 3.0.4: +// https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.4.pdf#nameddest=section-2.1.4 +function setTolerance (testR, testG, testB, value) { + var tol = new Float32Array(3); + var exponent; + if (value < Math.pow(2, -14)) { + exponent = -14; + } else { + exponent = Math.floor(Math.log(value) / Math.LN2); + } + var tol11F = Math.pow(2, exponent) / 64; + var tol10F = Math.pow(2, exponent) / 32; + tol[0] = testR ? tol11F : 0; + tol[1] = testG ? tol11F : 0; + tol[2] = testB ? tol10F : 0; + return tol; +} + +function clearAndVerifyColor(width, height, testR, testG, testB, value) { + var data = setupColor(testR, testG, testB, value); + var tol = setTolerance(testR, testG, testB, value); + gl.clearBufferfv(gl.COLOR, 0, data); + var buffer = new Float32Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.FLOAT, buffer); + for (var ii = 0; ii < width * height; ++ii) { + var pixel = [buffer[ii * 4], buffer[ii * 4 + 1], buffer[ii * 4 + 2], buffer[ii * 4 + 3]]; + if (isNaN(pixel[0]) || isNaN(pixel[1]) || isNaN(pixel[2]) || + Math.abs(pixel[0] - data[0]) > tol[0] || + Math.abs(pixel[1] - data[1]) > tol[1] || + Math.abs(pixel[2] - data[2]) > tol[2]) { + testFailed("ReadPixels " + ii + " : got [" + pixel + "], expected [" + data + "], tol [" + tol + "]"); + return; + } + } + testPassed("ReadPixels success : [" + data + "]"); +} + +function clearDrawAndVerifyColor(fbo, program, testR, testG, testB, value) { + var data = setupColor(testR, testG, testB, value); + var tol = setTolerance(testR, testG, testB, value); + debug("Testing : [" + data + "] with tolerance = [" + tol + "]"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.clearBufferfv(gl.COLOR, 0, data); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.clearColor(0, 0, 0,1); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.uniform3fv(program.colorPos, data.slice(0, 3)); + gl.uniform3fv(program.tolPos, tol); + + wtu.drawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "Should pass (green color instead of red)"); +} + + +function testReadPixelsFromColorChannelsWithVariousValues(width, height) { + debug("Testing R channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearAndVerifyColor(width, height, true, false, false, testValues[ii]); + } + debug("Testing G channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearAndVerifyColor(width, height, false, true, false, testValues[ii]); + } + debug("Testing B channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearAndVerifyColor(width, height, false, false, true, testValues[ii]); + } +} + +function testSampleTextureFromColorChannelsWithVariousValues(fbo, program) { + debug("Testing R channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearDrawAndVerifyColor(fbo, program, true, false, false, testValues[ii]); + } + debug("Testing G channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearDrawAndVerifyColor(fbo, program, false, true, false, testValues[ii]); + } + debug("Testing B channel"); + for (var ii = 0; ii < testValues.length; ++ii) { + clearDrawAndVerifyColor(fbo, program, false, false, true, testValues[ii]); + } +} + +function testRenderbufferReadback(width, height) { + debug(""); + debug("Checking clearing and readback of a color image of renderbuffer with R11F_G11F_B10F format."); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.R11F_G11F_B10F, width, height); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer); + shouldBe("gl.FRAMEBUFFER_COMPLETE", "gl.checkFramebufferStatus(gl.FRAMEBUFFER)"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with renderbuffer should succeed."); + + testReadPixelsFromColorChannelsWithVariousValues(width, height); + + gl.deleteFramebuffer(fbo); + gl.deleteRenderbuffer(renderbuffer); +} + +function testTextureReadback(width, height) { + debug(""); + debug("Checking clearing and readback of a color image of texture with R11F_G11F_B10F format."); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.R11F_G11F_B10F, width, height, 0, gl.RGB, gl.FLOAT, null); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe("gl.FRAMEBUFFER_COMPLETE", "gl.checkFramebufferStatus(gl.FRAMEBUFFER)"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + + testReadPixelsFromColorChannelsWithVariousValues(width, height); + + gl.deleteFramebuffer(fbo); + gl.deleteTexture(tex); +} + +function setupProgram() { + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["pos", "texCoord0"]); + if (!program) + return null; + program.colorPos = gl.getUniformLocation(program, "u_color"); + program.tolPos = gl.getUniformLocation(program, "u_tol"); + var texPos = gl.getUniformLocation(program, "u_tex"); + program.buffers = wtu.setupUnitQuad(gl, 0, 1); + if (!program.colorPos || !program.tolPos || !texPos || program.buffers.length == 0) { + gl.deleteProgram(program); + return null; + } + gl.useProgram(program); + gl.uniform1i(texPos, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup program should succeed."); + return program; +} + +function testTextureSampling(width, height) { + debug(""); + debug("Checking sampling of a texture with R11_G11F_B10F format"); + + var program = setupProgram(); + if (!program) { + testFailed("Failed to setup program"); + return; + } + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.R11F_G11F_B10F, width, height, 0, gl.RGB, gl.FLOAT, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + shouldBe("gl.FRAMEBUFFER_COMPLETE", "gl.checkFramebufferStatus(gl.FRAMEBUFFER)"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + + testSampleTextureFromColorChannelsWithVariousValues(fbo, program); + + gl.deleteTexture(tex); + gl.deleteFramebuffer(fbo); + gl.deleteProgram(program); +} + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error from tests."); +var successfullyParsed = true; + +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-fbo-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-fbo-test.html new file mode 100644 index 0000000000..c06e9988ed --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-fbo-test.html @@ -0,0 +1,642 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL 2 ReadPixels Test.</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"> </script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("Checks that ReadPixels from a fbo works as expected."); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext(undefined, undefined, 2); +gl.pixelStorei(gl.PACK_ALIGNMENT, 1); + +function getChannelCount(format) { + switch (format) { + case gl.RED: + case gl.RED_INTEGER: + case gl.ALPHA: + case gl.LUMINANCE: + return 1; + case gl.RB: + case gl.RB_INTEGER: + case gl.LUMINANCE_ALPHA: + return 2; + case gl.RGB: + case gl.RGB_INTEGER: + return 3; + case gl.RGBA: + case gl.RGBA_INTEGER: + return 4; + default: + return 0; + } +} + +function getUnpackInfo(type) { + switch (type) { + case gl.UNSIGNED_SHORT_5_6_5: + return {bitsCount: [5, 6, 5], isReverse: false}; + case gl.UNSIGNED_SHORT_4_4_4_4: + return {bitsCount: [4, 4, 4, 4], isReverse: false}; + case gl.UNSIGNED_SHORT_5_5_5_1: + return {bitsCount: [5, 5, 5, 1], isReverse: false}; + case gl.UNSIGNED_INT_2_10_10_10_REV: + return {bitsCount: [2, 10, 10, 10], isReverse: true}; + case gl.UNSIGNED_INT_10F_11F_11F_REV: + return {bitsCount: [10, 11, 11], isReverse: true}; + case gl.UNSIGNED_INT_5_9_9_9_REV: + return {bitsCount: [5, 9, 9, 9], isReverse: true}; + default: + return null; + } +} + +// bitsCount is an array contains bit count for each component. +function unpack(value, channelCount, bitsCount, isReverse) { + var result = new Array(channelCount); + + var accumBitsCount = 0; + for (var i = channelCount - 1; i >= 0; --i) { + var currentChannel = isReverse ? (channelCount - i - 1) : i; + var mask = 0xFFFFFFFF >>> (32 - bitsCount[i]); + result[currentChannel] = ((value >> accumBitsCount) & mask); + accumBitsCount += bitsCount[i]; + } + + return result; +} + +function getColor(buf, index, readFormat, readType) { + var channelCount = getChannelCount(readFormat); + var result = new Array(channelCount); + + var unpackInfo = getUnpackInfo(readType); + if (unpackInfo) { + result = unpack(buf[index], channelCount, unpackInfo.bitsCount, unpackInfo.isReverse); + } else { + for (var i = 0; i < channelCount; ++i) { + result[i] = buf[index + i]; + } + } + + return result; +} + +function convertToSRGB(val) { + if (val <= 0) { + return 0; + } else if (val < 0.0031308) { + return 12.92 * val; + } else if (val < 1) { + return 1.055 * Math.pow(val, 0.41666) - 0.055; + } else { + return 1; + } +} + +function denormalizeColor(srcInternalFormat, destType, color) { + var result = color.slice(); + var tol = 0; + + var srcIsNormalized = false; + + switch (srcInternalFormat) { + case gl.R8: + case gl.RG8: + case gl.RGB8: + case gl.RGBA8: + case gl.RGB5_A1: + case gl.SRGB8_ALPHA8: + case gl.RGB10_A2: + srcIsNormalized = true; + tol = 6; + break; + case gl.RGB565: + // RGB565 needs slightly extra tolerance, at least on Google Pixel. crbug.com/682753 + srcIsNormalized = true; + tol = 7; + break; + case gl.RGBA4: + srcIsNormalized = true; + tol = 10; + break; + } + + if (!srcIsNormalized) { + return { color: result, tol: tol }; + } + + if (srcInternalFormat == gl.SRGB8_ALPHA8) { + for (var i = 0; i < 3; ++i) { + result[i] = convertToSRGB(result[i]); + } + } + + switch (destType) { + case gl.UNSIGNED_BYTE: + result = result.map(val => { return val * 0xFF}); + break; + case gl.UNSIGNED_SHORT: + // On Linux NVIDIA, tol of 33 is necessary to pass the test. + tol = 40; + result = result.map(val => { return val * 0xFFFF}); + break; + case gl.UNSIGNED_INT: + tol = 40; + result = result.map(val => { return val * 0xFFFFFFFF}); + break; + case gl.UNSIGNED_SHORT_4_4_4_4: + result = result.map(val => { return val * 0xF}); + break; + case gl.UNSIGNED_SHORT_5_5_5_1: + result[0] = result[0] * 0x1F; + result[1] = result[1] * 0x1F; + result[2] = result[2] * 0x1F; + result[3] = result[3] * 0x1; + break; + case gl.UNSIGNED_SHORT_5_6_5: + result[0] = result[0] * 0x1F; + result[1] = result[1] * 0x3F; + result[2] = result[2] * 0x1F; + break; + case gl.UNSIGNED_INT_2_10_10_10_REV: + tol = 25; + result[0] = result[0] * 0x3FF; + result[1] = result[1] * 0x3FF; + result[2] = result[2] * 0x3FF; + result[3] = result[3] * 0x3; + break; + case gl.UNSIGNED_INT_5_9_9_9_REV: + result[0] = result[0] * 0x1FF; + result[1] = result[1] * 0x1FF; + result[2] = result[2] * 0x1FF; + result[3] = result[3] * 0x1F; + break; + } + + return { color: result, tol: tol }; +} + +function compareColor(buf, index, expectedColor, srcInternalFormat, + srcFormat, srcType, readFormat, readType) { + var srcChannelCount = getChannelCount(srcFormat); + var readChannelCount = getChannelCount(readFormat); + + var color = getColor(buf, index, readFormat, readType); + expectedColor = denormalizeColor(srcInternalFormat, readType, expectedColor); + + var minChannel = Math.min(srcChannelCount, readChannelCount); + for (var i = 0; i < minChannel; ++i) { + if (Math.abs(expectedColor.color[i] - color[i]) > expectedColor.tol) { + testFailed("Expected color = " + expectedColor.color + ", was = " + color); + return false; + } + } + + return true; +} + +var textureTestCases = [ + { + texInternalFormat: 'R8', texFormat: 'RED', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.0, 0.0, 0], + }, + { + texInternalFormat: 'R8UI', texFormat: 'RED_INTEGER', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [250, 0, 0, 0], + }, + { + texInternalFormat: 'R8I', texFormat: 'RED_INTEGER', texType: 'BYTE', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-126, 0, 0, 0], + }, + { + texInternalFormat: 'R16UI', texFormat: 'RED_INTEGER', texType: 'UNSIGNED_SHORT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [30001, 0, 0, 0], + }, + { + texInternalFormat: 'R16I', texFormat: 'RED_INTEGER', texType: 'SHORT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-14189, 0, 0, 0], + }, + { + texInternalFormat: 'R32UI', texFormat: 'RED_INTEGER', texType: 'UNSIGNED_INT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [126726, 0, 0, 0], + }, + { + texInternalFormat: 'R32I', texFormat: 'RED_INTEGER', texType: 'INT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-126726, 0, 0, 0], + }, + + { + texInternalFormat: 'RG8', texFormat: 'RG', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 0.0, 0], + }, + { + texInternalFormat: 'RG8UI', texFormat: 'RG_INTEGER', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [250, 124, 0, 0], + }, + { + texInternalFormat: 'RG8I', texFormat: 'RG_INTEGER', texType: 'BYTE', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-55, 124, 0, 0], + }, + { + texInternalFormat: 'RG16UI', texFormat: 'RG_INTEGER', texType: 'UNSIGNED_SHORT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [30001, 18, 0, 0], + }, + { + texInternalFormat: 'RG16I', texFormat: 'RG_INTEGER', texType: 'SHORT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-14189, 6735, 0, 0], + }, + { + texInternalFormat: 'RG32UI', texFormat: 'RG_INTEGER', texType: 'UNSIGNED_INT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [126726, 1976, 0, 0], + }, + { + texInternalFormat: 'RG32I', texFormat: 'RG_INTEGER', texType: 'INT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-126726, 126726, 0, 0], + }, + + { + texInternalFormat: 'RGB8', texFormat: 'RGB', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 1, 0, 0], + }, + { + texInternalFormat: 'RGB565', texFormat: 'RGB', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 0.2, 0], + }, + { + texInternalFormat: 'RGB565', texFormat: 'RGB', texType: 'UNSIGNED_SHORT_5_6_5', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 0.2, 0], + }, + + { + texInternalFormat: 'RGBA8', texFormat: 'RGBA', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0, 1, 0.7], + }, + { + texInternalFormat: 'SRGB8_ALPHA8', texFormat: 'RGBA', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0, 1, 0.7], + }, + { + texInternalFormat: 'RGB5_A1', texFormat: 'RGBA', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0, 0.7, 1], + }, + { + texInternalFormat: 'RGB5_A1', texFormat: 'RGBA', texType: 'UNSIGNED_SHORT_5_5_5_1', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 1, 0], + }, + { + texInternalFormat: 'RGB5_A1', texFormat: 'RGBA', texType: 'UNSIGNED_INT_2_10_10_10_REV', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 0, 1], + }, + { + texInternalFormat: 'RGBA4', texFormat: 'RGBA', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.5, 0.7, 1, 0], + }, + { + texInternalFormat: 'RGBA4', texFormat: 'RGBA', texType: 'UNSIGNED_SHORT_4_4_4_4', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [1, 0, 0.5, 0.7], + }, + { + texInternalFormat: 'RGBA8UI', texFormat: 'RGBA_INTEGER', texType: 'UNSIGNED_BYTE', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [127, 0, 255, 178], + }, + { + texInternalFormat: 'RGBA8I', texFormat: 'RGBA_INTEGER', texType: 'BYTE', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [-55, 56, 80, 127], + }, + { + texInternalFormat: 'RGB10_A2UI', texFormat: 'RGBA_INTEGER', texType: 'UNSIGNED_INT_2_10_10_10_REV', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [178, 0, 127, 3], + }, + { + texInternalFormat: 'RGBA16UI', texFormat: 'RGBA_INTEGER', texType: 'UNSIGNED_SHORT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [14189, 6735, 0, 19], + }, + { + texInternalFormat: 'RGBA16I', texFormat: 'RGBA_INTEGER', texType: 'SHORT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [14189, -6735, 0, 19], + }, + { + texInternalFormat: 'RGBA32UI', texFormat: 'RGBA_INTEGER', texType: 'UNSIGNED_INT', + readFormat: 'RGBA_INTEGER', readType: 'UNSIGNED_INT', + clearColor: [126726, 6726, 98765, 2015], + }, + { + texInternalFormat: 'RGBA32I', texFormat: 'RGBA_INTEGER', texType: 'INT', + readFormat: 'RGBA_INTEGER', readType: 'INT', + clearColor: [126726, -6726, -98765, 2015], + }, + + { + texInternalFormat: 'RGB10_A2', texFormat: 'RGBA', texType: 'UNSIGNED_INT_2_10_10_10_REV', + readFormat: 'RGBA', readType: 'UNSIGNED_BYTE', + clearColor: [0.7, 0, 0.5, 1], + }, + + // TODO(zmo): add float/half_float test cases with extension supports. +]; + +function getArrayTypeFromReadPixelsType(gl, type) { + switch (type) { + case gl.UNSIGNED_BYTE: + return Uint8Array; + case gl.BYTE: + return Int8Array; + case gl.UNSIGNED_SHORT: + case gl.UNSIGNED_SHORT_5_6_5: + case gl.UNSIGNED_SHORT_4_4_4_4: + case gl.UNSIGNED_SHORT_5_5_5_1: + return Uint16Array; + case gl.SHORT: + return Int16Array; + case gl.UNSIGNED_INT: + case gl.UNSIGNED_INT_2_10_10_10_REV: + case gl.UNSIGNED_INT_10F_11F_11F_REV: + case gl.UNSIGNED_INT_5_9_9_9_REV: + return Uint32Array; + case gl.INT: + return Int32Array; + case gl.HALF_FLOAT: + return Uint16Array; + case gl.FLOAT: + return Float32Array; + default: + return null; + } +} + +function getFormatString(gl, format) { + switch (format) { + case gl.RED: + return 'RED'; + case gl.RED_INTEGER: + return 'RED_INTEGER'; + case gl.RG: + return 'RG'; + case gl.RG_INTEGER: + return 'RG_INTEGER'; + case gl.RGB: + return 'RGB'; + case gl.RGB_INTEGER: + return 'RGB_INTEGER'; + case gl.RGBA: + return 'RGBA'; + case gl.RGBA_INTEGER: + return 'RGBA_INTEGER'; + case gl.LUMINANCE: + return 'LUMINANCE'; + case gl.LUMINANCE_ALPHA: + return 'LUMINANCE_ALPHA'; + case gl.ALPHA: + return 'ALPHA'; + default: + return ''; + }; +} + +function getTypeString(gl, type) { + switch (type) { + case gl.UNSIGNED_BYTE: + return 'UNSIGNED_BYTE'; + case gl.BYTE: + return 'BYTE'; + case gl.UNSIGNED_SHORT: + return 'UNSIGNED_SHORT'; + case gl.SHORT: + return 'SHORT'; + case gl.UNSIGNED_INT: + return 'UNSIGNED_INT'; + case gl.INT: + return 'INT'; + case gl.UNSIGNED_SHORT_5_6_5: + return 'UNSIGNED_SHORT_5_6_5'; + case gl.UNSIGNED_SHORT_5_5_5_1: + return 'UNSIGNED_SHORT_5_5_5_1'; + case gl.UNSIGNED_INT_2_10_10_10_REV: + return 'UNSIGNED_INT_2_10_10_10_REV'; + case gl.UNSIGNED_SHORT_4_4_4_4: + return 'UNSIGNED_SHORT_4_4_4_4'; + case gl.UNSIGNED_INT_10F_11F_11F_REV: + return 'UNSIGNED_INT_10F_11F_11F_REV'; + case gl.UNSIGNED_INT_5_9_9_9_REV: + return 'UNSIGNED_INT_5_9_9_9_REV'; + default: + return ''; + }; +} + +function elementCountPerPixel(gl, readFormat, readType) { + switch (readFormat) { + case gl.RED: + case gl.RED_INTEGER: + case gl.ALPHA: + case gl.LUMINANCE: + return 1; + case gl.RG: + case gl.RG_INTEGER: + case gl.LUMINANCE_ALPHA: + return 2; + case gl.RGB: + case gl.RGB_INTEGER: + switch (readType) { + case gl.UNSIGNED_SHORT_5_6_5: + return 1; + default: + return 3; + } + case gl.RGBA: + case gl.RGBA_INTEGER: + switch (readType) { + case gl.UNSIGNED_SHORT_4_4_4_4: + case gl.UNSIGNED_SHORT_5_5_5_1: + case gl.UNSIGNED_INT_2_10_10_10_REV: + case gl.UNSIGNED_INT_10F_11F_11F_REV: + case gl.UNSIGNED_INT_5_9_9_9_REV: + return 1; + default: + return 4; + } + default: + testFailed("Unexpected read format/type = " + readFormat + "/" + readType); + return 0; + } +} + +function testReadPixels(gl, srcInternalFormat, srcFormat, srcType, + readFormat, readType, expectedColor) { + var arrayType = getArrayTypeFromReadPixelsType(gl, readType); + var buf = new arrayType(width * height * 4); + gl.readPixels(0, 0, width, height, readFormat, readType, buf); + wtu.glErrorShouldBe( + gl, gl.NO_ERROR, "readPixels should generate no error"); + var diffFound = false; + for (var ii = 0; ii < width * height; ++ii) { + var offset = ii * elementCountPerPixel(gl, readFormat, readType); + if (!compareColor(buf, offset, expectedColor, srcInternalFormat, srcFormat, srcType, + readFormat, readType)) { + diffFound = true; + break; + } + } + if (!diffFound) { + testPassed("Color read back as expected"); + } +} + +function clearBuffer(gl, texInternalFormat, clearColor) { + var value; + switch (texInternalFormat) { + case gl.R8UI: + case gl.R16UI: + case gl.R32UI: + case gl.RG8UI: + case gl.RG16UI: + case gl.RG32UI: + case gl.RGBA8UI: + case gl.RGBA16UI: + case gl.RGBA32UI: + case gl.RGB10_A2UI: + value = new Uint32Array(4); + for (var ii = 0; ii < 4; ++ii) + value[ii] = clearColor[ii]; + gl.clearBufferuiv(gl.COLOR, 0, value); + break; + case gl.R8I: + case gl.R16I: + case gl.R32I: + case gl.RG8I: + case gl.RG16I: + case gl.RG32I: + case gl.RGBA8I: + case gl.RGBA16I: + case gl.RGBA32I: + value = new Int32Array(4); + for (var ii = 0; ii < 4; ++ii) + value[ii] = clearColor[ii]; + gl.clearBufferiv(gl.COLOR, 0, value); + break; + default: + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + gl.clear(gl.COLOR_BUFFER_BIT); + break; + } +} + +for (var tt = 0; tt < textureTestCases.length; ++tt) { + var test = textureTestCases[tt]; + debug(""); + debug("ReadPixels from fbo with texture = (" + test.texInternalFormat + + ", " + test.texFormat + ", " + test.texType + + "), format = " + test.readFormat + ", type = " + test.readType); + var width = 2; + var height = 2; + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + var colorImage = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, colorImage); + gl.texImage2D(gl.TEXTURE_2D, 0, gl[test.texInternalFormat], width, height, 0, + gl[test.texFormat], gl[test.texType], null); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.TEXTURE_2D, colorImage, 0); + wtu.glErrorShouldBe( + gl, gl.NO_ERROR, "Setting up fbo should generate no error"); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + debug("fbo is not complete, skip"); + continue; + } + clearBuffer(gl, gl[test.texInternalFormat], test.clearColor); + wtu.glErrorShouldBe( + gl, gl.NO_ERROR, "Clear color should generate no error"); + + var implFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); + var implType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); + var implFormatString = getFormatString(gl, implFormat); + var implTypeString = getTypeString(gl, implType); + + if (gl[test.texInternalFormat] == gl.RGB10_A2) { + // This is a special case where three read format/type are supported. + var readTypes = [gl.UNSIGNED_BYTE, gl.UNSIGNED_INT_2_10_10_10_REV]; + var readTypeStrings = ['UNSIGNED_BYTE', 'UNSIGNED_INT_2_10_10_10_REV']; + if (implFormat == gl.RGBA && implTypeString != '') { + readTypes.push(implType); + readTypeStrings.push(implTypeString); + } else { + testFailed("Unimplemented Implementation dependent color read format/type = " + + implFormat + "/" + implType); + } + for (var rr = 0; rr < readTypes.length; ++rr) { + debug("Special case RGB10_A2, format = RGBA, type = " + readTypeStrings[rr]); + testReadPixels(gl, gl[test.texInternalFormat], gl[test.texFormat], gl[test.texType], + gl.RGBA, readTypes[rr], test.clearColor); + } + } else { + testReadPixels(gl, gl[test.texInternalFormat], gl[test.texFormat], gl[test.texType], + gl[test.readFormat], gl[test.readType], test.clearColor); + + debug("Testing implementation dependent color read format = " + implFormatString + + ", type = " + implTypeString); + if (implFormatString == '') { + testFailed("Invalid IMPLEMENTATION_COLOR_READ_FORMAT = " + implFormat); + continue; + } + if (implTypeString == '') { + testFailed("Invalid IMPLEMENTATION_COLOR_READ_TYPE = " + implType); + continue; + } + testReadPixels(gl, gl[test.texInternalFormat], gl[test.texFormat], gl[test.texType], + implFormat, implType, test.clearColor); + + gl.deleteTexture(colorImage); + gl.deleteFramebuffer(fbo); + } +} + +debug("") +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-rgb8-into-pbo-bug.html b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-rgb8-into-pbo-bug.html new file mode 100644 index 0000000000..b4f91f1d3c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-from-rgb8-into-pbo-bug.html @@ -0,0 +1,85 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL 2 Conformance Test: readPixels from RGB8 Buffer Into Pixel Pack Buffer.</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<canvas id="example" width="4" height="4"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +debug(""); +description("Verifies readPixels from RGB8 buffer into PIXEL_PACK buffer works"); + +debug("On MacOSX with AMD GPUs, the alpha channel is readback as 0 instead of 255"); + +var wtu = WebGLTestUtils; +var pixel = [0, 0, 0, 0]; +var expectedColor = [255, 102, 0, 255]; + +var canvas = document.getElementById("example"); +var gl = wtu.create3DContext(canvas, undefined, 2); + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var width = 4; + var height = 4; + + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB8, width, height); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer); + + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("framebuffer with RGB8 color buffer is incomplete"); + } else { + gl.clearColor(1.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + var pbo = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo); + gl.bufferData(gl.PIXEL_PACK_BUFFER, width * height * 4, gl.STATIC_COPY); + + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, 0); + + var data = new Uint8Array(width * height * 4); + gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data); + + for (var ii = 0; ii < width * height; ++ii) { + if (data[ii * 4] != 255 || + data[ii * 4 + 1] != 0 || + data[ii * 4 + 2] != 0 || + data[ii * 4 + 3] != 255) { + testFailed("Expected in pixel " + ii + ": [255,0,0,255], got: " + + [data[ii * 4], data[ii * 4 + 1], data[ii * 4 + 2], data[ii * 4 + 3]]); + break; + } + } + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Tests should complete without gl errors"); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-into-pixel-pack-buffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-into-pixel-pack-buffer.html new file mode 100644 index 0000000000..7d646a6ecd --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-into-pixel-pack-buffer.html @@ -0,0 +1,152 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL 2 Conformance Test: ReadPixels Into Pixel Pack Buffer.</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<canvas id="example" width="4" height="4"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +function checkFormatAndType() +{ + debug(""); + debug("check format / type"); + var invalidFormat = [gl.DEPTH_COMPONENT, gl.DEPTH_STENCIL, gl.R8, gl.RGBA4, gl.LUMINANCE, gl.LUMINANCE_ALPHA]; + var invalidType = [gl.UNSIGNED_INT_24_8]; + for (var ff = 0; ff < invalidFormat.length; ++ff) { + var format = invalidFormat[ff]; + gl.readPixels(0, 0, 1, 1, format, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Format should not be able to read as " + wtu.glEnumToString(gl, format)); + } + for (var tt = 0; tt < invalidType.length; ++tt) { + var type = invalidType[tt]; + gl.readPixels(0, 0, 1, 1, gl.RGBA, type, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Type should not be able to read as " + wtu.glEnumToString(gl, type)); + } + + debug(""); + debug("check combinations of format and type"); + var combinations = [ + {format: gl.RGBA, type: gl.UNSIGNED_BYTE}, + {format: gl.RGB, type: gl.UNSIGNED_BYTE}, + {format: gl.RGB, type: gl.UNSIGNED_SHORT_5_6_5}, + {format: gl.RGBA, type: gl.UNSIGNED_SHORT_5_5_5_1}, + {format: gl.RGBA, type: gl.UNSIGNED_SHORT_4_4_4_4}, + {format: gl.ALPHA, type: gl.UNSIGNED_BYTE}, + {format: gl.RED, type: gl.UNSIGNED_BYTE}, + {format: gl.RGBA_INTEGER, type: gl.UNSIGNED_INT}, + {format: gl.RGBA_INTEGER, type: gl.INT} + ]; + + var implFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); + var implType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); + for (var tt = 0; tt < combinations.length; ++ tt) { + var info = combinations[tt]; + var format = info.format; + var type = info.type; + gl.readPixels(0, 0, 1, 1, format, type, 0); + // Only two format/type parameter pairs are accepted. GL_RGBA/GL_UNSIGNED_BYTE is always + // accepted on default readbuffer. The other acceptable pair can be discovered by querying + // GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE. + if ((format == gl.RGBA && type == gl.UNSIGNED_BYTE) || (format == implFormat && type == implType)) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "The combination of format/type should be able to read as " + + wtu.glEnumToString(gl, format) + " / " + wtu.glEnumToString(gl, type)); + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The combination of format/type should not be able to read as " + + wtu.glEnumToString(gl, format) + " / " + wtu.glEnumToString(gl, type)); + } + } +} + +function validatePixelPackBufferAndParameters(canvasWidth, canvasHeight) +{ + debug(""); + debug("Validate PIXEL_PACK buffer and readPixels' parameters"); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.pixelStorei(gl.PACK_ALIGNMENT, 1); + + var size = canvasWidth * canvasHeight * 4; + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buffer); + gl.bufferData(gl.PIXEL_PACK_BUFFER, size, gl.STATIC_DRAW); + var array = new Uint8Array(size); + + debug(""); + debug("PIXEL_PACK buffer is bound"); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, array); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should generate INVALID_OPERATION if pixel pack buffer is bound"); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Validate the offset of PIXEL_PACK buffer and buffer size"); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, -1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "offset < 0"); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, size); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "offset > buffer size"); + gl.readPixels(0, 0, canvasWidth + 1, canvasHeight, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "pixel pack buffer is not large enough"); + + debug(""); + debug("Validate the reading area of framebuffer"); + gl.readPixels(-1, -2, canvasWidth, canvasHeight, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading pixels outside of the framebuffer should succeed."); + gl.readPixels(2, 1, canvasWidth, canvasHeight, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading pixels outside of the framebuffer should succeed."); + gl.readPixels(2, 1, -1, -1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, + "reading pixels with negative width / height should generate INVALID_VALUE."); + + checkFormatAndType(); + + debug(""); + debug("no PIXEL_PACK buffer bound"); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, array); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "no pixel pack buffer bound"); + + gl.deleteBuffer(buffer); +} + +debug(""); +debug("Canvas.getContext"); + +var wtu = WebGLTestUtils; +var pixel = [0, 0, 0, 0]; +var expectedColor = [255, 102, 0, 255]; + +var canvas = document.getElementById("example"); +var gl = wtu.create3DContext(canvas, undefined, 2); + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + description('ReadPixels into PIXEL_PACK buffer'); + validatePixelPackBufferAndParameters(4, 4); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-pack-parameters.html b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-pack-parameters.html new file mode 100644 index 0000000000..8d4559fb93 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/reading/read-pixels-pack-parameters.html @@ -0,0 +1,353 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<canvas id="example" width="4" height="4"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict" + +var wtu = WebGLTestUtils; +var initialColor = [1, 2, 3, 4]; +var expectedColor = [[249, 102, 0, 255], + [2, 200, 102, 255], + [134, 87, 234, 255], + [99, 5, 76, 255]]; + +function calculatePaddingBytes(bytesPerPixel, packAlignment, width) +{ + var padding = 0; + switch (packAlignment) { + case 1: + case 2: + case 4: + case 8: + padding = (bytesPerPixel * width) % packAlignment; + if (padding > 0) + padding = packAlignment - padding; + return padding; + default: + testFailed("should not reach here"); + return; + } +} + +function paintWebGLCanvas(gl) +{ + var program = wtu.setupTexturedQuad(gl); + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + var data = new Uint8Array(4 * 4); + for (var ii = 0; ii < 4; ++ii) { + data[ii * 4] = expectedColor[ii][0]; + data[ii * 4 + 1] = expectedColor[ii][1]; + data[ii * 4 + 2] = expectedColor[ii][2]; + data[ii * 4 + 3] = expectedColor[ii][3]; + } + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); + + 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); + + var loc = gl.getUniformLocation(program, "tex"); + gl.uniform1i(loc, 0); + + wtu.clearAndDrawUnitQuad(gl); +} + +function samePixel(array, index, refPixel, row, pixelTag) +{ + for (var ii = 0; ii < refPixel.length; ++ii) { + if (array[index] == refPixel[ii][0] && + array[index + 1] == refPixel[ii][1] && + array[index + 2] == refPixel[ii][2] && + array[index + 3] == refPixel[ii][3]) { + return true; + } + } + var refPixelText = ""; + for (var ii = 0; ii < refPixel.length; ++ii) { + if (ii > 0) + refPixelText += " or "; + refPixelText += "[" + refPixel[ii] + "]"; + } + testFailed(pixelTag + " pixel of row " + row + ": expected " + refPixelText + ", got [" + + [array[index], array[index + 1], array[index + 2], array[index + 3]] + "]"); + return false; +} + +function runTestIteration(xoffset, yoffset, width, height, packParams, usePixelPackBuffer, packParamsValid) +{ + if (!("alignment" in packParams)) + packParams.alignment = 4; + if (!("rowLength" in packParams)) + packParams.rowLength = 0; + if (!("skipPixels" in packParams)) + packParams.skipPixels = 0; + if (!("skipRows" in packParams)) + packParams.skipRows = 0; + debug("Testing xoffset = " + xoffset + ", yoffset " + yoffset + + ", width = " + width + ", height = " + height + + ", PACK_ALIGNMENT = " + packParams.alignment + ", PACK_ROW_LENGTH = " + packParams.rowLength + + ", PACK_SKIP_PIXELS = " + packParams.skipPixels + " , PACK_SKIP_ROWS = " + packParams.skipRows); + gl.pixelStorei(gl.PACK_ALIGNMENT, packParams.alignment); + gl.pixelStorei(gl.PACK_ROW_LENGTH, packParams.rowLength); + gl.pixelStorei(gl.PACK_SKIP_PIXELS, packParams.skipPixels); + gl.pixelStorei(gl.PACK_SKIP_ROWS, packParams.skipRows); + + var actualWidth = packParams.rowLength > 0 ? packParams.rowLength : width; + + var bytesPerPixel = 4; // see readPixels' parameters below, the format is gl.RGBA, type is gl.UNSIGNED_BYTE + var padding = calculatePaddingBytes(bytesPerPixel, packParams.alignment, actualWidth); + var bytesPerRow = actualWidth * bytesPerPixel + padding; + + var size = bytesPerRow * (height - 1) + bytesPerPixel * width; + var skipSize = packParams.skipPixels * bytesPerPixel + packParams.skipRows * bytesPerRow; + var array = new Uint8Array(skipSize + size); + for (var ii = 0; ii < skipSize + size; ++ii) { + array[ii] = initialColor[ii % bytesPerPixel]; + } + var arrayWrongSize = null; + if (size > 0) + arrayWrongSize = new Uint8Array(skipSize + size - 1); + if (usePixelPackBuffer) { + var offset = 0; + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buffer); + if (size > 0) { + gl.bufferData(gl.PIXEL_PACK_BUFFER, arrayWrongSize, gl.STATIC_DRAW); + gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, offset); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer too small"); + } + gl.bufferData(gl.PIXEL_PACK_BUFFER, array, gl.STATIC_DRAW); + gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, offset); + } else { + if (size > 0) { + gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, arrayWrongSize); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "buffer too small"); + } + gl.readPixels(xoffset, yoffset, width, height, gl.RGBA, gl.UNSIGNED_BYTE, array); + } + if (packParamsValid) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should succeed"); + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Invalid pack params combination"); + return; + } + + if (size == 0) + return; + + if (usePixelPackBuffer) { + array = new Uint8Array(skipSize + size); + gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, array); + } + + // Check skipped bytes are unchanged. + for (var ii = 0; ii < skipSize; ++ii) { + if (array[ii] != initialColor[ii % bytesPerPixel]) { + testFailed("skipped bytes changed at index " + ii + ": expected " + + initialColor[ii % bytesPerPixel] + " got " + array[ii]); + break; + } + } + // Check the first and last pixels of each row. + var canvasWidth = 4; + var canvasHeight = 4; + for (var row = 0; row < height; ++row) { + var refColor; + var yIndex = yoffset + row; + var xIndex; + + // First pixel + var pos = skipSize + bytesPerRow * row; + xIndex = xoffset; + if (xIndex < 0 || xIndex >= canvasWidth || yIndex < 0 || yIndex >= canvasHeight) { + if (row > 0 && usePixelPackBuffer && packParams.rowLength > 0 && packParams.rowLength < width) + refColor = [initialColor, expectedColor[yIndex - 1]]; + else + refColor = [initialColor]; + } else { + refColor = [expectedColor[yIndex]]; + } + samePixel(array, pos, refColor, row, "first"); + + // Last pixel + var xSpan; + if (row + 1 == height || packParams.rowLength > width) + xSpan = width; + else + xSpan = actualWidth; + xIndex = xoffset + xSpan - 1; + pos += (xSpan - 1) * bytesPerPixel; + if (xIndex < 0 || xIndex >= canvasWidth || yIndex < 0 || yIndex >= canvasHeight) { + if (row > 0 && usePixelPackBuffer && packParams.rowLength > 0 && packParams.rowLength < width) + refColor = [initialColor, expectedColor[yIndex - 1]]; + else + refColor = [initialColor]; + } else { + refColor = [expectedColor[yIndex]]; + } + samePixel(array, pos, refColor, row, "last"); + + // Check padding bytes are unchanged and bytes beyond rowLength set correctly. + pos += bytesPerPixel; + if (row + 1 < height) { + // Beyond bytes filled for PACK_ROW_LENGTH, the row could have extra bytes due to + // padding. These extra bytes could be either filled with pixel data if + // PACK_ROW_LENGTH is set to be less than width, or they could be left unchanged + // if they are beyond |width| pixels. + if (packParams.rowLength > 0 && packParams.rowLength < width) { + var trailingBytes = Math.min((width - packParams.rowLength) * bytesPerPixel, + bytesPerRow - packParams.rowLength * bytesPerPixel); + for (var ii = 0; ii < trailingBytes; ++ii) { + if (array[pos + ii] != refColor[0][ii % bytesPerPixel]) { + testFailed("Trailing byte " + ii + " after rowLength of row " + row + " : expected " + + refColor[0][ii % bytesPerPixel] + ", got " + array[pos + ii]); + break; + } + } + pos += trailingBytes; + } + var paddingBytes = skipSize + bytesPerRow * (row + 1) - pos; + for (var ii = 0; ii < paddingBytes; ++ii) { + if (array[pos + ii] != initialColor[ii % bytesPerPixel]) { + testFailed("Padding byte " + ii + " of row " + row + " changed: expected " + + initialColor[ii % bytesPerPixel] + ", got " + array[pos + ii]); + break; + } + } + } + } +} + +function testPackParameters(usePixelPackBuffer) +{ + debug(""); + var destText = usePixelPackBuffer ? "PIXEL_PACK buffer" : "array buffer"; + debug("Verify that reading pixels to " + destText + " works fine with various pack alignments."); + runTestIteration(0, 0, 1, 3, {alignment:1}, usePixelPackBuffer, true); + runTestIteration(0, 0, 1, 3, {alignment:2}, usePixelPackBuffer, true); + runTestIteration(0, 0, 1, 3, {alignment:4}, usePixelPackBuffer, true); + runTestIteration(0, 0, 1, 3, {alignment:8}, usePixelPackBuffer, true); + runTestIteration(0, 0, 2, 3, {alignment:4}, usePixelPackBuffer, true); + runTestIteration(0, 0, 2, 3, {alignment:8}, usePixelPackBuffer, true); + runTestIteration(0, 0, 3, 3, {alignment:4}, usePixelPackBuffer, true); + runTestIteration(0, 0, 3, 3, {alignment:8}, usePixelPackBuffer, true); + runTestIteration(0, 0, 0, 0, {alignment:1}, usePixelPackBuffer, true); + runTestIteration(0, 0, 1, 3, {alignment:4}, usePixelPackBuffer, true); + runTestIteration(0, 0, 1, 3, {alignment:8}, usePixelPackBuffer, true); + + debug(""); + debug("Verify that reading pixels to " + destText + " is disallowed when PACK_ROW_LENGTH < width."); + runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:2}, usePixelPackBuffer, false); + runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:1}, usePixelPackBuffer, false); + + debug(""); + debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH == width."); + runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:3}, usePixelPackBuffer, true); + + debug(""); + debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH > width and with no padding"); + runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:4}, usePixelPackBuffer, true); + + debug(""); + debug("Verify that reading pixels to " + destText + " works fine with PACK_ROW_LENGTH > width and with padding"); + runTestIteration(0, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(-1, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(0, -1, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(-1, -1, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(-5, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(0, -5, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(2, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(0, 2, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(2, 2, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(5, 0, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + runTestIteration(0, 5, 3, 3, {alignment:8, rowLength:5}, usePixelPackBuffer, true); + + debug(""); + debug("Verify that reading pixels to " + destText + " works fine with pack skip parameters."); + runTestIteration(0, 0, 3, 3, {alignment:8, skipPixels:2}, usePixelPackBuffer, false); + runTestIteration(0, 0, 3, 3, {alignment:8, skipPixels:1, skipRows:3}, usePixelPackBuffer, false); + runTestIteration(0, 0, 3, 3, {alignment:8, skipRows:3}, usePixelPackBuffer, true); + runTestIteration(0, 0, 2, 3, {alignment:8, skipPixels:2}, usePixelPackBuffer, false); + runTestIteration(0, 0, 2, 3, {alignment:8, skipPixels:1, skipRows:3}, usePixelPackBuffer, false); + runTestIteration(0, 0, 2, 3, {alignment:8, skipRows:3}, usePixelPackBuffer, true); + runTestIteration(0, 0, 2, 3, {skipPixels:1, rowLength:4}, usePixelPackBuffer, true); +} + +debug(""); +debug("Canvas.getContext"); + +var canvas = document.getElementById("example"); +var gl = wtu.create3DContext(canvas, undefined, 2); + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + description('ReadPixels into array buffer'); + paintWebGLCanvas(gl); + var usePixelPackBuffer = false; + testPackParameters(usePixelPackBuffer); + usePixelPackBuffer = true; + testPackParameters(usePixelPackBuffer); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> |