diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance2/rendering')
53 files changed, 9590 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt new file mode 100644 index 0000000000..92ce232ee2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt @@ -0,0 +1,51 @@ +attrib-type-match.html +blitframebuffer-filter-outofbounds.html +blitframebuffer-filter-srgb.html +blitframebuffer-multisampled-readbuffer.html +--min-version 2.0.1 blitframebuffer-outside-readbuffer.html +--min-version 2.0.1 blitframebuffer-r11f-g11f-b10f.html +--min-version 2.0.1 blitframebuffer-resolve-to-back-buffer.html +blitframebuffer-scissor-enabled.html +blitframebuffer-size-overflow.html +--min-version 2.0.1 blitframebuffer-srgb-and-linear-drawbuffers.html +--min-version 2.0.1 blitframebuffer-stencil-only.html +blitframebuffer-test.html +--min-version 2.0.1 blitframebuffer-unaffected-by-colormask.html +--min-version 2.0.1 builtin-vert-attribs.html +canvas-resizing-with-pbo-bound.html +--min-version 2.0.1 clearbuffer-sub-source.html +--min-version 2.0.1 clearbufferfv-with-alpha-false.html +clear-func-buffer-type-match.html +--min-version 2.0.1 clear-srgb-color-buffer.html +--min-version 2.0.1 clipping-wide-points.html +--min-version 2.0.1 depth-stencil-feedback-loop.html +draw-buffers.html +--min-version 2.0.1 draw-buffers-dirty-state-bug.html +--min-version 2.0.1 draw-buffers-driver-hang.html +--min-version 2.0.1 draw-buffers-sparse-output-locations.html +--min-version 2.0.1 draw-with-integer-texture-base-level.html +element-index-uint.html +--min-version 2.0.1 framebuffer-completeness-draw-framebuffer.html +framebuffer-completeness-unaffected.html +--min-version 2.0.1 framebuffer-mismatched-attachment-targets.html +--min-version 2.0.1 framebuffer-render-to-layer.html +--min-version 2.0.1 framebuffer-render-to-layer-angle-issue.html +--min-version 2.0.1 framebuffer-texture-changing-base-level.html +--min-version 2.0.1 framebuffer-texture-level1.html +--min-version 2.0.1 framebuffer-to-texture.html +framebuffer-unsupported.html +--min-version 2.0.1 fs-color-type-mismatch-color-buffer-type.html +instanced-arrays.html +--min-version 2.0.1 instanced-rendering-bug.html +--min-version 2.0.1 instanced-rendering-large-divisor.html +--min-version 2.0.1 line-rendering-quality.html +--min-version 2.0.1 multisampling-depth-resolve.html +--min-version 2.0.1 multisampling-fragment-evaluation.html +out-of-bounds-index-buffers-after-copying.html +--min-version 2.0.1 rasterizer-discard-and-implicit-clear.html +--min-version 2.0.1 read-draw-when-missing-image.html +rgb-format-support.html +uniform-block-buffer-size.html +--min-version 2.0.1 texture-switch-performance.html +--min-version 2.0.1 vertex-id.html +--min-version 2.0.1 vertex-id-large-count.html diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/attrib-type-match.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/attrib-type-match.html new file mode 100644 index 0000000000..5b1b2884aa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/attrib-type-match.html @@ -0,0 +1,561 @@ +<!-- +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 Conformance Tests: Vertex Attribute Type Match</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing instanced draws --> +<script id="outputVertexShader" type="x-shader/x-vertex">#version 300 es +in vec4 aPosition; +in ivec2 aOffsetI; +in uvec2 aOffsetU; +in vec4 aColor; +out vec4 vColor; +void main() { + vColor = aColor; + vec2 offset = vec2(float(aOffsetI.x) + float(aOffsetU.x), + float(aOffsetI.y) + float(aOffsetU.y)); + gl_Position = aPosition + vec4(offset, 0.0, 0.0); +} +</script> + +<script id="outputFragmentShader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +in vec4 vColor; +out vec4 fragColor; +void main() { + fragColor = vColor; +} +</script> + +<script id='vshader_inactive_attrib' type='x-shader/x-vertex'>#version 300 es +in ivec4 p; +in ivec4 a; +void main() +{ + gl_Position = vec4(p); +} +</script> +<script id='vshader_active_attrib_int' type='x-shader/x-vertex'>#version 300 es +in ivec4 p; +in ivec4 a; +in uvec4 b; +void main() +{ + gl_Position = vec4(p) + vec4(a) + vec4(b); +} +</script> +<script id='vshader_active_attrib_float' type='x-shader/x-vertex'>#version 300 es +in vec4 p; +in vec4 a; +in vec4 c; +void main() +{ + gl_Position = vec4(p) + vec4(a) + vec4(c); +} +</script> +<script id='fshader' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; +void main() +{ + oColor = vec4(1.0, 0.0, 0.0, 1.0); +} +</script> + + +<script> +"use strict"; +description("This test verifies an active vertex attribute's base type has to match the verexAttrib function type."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + testGenericAttribs(); + runTests(); +} + +function testGenericAttribs() { + debug(""); + debug("Test Generic Vertex Attributes for some corner cases"); + + var pIndex = 2; + var aIndex = 3; + var bIndex = 4; + var cIndex = 5; + var program0 = wtu.setupProgram(gl, ["vshader_inactive_attrib", "fshader"], + ['p', 'a'], [pIndex, aIndex]); + var program1 = wtu.setupProgram(gl, ["vshader_active_attrib_int", "fshader"], + ['p', 'a', 'b'], [pIndex, aIndex, bIndex]); + var program2 = wtu.setupProgram(gl, ["vshader_active_attrib_float", "fshader"], + ['p', 'a', 'c'], [pIndex, aIndex, cIndex]); + if (!program0 || !program1 || !program2) { + testFailed("Set up program failed"); + return; + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error from set up"); + + wtu.setupUnitQuad(gl, 0); + + debug("Inactive input in vertex shader"); + gl.useProgram(program0); + gl.vertexAttribI4i(pIndex, 1, 0, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds: type in shader mismatch default vertex type is valid for inactive attrib"); + + gl.vertexAttrib4f(aIndex, 0.0, 1.0, 0.0, 0.0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds: type in shader mismatch vertexAttrib type is valid for inactive attrib"); + + debug("active int/uint inputs in vertex shader"); + gl.useProgram(program1); + gl.vertexAttribI4i(pIndex, 1, 0, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Type mismatch: type in shader mismatch the default type for a vertex attrib"); + gl.vertexAttribI4i(aIndex, 0, 1, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Type mismatch: type in shader mismatch the default type for a vertex attrib"); + gl.vertexAttribI4ui(bIndex, 0, 0, 1, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds"); + + debug("active float input in vertex shader"); + gl.useProgram(program2); + gl.vertexAttrib4f(pIndex, 1.0, 0.0, 0.0, 0.0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Type mismatch: generic attrib is valid per context. 'a' is set to int type by previous test case"); + gl.vertexAttrib4f(aIndex, 0.0, 1.0, 0.0, 0.0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds: default type of generic attrib is float"); +} + +function setupAttribValues(offsetILoc, offsetULoc, colorLoc) { + gl.vertexAttribI4i(offsetILoc, -1, -2, 0, 0); + gl.vertexAttribI4ui(offsetULoc, 1, 2, 0, 0); + gl.vertexAttrib4f(colorLoc, 1.0, 0, 0, 1.0); +} + +function setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer) { + gl.bindBuffer(gl.ARRAY_BUFFER, offsetIBuffer); + gl.vertexAttribIPointer(offsetILoc, 2, gl.INT, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, offsetUBuffer); + gl.vertexAttribIPointer(offsetULoc, 2, gl.UNSIGNED_INT, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); +} + +function runTests() { + debug(""); + debug("Test vertexAttrib with drawArrays and drawArraysInstanced"); + + var instanceCount = 4; + + var positionLoc = 0; + var offsetILoc = 2; + var offsetULoc = 3; + var colorLoc = 4; + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], + ['aPosition', 'aOffsetI', 'aOffsetU','aColor'], + [positionLoc, offsetILoc, offsetULoc, colorLoc]); + if (!program) { + testFailed("Set up program failed"); + return; + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error from set up"); + + wtu.setupUnitQuad(gl, 0); + + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); + + debug("int type function on uint type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4i(offsetULoc, 1, 2, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("float type function on uint type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttrib4f(offsetULoc, 1.0, 2.0, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("uint type function on int type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4ui(offsetILoc, 1, 2, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("float type function on int type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttrib4f(offsetILoc, 1.0, 2.0, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("int type function on float type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4i(colorLoc, 1, 0, 0, 1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("uint type function on float type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4ui(colorLoc, 1, 0, 0, 1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug(""); + debug("Test vertexAttrib with drawElements, drawRangeElements, and drawElementsInstanced"); + wtu.setupIndexedQuad(gl, 1, 0); + + debug("Correct setup"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawRangeElements succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); + + debug("int type function on uint type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4i(offsetULoc, 1, 2, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("float type function on uint type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttrib4f(offsetULoc, 1.0, 2.0, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("uint type function on int type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4ui(offsetILoc, 1, 2, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("float type function on int type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttrib4f(offsetILoc, 1.0, 2.0, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("int type function on float type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4i(colorLoc, 1, 0, 0, 1); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("uint type function on float type attrib"); + setupAttribValues(offsetILoc, offsetULoc, colorLoc); + gl.vertexAttribI4ui(colorLoc, 1, 0, 0, 1); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + + var offsetIBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetIBuffer); + var offsetI = new Int32Array([-1, -2, + -1, -2, + -1, -2, + -1, -2, + -1, -2, + -1, -2]); + gl.bufferData(gl.ARRAY_BUFFER, offsetI, gl.STATIC_DRAW); + + var offsetUBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetUBuffer); + var offsetU = new Uint32Array([1, 2, + 1, 2, + 1, 2, + 1, 2, + 1, 2, + 1, 2]); + gl.bufferData(gl.ARRAY_BUFFER, offsetU, gl.STATIC_DRAW); + + var offsetFBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetFBuffer); + var offsetF = new Float32Array([1.0, 2.0, + 1.0, 2.0, + 1.0, 2.0, + 1.0, 2.0, + 1.0, 2.0, + 1.0, 2.0]); + gl.bufferData(gl.ARRAY_BUFFER, offsetF, gl.STATIC_DRAW); + + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + var colors = new Float32Array([0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0]); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + + var colorUBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorUBuffer); + var colorsU = new Uint32Array([0, 1, 0, 1, + 0, 1, 0, 1, + 0, 1, 0, 1, + 0, 1, 0, 1, + 0, 1, 0, 1, + 0, 1, 0, 1]); + gl.bufferData(gl.ARRAY_BUFFER, colorsU, gl.STATIC_DRAW); + + gl.enableVertexAttribArray(offsetILoc); + gl.enableVertexAttribArray(offsetULoc); + gl.enableVertexAttribArray(colorLoc); + + debug(""); + debug("Test vertexAttrib{I}Pointer with drawArrays and drawArraysInstanced"); + wtu.setupUnitQuad(gl, 0); + + debug("Correct setup"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [0, 255, 0, 255]); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [0, 255, 0, 255]); + + debug("vertexAttribIPointer with int type on uint type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetIBuffer); + gl.vertexAttribIPointer(offsetULoc, 2, gl.INT, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribPointer on uint type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetFBuffer); + gl.vertexAttribPointer(offsetULoc, 2, gl.FLOAT, false, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("VertexAttribIPointer with uint type on int type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetUBuffer); + gl.vertexAttribIPointer(offsetILoc, 2, gl.UNSIGNED_INT, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribPointer on int type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetFBuffer); + gl.vertexAttribPointer(offsetILoc, 2, gl.FLOAT, false, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribIPointer with uint type on float type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, colorUBuffer); + gl.vertexAttribIPointer(colorLoc, 4, gl.UNSIGNED_INT, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribIPointer with int type on float type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, colorUBuffer); + gl.vertexAttribIPointer(colorLoc, 4, gl.INT, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug(""); + debug("Test vertexAttrib{I}Pointer with drawElements, drawRangeElements, and drawElementsInstanced"); + wtu.setupIndexedQuad(gl, 1, 0); + + debug("Correct setup"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up succeeds"); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [0, 255, 0, 255]); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawRangeElements succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [0, 255, 0, 255]); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced succeeds"); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [0, 255, 0, 255]); + + debug("vertexAttribIPointer with int type on uint type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetIBuffer); + gl.vertexAttribIPointer(offsetULoc, 2, gl.INT, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribPointer on uint type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetFBuffer); + gl.vertexAttribPointer(offsetULoc, 2, gl.FLOAT, false, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("VertexAttribIPointer with uint type on int type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetUBuffer); + gl.vertexAttribIPointer(offsetILoc, 2, gl.UNSIGNED_INT, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribPointer on int type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetFBuffer); + gl.vertexAttribPointer(offsetILoc, 2, gl.FLOAT, false, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribIPointer with uint type on float type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, colorUBuffer); + gl.vertexAttribIPointer(colorLoc, 4, gl.UNSIGNED_INT, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + + debug("vertexAttribIPointer with int type on float type attrib"); + setupAttribPointers(offsetILoc, offsetULoc, colorLoc, + offsetIBuffer, offsetUBuffer, colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, colorUBuffer); + gl.vertexAttribIPointer(colorLoc, 4, gl.INT, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "type mismatch"); +} + +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/rendering/blitframebuffer-filter-outofbounds.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-filter-outofbounds.html new file mode 100644 index 0000000000..f1fda162d5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-filter-outofbounds.html @@ -0,0 +1,172 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> + +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer when src/dst region are out-of-bounds."); + +var gl = wtu.create3DContext("example", undefined, 2); + +function checkPixel(color, expectedColor) { + var tolerance = 3; + return (Math.abs(color[0] - expectedColor[0]) <= tolerance && + Math.abs(color[1] - expectedColor[1]) <= tolerance && + Math.abs(color[2] - expectedColor[2]) <= tolerance && + Math.abs(color[3] - expectedColor[3]) <= tolerance); +} + +function blitframebuffer_filter_outofbounds(readbufferFormat, drawbufferFormat, filter) { + debug(""); + debug("blitting pixels out-of-bounds, read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat) + ", draw buffer format is: " + wtu.glEnumToString(gl, drawbufferFormat) + ", filter is: " + wtu.glEnumToString(gl, filter)); + + // Initiate data to read framebuffer + var size = 8; + var uint_read = new Uint8Array(size * size * 4); + var color = 0x20; + for (var ii = 0; ii < size * size * 4; ii += 4) { + for (var jj = 0; jj < 3; ++jj) { + uint_read[ii + jj] = color; + } + uint_read[ii + 3] = 0xff; + } + + // Create read framebuffer and feed data to read buffer + // Read buffer may have srgb image + var tex_read = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_read); + gl.texImage2D(gl.TEXTURE_2D, 0, readbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, uint_read); + + var fbo_read = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + + // Create draw framebuffer. Color in draw buffer is initialized to 0. + // Draw buffer may have srgb image + var tex_draw = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + var fbo_draw = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_draw, 0); + + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) { + // Blit read framebuffer to the image in draw framebuffer. + var tests = [ + // [srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1] + + { args: [-2, -2, 4, 4, 1, 1, 4, 4], changedDstRect: [2, 2, 4, 4], desc: 'only src region is out-of-bounds, dst region has different width/height as src region.'}, + { args: [-2, -2, 4, 4, 1, 1, 7, 7], changedDstRect: [3, 3, 7, 7], desc: 'only src region is out-of-bounds, dst region has the same width/height as src region.'}, + { args: [0, 0, 6, 6, 7, 7, 10, 10], changedDstRect: [7, 7, 8, 8], desc: 'only dst region is out-of-bounds, dst region has different width/height as src region after dst region is clipped to the bounds of draw buffer.'}, + { args: [0, 0, 6, 6, 4, 4, 10, 10], changedDstRect: [4, 4, 8, 8], desc: 'only dst region is out-of-bounds, dst region has the same width/height as src region after dst region is clipped to the bounds of draw buffer.'}, + { args: [-2, -2, 4, 4, 7, 7, 10, 10], changedDstRect: [8, 8, 8, 8], desc: 'both src and dst region are out-of-bounds, dst region has different width/height as src region after dst region is clipped to the bounds of draw buffer.'}, + { args: [-2, -2, 4, 4, 4, 4, 10, 10], changedDstRect: [6, 6, 8, 8], desc: 'both src and dst region are out-of-bounds, dst region has the same width/height as src region after dst region is clipped to the bounds of draw buffer.'}, + { args: [-2, -2, 4, 4, 2, 2, 10, 10], changedDstRect: [2 + 2/6*8, 2 + 2/6*8, 8, 8], desc: 'both src and dst region are out-of-bounds. There are some dst pixels (x and y are within [4, 8] , and x or y equals to 4) whose corresponding src pixels are partially inside and partially outside the real sampling area of the src region (the real sampling area is [0, 0, 4, 4]). But the centers of such src pixels are lying outside the real sampling area.'}, + { args: [-2, -2, 4, 4, 3, 3, 10, 10], changedDstRect: [5, 5, 8, 8], desc: 'both src and dst region are out-of-bounds. There are some dst pixels (x and y are within [4, 7] , and x or y equals to 5) whose corresponding src pixels are partially inside and partially outside the real sampling area of the src region (the real sampling area is [0, 0, 4, 4]). But the centers of such src pixels are lying inside the real sampling area.'}, + { args: [-2, -2, 4, 4, 10, 10, 2, 2], changedDstRect: [2, 2, 7, 7], desc: 'both src and dst region are out-of-bounds, and the dst coordinates are reversed. There are some dst pixels (x and y are within [2, 7] , and x or y equals to 7) whose corresponding src pixels are partially inside and partially outside the real sampling area of the src region (the real sampling area is [0, 0, 4, 4]). But the centers of such src pixels are lying outside the real sampling area.'}, + { args: [-2, -2, 4, 4, 10, 10, 3, 3], changedDstRect: [3, 3, 8, 8], desc: 'both src and dst region are out-of-bounds, and the dst coordinates are reversed. There are some dst pixels (x and y are within [3, 7] , and x or y equals to 7) whose corresponding src pixels are partially inside and partially outside the read sampling area of the src region (the real sampling area is [0, 0, 4, 4]). But the centers of such src pixels are lying inside the real sampling area.'}, + ]; + + var readbufferHasSRGBImage = (readbufferFormat == gl.SRGB8_ALPHA8); + var drawbufferHasSRGBImage = (drawbufferFormat == gl.SRGB8_ALPHA8); + + for (const test of tests) { + const args = test.args; + const changedDstRect = test.changedDstRect; + debug(""); + debug("Both the read framebuffer and draw framebuffer bounds are [0, 0, 8, 8]"); + debug(`Blitting from src region [${args.slice(0, 4)}] to dst region [${args.slice(4, 8)}]`); + debug(`Expects changed dst region of [${changedDstRect}]`); + debug(`Explaination: ${test.desc}`); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.blitFramebuffer(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], gl.COLOR_BUFFER_BIT, filter); + + // Read pixels and check the correctness. + var pixels = new Uint8Array(size * size * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + gl.readPixels(0, 0, size, size, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + + for (var ii = 0; ii < size; ++ii) { + for (var jj = 0; jj < size; ++jj) { + var loc = ii * size + jj; + var color = [pixels[loc * 4], pixels[loc * 4 + 1], pixels[loc * 4 + 2], pixels[loc * 4 + 3]]; + + var expectedColor = [0, 0, 0, 0]; + if (ii >= changedDstRect[0] && ii < changedDstRect[2] && jj >= changedDstRect[1] && jj < changedDstRect[3]) { + expectedColor = [0x20, 0x20, 0x20, 0xff]; + + // We may need to covert the color space for pixels in blit region + if (readbufferHasSRGBImage ^ drawbufferHasSRGBImage) { + if (drawbufferHasSRGBImage) { + expectedColor = wtu.linearToSRGB(expectedColor); + } else { + expectedColor = wtu.sRGBToLinear(expectedColor); + } + } + } + + if (checkPixel(color, expectedColor) == true) { + testPassed("pixel at [" + jj + ", " + ii + "] is (" + color + "). It is correct!"); + } else { + testFailed("pixel at [" + jj + ", " + ii + "] should be (" + expectedColor + "), but the actual color is (" + color + ")"); + } + } + } + } + } else { + testFailed("framebuffer not complete"); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteFramebuffer(fbo_read); + gl.deleteFramebuffer(fbo_draw); + gl.deleteTexture(tex_read); + gl.deleteTexture(tex_draw); +}; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + var filters = [gl.LINEAR, gl.NEAREST]; + for (var ii = 0; ii < filters.length; ++ii) { + blitframebuffer_filter_outofbounds(gl.RGBA8, gl.RGBA8, filters[ii]); + break; + blitframebuffer_filter_outofbounds(gl.RGBA8, gl.SRGB8_ALPHA8, filters[ii]); + blitframebuffer_filter_outofbounds(gl.SRGB8_ALPHA8, gl.RGBA8, filters[ii]); + blitframebuffer_filter_outofbounds(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8, filters[ii]); + } +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-filter-srgb.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-filter-srgb.html new file mode 100644 index 0000000000..4054a0af25 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-filter-srgb.html @@ -0,0 +1,161 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer with sRGB framebuffers."); + +var gl = wtu.create3DContext("example", undefined, 2); + +function checkPixel(color, expectedColor) { + var tolerance = 7; + return (Math.abs(color[0] - expectedColor[0]) <= tolerance && + Math.abs(color[1] - expectedColor[1]) <= tolerance && + Math.abs(color[2] - expectedColor[2]) <= tolerance && + Math.abs(color[3] - expectedColor[3]) <= tolerance); +} + +var tex_read = gl.createTexture(); +var tex_draw = gl.createTexture(); +var fbo_read = gl.createFramebuffer(); +var fbo_draw = gl.createFramebuffer(); +var size_read = 4; +var size_draw = 0; + +function blitframebuffer_helper(readbufferFormat, drawbufferFormat, filter, data) { + // Create read framebuffer and feed data to read buffer + gl.bindTexture(gl.TEXTURE_2D, tex_read); + gl.texImage2D(gl.TEXTURE_2D, 0, readbufferFormat, size_read, size_read, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + + // Create draw framebuffer and feed 0 to draw buffer + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size_draw, size_draw, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_draw, 0); + + gl.blitFramebuffer(0, 0, size_read, size_read, 0, 0, size_draw, size_draw, gl.COLOR_BUFFER_BIT, filter); + + // Read pixels for comparision + var pixels = new Uint8Array(size_draw * size_draw * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + gl.readPixels(0, 0, size_draw, size_draw, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + return pixels; +} + +function blitframebuffer_filter_srgb(readbufferFormat, drawbufferFormat, filter, minified) { + debug(""); + debug("Test srgb filtering for blitFramebuffer, the current filter is: " + wtu.glEnumToString(gl, filter)); + var min_mag = minified ? "minified to half the size." : "magnified to double the size."; + debug("read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat) + ", draw buffer format is: " + wtu.glEnumToString(gl, drawbufferFormat) + ", minify/magnify: " + min_mag); + + // Initiate data to read framebuffer + var src_buffer = new Uint8Array(size_read * size_read * 4); + var start = 0; + for (var ii = 0; ii < size_read * size_read * 4; ii += 4) { + for (var jj = 0; jj < 3; ++jj) { + src_buffer[ii + jj] = start; + } + src_buffer[ii + 3] = 0xff; + start += 0x10; + } + + // We may need to decode srgb to linear for reference data + var ref_buffer = new Uint8Array(size_read * size_read * 4); + for (var ii = 0; ii < size_read * size_read * 4; ii += 4) { + var color = [src_buffer[ii], src_buffer[ii + 1], src_buffer[ii + 2], src_buffer[ii + 3]]; + var ref_color; + if (readbufferFormat == gl.SRGB8_ALPHA8) { + ref_color = wtu.sRGBToLinear(color); + } else { + ref_color = color; + } + for (var jj = 0; jj < 4; ++jj) { + ref_buffer[ii + jj] = ref_color[jj]; + } + } + + // Blit framebuffer to filter srgb image, but the reference data is always retrieved by blitFramebuffer against linear image + size_draw = minified ? size_read / 2 : size_read * 2; + var pixels = blitframebuffer_helper(readbufferFormat, drawbufferFormat, filter, src_buffer); + var temp = blitframebuffer_helper(gl.RGBA, gl.RGBA, filter, ref_buffer); + + // We may need to encode linear to srgb for reference data + var ref_pixels = new Uint8Array(size_draw * size_draw * 4); + for (var ii = 0; ii < size_draw * size_draw * 4; ii += 4) { + var color = [temp[ii], temp[ii + 1], temp[ii + 2], temp[ii + 3]]; + var ref_color; + if (drawbufferFormat == gl.SRGB8_ALPHA8) { + ref_color = wtu.linearToSRGB(color); + } else { + ref_color = color; + } + for (var jj = 0; jj < 4; ++jj) { + ref_pixels[ii + jj] = ref_color[jj]; + } + } + + // Compare + for (var ii = 0; ii < size_draw; ++ii) { + for (var jj = 0; jj < size_draw; ++jj) { + var index = ii * size_draw * 4 + jj; + var color = [pixels[index], pixels[index + 1], pixels[index + 2], pixels[index + 3]]; + var expectedColor = [ref_pixels[index], ref_pixels[index + 1], ref_pixels[index + 2], ref_pixels[index + 3]]; + if (checkPixel(color, expectedColor) == true) { + testPassed("pixel at [" + jj + ", " + ii + "] is (" + color + "). It is correct!"); + } else { + testFailed("pixel at [" + jj + ", " + ii + "] should be (" + expectedColor + "), but the actual color is (" + color + ")"); + } + } + } +} + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + var filters = [gl.LINEAR, gl.NEAREST]; + for (var ii = 0; ii < filters.length; ++ii) { + blitframebuffer_filter_srgb(gl.RGBA8, gl.SRGB8_ALPHA8, filters[ii], true); + blitframebuffer_filter_srgb(gl.RGBA8, gl.SRGB8_ALPHA8, filters[ii], false); + blitframebuffer_filter_srgb(gl.SRGB8_ALPHA8, gl.RGBA8, filters[ii], true); + blitframebuffer_filter_srgb(gl.SRGB8_ALPHA8, gl.RGBA8, filters[ii], false); + blitframebuffer_filter_srgb(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8, filters[ii], true); + blitframebuffer_filter_srgb(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8, filters[ii], false); + } +} + +gl.bindTexture(gl.TEXTURE_2D, null); +gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); +gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); +gl.deleteFramebuffer(fbo_read); +gl.deleteFramebuffer(fbo_draw); +gl.deleteTexture(tex_read); +gl.deleteTexture(tex_draw); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-multisampled-readbuffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-multisampled-readbuffer.html new file mode 100644 index 0000000000..73f8e8b735 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-multisampled-readbuffer.html @@ -0,0 +1,112 @@ +<!-- +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 BlitFramebuffer Tests</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="canvas" width="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer with multisampled sRGB color buffer."); + +var gl = wtu.create3DContext("canvas", undefined, 2); + +var tex_blit = gl.createTexture(); +var fb0 = gl.createFramebuffer(); +var rb0 = gl.createRenderbuffer(); +var fbo_blit = gl.createFramebuffer(); +var size = 32; +var program; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + init(); + + var filters = [gl.LINEAR, gl.NEAREST]; + for (var ii = 0; ii < filters.length; ++ii) { + blitframebuffer_multisampled_readbuffer(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8, filters[ii]); + } +} + +function init() { + program = wtu.setupColorQuad(gl); + gl.viewport(0, 0, size, size); +} + +function blitframebuffer_helper(readbufferFormat, drawbufferFormat, filter) { + // Create draw framebuffer and feed 0 to draw buffer + gl.bindTexture(gl.TEXTURE_2D, tex_blit); + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_blit); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_blit, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup draw framebuffer should succeed"); + + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, filter); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitframebuffer should succeed"); +} + +function blitframebuffer_multisampled_readbuffer(readbufferFormat, drawbufferFormat, filter) { + debug(""); + debug("Test blitFramebuffer when the read buffer is a multisampled srgb image. The filter is: " + wtu.glEnumToString(gl, filter)); + debug("read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat) + ", draw buffer format is: " + wtu.glEnumToString(gl, drawbufferFormat)); + + // Draw to a multi-sampled srgb image, and blit to a srgb image. + gl.bindRenderbuffer(gl.RENDERBUFFER, rb0); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, readbufferFormat, size, size); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + var color = [252, 122, 15, 255]; + var expectedColor = wtu.linearToSRGB(color); + for (var i = 0; i < 4; ++i) { + color[i] = color[i] / 255; + } + // Draw a rectangle. Fill it with solid color. + // Note that the draw buffer is a multisampled srgb image. So during drawing, the color will be converted into srgb color space. + gl.useProgram(program); + wtu.drawFloatColorQuad(gl, color); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + blitframebuffer_helper(readbufferFormat, drawbufferFormat, filter); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Blit from a multi-sampled srgb image to a srgb image should succeed"); + + // Compare + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_blit); + wtu.checkCanvasRect(gl, 0, 0, size, size, expectedColor); +} + +gl.bindTexture(gl.TEXTURE_2D, null); +gl.bindRenderbuffer(gl.RENDERBUFFER, null); +gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); +gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); +gl.deleteRenderbuffer(rb0); +gl.deleteTexture(tex_blit); +gl.deleteFramebuffer(fb0); +gl.deleteFramebuffer(fbo_blit); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-outside-readbuffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-outside-readbuffer.html new file mode 100644 index 0000000000..a2e87034eb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-outside-readbuffer.html @@ -0,0 +1,267 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer."); + +var gl = wtu.create3DContext("example", undefined, 2); + +function checkPixel(color, expectedColor) { + var tolerance = 3; + return (Math.abs(color[0] - expectedColor[0]) <= tolerance && + Math.abs(color[1] - expectedColor[1]) <= tolerance && + Math.abs(color[2] - expectedColor[2]) <= tolerance && + Math.abs(color[3] - expectedColor[3]) <= tolerance); +} + +function blitframebuffer_outside_readbuffer(readbufferFormat, drawbufferFormat) { + debug(""); + debug("blitting outside of read framebuffer, read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat) + ", draw buffer format is: " + wtu.glEnumToString(gl, drawbufferFormat)); + + // Initiate data to read framebuffer + var size_read = 3; + var uint_read = new Uint8Array(size_read * size_read * 4); + var start = 0x20; + for (var ii = 0; ii < size_read * size_read * 4; ii += 4) { + for (var jj = 0; jj < 3; ++jj) { + uint_read[ii + jj] = start; + } + uint_read[ii + 3] = 0xff; + start += 0x10; + } + + // Create read framebuffer and feed data to read buffer + // Read buffer may has srgb image + var tex_read = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_read); + gl.texImage2D(gl.TEXTURE_2D, 0, readbufferFormat, size_read, size_read, 0, gl.RGBA, gl.UNSIGNED_BYTE, uint_read); + + var fbo_read = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + + // Initiate data to draw framebuffer + var size_draw = 7; + var uint_draw = new Uint8Array(size_draw * size_draw * 4); + for (var ii = 0; ii < size_draw * size_draw * 4; ii += 4) { + for (var jj = 0; jj < 3; ++jj) { + uint_draw[ii + jj] = 0x10; + } + uint_draw[ii + 3] = 0xff; + } + + // Create draw framebuffer and feed data to draw buffer + // Draw buffer may has srgb image + var tex_draw = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + 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); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size_draw, size_draw, 0, gl.RGBA, gl.UNSIGNED_BYTE, uint_draw); + + var fbo_draw = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_draw, 0); + + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) { + var ref = [ + // The reference pixels of the 1st line: (0, 0) ~ (6, 0) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 2nd line: (0, 1) ~ (6, 1) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 3rd line: (0, 2) ~ (6, 2) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x20, 0x20, 0x20, 0xff], [0x30, 0x30, 0x30, 0xff], + [0x40, 0x40, 0x40, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 4th line: (0, 3) ~ (6, 3) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x50, 0x50, 0x50, 0xff], [0x60, 0x60, 0x60, 0xff], + [0x70, 0x70, 0x70, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 5th line: (0, 4) ~ (6, 4) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x80, 0x80, 0x80, 0xff], [0x90, 0x90, 0x90, 0xff], + [0xa0, 0xa0, 0xa0, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 6th line: (0, 5) ~ (6, 5) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + + // The reference pixels of the 7th line: (0, 6) ~ (6, 6) + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], [0x10, 0x10, 0x10, 0xff], + ]; + + // The 1st round test: blit read framebuffer to the image in draw framebuffer + // All directions of the read region have pixels outside of the read buffer + // The src region and/or dst region may be reversed during blitting. + var test1 = [ + [-1, 4, 1, 6], // reverse neither src nor dst + [4, -1, 1, 6], // reverse src only + [-1, 4, 6, 1], // reverse dst only + [4, -1, 6, 1] // reverse both src and dst + ]; + + var readbufferHasSRGBImage = (readbufferFormat == gl.SRGB8_ALPHA8); + var drawbufferHasSRGBImage = (drawbufferFormat == gl.SRGB8_ALPHA8); + + for (var i = 0; i < 4; ++i) { + debug(""); + switch (i) { + case 0: debug("reverse neither src region nor dst region"); break; + case 1: debug("reverse src region only"); break; + case 2: debug("reverse dst region only"); break; + case 3: debug("reverse both src region and dst region"); break; + } + var srcStart = test1[i][0]; + var srcEnd = test1[i][1]; + var dstStart = test1[i][2]; + var dstEnd = test1[i][3]; + var realBlittedDstStart = 2; + var realBlittedDstEnd = 5; + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.blitFramebuffer(srcStart, srcStart, srcEnd, srcEnd, dstStart, dstStart, dstEnd, dstEnd, gl.COLOR_BUFFER_BIT, gl.LINEAR); + + // Read pixels and check the correctness. + var pixels = new Uint8Array(size_draw * size_draw * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + gl.readPixels(0, 0, size_draw, size_draw, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + + for (var ii = 0; ii < size_draw; ++ii) { + for (var jj = 0; jj < size_draw; ++jj) { + var loc = ii * size_draw + jj; + var color = [pixels[loc * 4], pixels[loc * 4 + 1], pixels[loc * 4 + 2], pixels[loc * 4 + 3]]; + + // We may need to reverse the reference loc if necessary + var ref_loc = loc; + var reverse_src = (srcStart < srcEnd); + var reverse_dst = (dstStart < dstEnd); + var reversed = reverse_src ^ reverse_dst; + if (reversed) { + ref_loc = (size_draw - ii - 1) * size_draw + (size_draw - jj -1); + } + var expectedColor = ref[ref_loc]; + + // We may need to covert the color space for pixels in blit region + if ((readbufferHasSRGBImage ^ drawbufferHasSRGBImage) && + (ii >= realBlittedDstStart && ii < realBlittedDstEnd && jj >= realBlittedDstStart && jj < realBlittedDstEnd)) { + if (drawbufferHasSRGBImage) { + expectedColor = wtu.linearToSRGB(expectedColor); + } else { + expectedColor = wtu.sRGBToLinear(expectedColor); + } + } + if (checkPixel(color, expectedColor) == true) { + testPassed("pixel at [" + jj + ", " + ii + "] is (" + color + "). It is correct!"); + } else { + testFailed("pixel at [" + jj + ", " + ii + "] should be (" + expectedColor + "), but the actual color is (" + color + ")"); + } + } + } + } + + // The 2nd round test: blit read framebuffer to the image in draw framebuffer + // Only one direction of the read region have pixels outside of the read buffer + var tests = [ + [-1, 0], // pixels are outside the left edge of the read buffer + [0, -1], // pixels are outside the bottom edge of the read buffer + [1, 0], // pixels are outside the right edge of the read buffer + [0, 1] // pixels are outside the top edge of the read buffer + ]; + for (var i = 0; i < 4; ++i) { + debug(""); + switch (i) { + case 0: debug("verify that pixels lying outside the left edge of the read buffer should remain untouched"); break; + case 1: debug("verify that pixels lying outside the bottom edge of the read buffer should remain untouched"); break; + case 2: debug("verify that pixels lying outside the right edge of the read buffer should remain untouched"); break; + case 3: debug("verify that pixels lying outside the top edge of the read buffer should remain untouched"); break; + } + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + var srcX = tests[i][0]; + var srcY = tests[i][1]; + var offset = dstStart - srcStart; + gl.blitFramebuffer(srcX, srcY, srcX + size_read, srcY + size_read, + srcX + offset, srcY + offset, srcX + offset + size_read, srcY + offset + size_read, + gl.COLOR_BUFFER_BIT, gl.LINEAR); + + // Read pixels and check the correctness. + var pixels = new Uint8Array(size_draw * size_draw * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + gl.readPixels(0, 0, size_draw, size_draw, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + for (var ii = srcY + offset; ii < srcY + offset + size_read; ++ii) { + for (var jj = srcX + offset; jj < srcX + offset + size_read; ++jj) { + var loc = ii * size_draw + jj; + var color = [pixels[loc * 4], pixels[loc * 4 + 1], pixels[loc * 4 + 2], pixels[loc * 4 + 3]]; + var expectedColor = ref[loc]; + // We may need to covert the color space for pixels in blit region + if ((readbufferHasSRGBImage ^ drawbufferHasSRGBImage) && + (ii >= realBlittedDstStart && ii < realBlittedDstEnd && jj >= realBlittedDstStart && jj < realBlittedDstEnd)) { + if (drawbufferHasSRGBImage) { + expectedColor = wtu.linearToSRGB(expectedColor); + } else { + expectedColor = wtu.sRGBToLinear(expectedColor); + } + } + if (checkPixel(color, expectedColor) == true) { + testPassed("pixel at [" + jj + ", " + ii + "] is (" + color + "). It is correct!"); + } else { + testFailed("pixel at [" + jj + ", " + ii + "] should be (" + expectedColor + "), but the actual color is (" + color + ")"); + } + } + } + } + } else { + testFailed("framebuffer not complete"); + } + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteFramebuffer(fbo_read); + gl.deleteFramebuffer(fbo_draw); + gl.deleteTexture(tex_read); + gl.deleteTexture(tex_draw); +}; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + blitframebuffer_outside_readbuffer(gl.RGBA8, gl.RGBA8); + blitframebuffer_outside_readbuffer(gl.RGBA8, gl.SRGB8_ALPHA8); + blitframebuffer_outside_readbuffer(gl.SRGB8_ALPHA8, gl.RGBA8); + blitframebuffer_outside_readbuffer(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html new file mode 100644 index 0000000000..636e76ac29 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-r11f-g11f-b10f.html @@ -0,0 +1,113 @@ +<!-- +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 R11F_G11F_B10F BlitFramebuffer Tests</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"; + +var wtu = WebGLTestUtils; +description("This tests multisample blitting with the R11F_G11F_B10F format."); + +var width = 8; +var height = 8; + +function runWithContextCreationArguments(args) { + debug(''); + debug('Running test with arguments: ' + JSON.stringify(args)); + + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + + var gl = wtu.create3DContext(canvas, args, 2); + if (!gl) { + testFailed("WebGL 2.0 context does not exist"); + return; + } + + var ext = gl.getExtension("EXT_color_buffer_float"); + if (!ext) { + testPassed("EXT_color_buffer_float extension not supported"); + return; + } + + var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.R11F_G11F_B10F, gl.SAMPLES); + + // Set up source framebuffer. + var rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.R11F_G11F_B10F, width, height); + var readfb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, readfb); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after multisampled R11F_G11F_B10F FBO setup"); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Source framebuffer incomplete."); + return; + } + + // Draw something to that framebuffer. + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after clearing R11F_G11F_B10F framebuffer"); + + // Set up destination framebuffer for resolving MSAA. + 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.UNSIGNED_INT_10F_11F_11F_REV, null); + var drawfb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, drawfb); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after destination R11F_G11F_B10F FBO setup"); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + // Attempt a blit. + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after blitFramebuffer for multisample resolve"); + + // Try a readback. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, drawfb); + var readBackBuf = new Float32Array(width * height * 4); + wtu.checkCanvasRect(gl, 0, 0, width, height, [0.0, 1.0, 0.0], "should be green", undefined, readBackBuf, gl.FLOAT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after floating-point canvas readback"); + + // If default backbuffer is RGB and non-antialiased, test blitting to it too. + if (args && !args['alpha'] && !args['antialias']) { + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, drawfb); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after blit to default back buffer"); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + wtu.checkCanvas(gl, [ 0, 255, 0, 255 ], "default back buffer should be green"); + } +} + +// The format of the back buffer should have no effect on the behavior of this test. +runWithContextCreationArguments(undefined); +runWithContextCreationArguments({ alpha: true, antialias: true }); +runWithContextCreationArguments({ alpha: true, antialias: false }); +runWithContextCreationArguments({ alpha: false, antialias: true }); +runWithContextCreationArguments({ alpha: false, antialias: false }); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-resolve-to-back-buffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-resolve-to-back-buffer.html new file mode 100644 index 0000000000..addbdc4e9d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-resolve-to-back-buffer.html @@ -0,0 +1,245 @@ +<!-- +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 BlitFramebuffer Resolve to Back 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> +<div id="description"></div> +<div id="canvasHeader"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the behavior of blitFramebuffer when resolving directly to the back buffer."); + +debug("Regression test for <a href='http://crbug.com/699566'>http://crbug.com/699566</a>"); + +function runTest(testParams) { + const sz = 64; + + if (testParams.multisampled === undefined) { + testParams.multisampled = true; + } + + debug(''); + debug('Testing with alpha = ' + testParams.attribs.alpha + + ', antialias = ' + testParams.attribs.antialias + + ', internalformat = ' + testParams.internalformat + + ', multisampled = ' + testParams.multisampled); + + var canvas = document.createElement('canvas'); + canvas.width = sz; + canvas.height = sz; + document.getElementById('canvasHeader').appendChild(canvas); + var gl = wtu.create3DContext(canvas, testParams.attribs, 2); + + // Find the supported samples for a multisampled renderbuffer of the appropriate internal format. + let samples = [0]; + if (testParams.multisampled) { + samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl[testParams.internalformat], gl.SAMPLES); + if (!samples || !samples.length) { + testFailed("At least one multisampled format is required to be supported"); + return; + } + } + + // Create a framebuffer with a multisampled renderbuffer. + let rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl[testParams.internalformat], sz, sz); + + // Create a framebuffer. + let fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + + // Check for completeness. + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Rendering to a multisampled renderbuffer of format " + testParams.internalformat + " is required by the spec"); + return; + } + + // Clear to specified color. + gl.clearColor.apply(gl, testParams.clearColor); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Unbind draw framebuffer. Read framebuffer is now user framebuffer; + // draw framebuffer is default framebuffer. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors before blit"); + + // Blit from user framebuffer to default framebuffer. + gl.blitFramebuffer(0, 0, sz, sz, 0, 0, sz, sz, gl.COLOR_BUFFER_BIT, gl.NEAREST); + + if (testParams.shouldSucceed) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be legal to blit/resolve to default back buffer"); + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "incompatible src/dest blitFramebuffer combination must fail"); + } + + // Unbind user framebuffer completely. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + + if (testParams.shouldSucceed) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error before readback"); + wtu.checkCanvasRect(gl, 0, 0, 8, 8, testParams.resultColor); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + } +} + +var tests = [ + // No-alpha, no-antialias, RGB8 source + { + attribs: { + alpha: false, + antialias: false, + }, + internalformat: 'RGB8', + clearColor: [ 0.0, 1.0, 0.0, 0.5 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: true, + }, + // No-alpha, no-antialias, RGBA8 source + { + attribs: { + alpha: false, + antialias: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 0.5 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // No-alpha, no-antialias, RGBA8 source, single-sampled blit + { + attribs: { + alpha: false, + antialias: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 0.5 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: true, + multisampled: false, + }, + // Alpha, no-antialias, RGB8 source + { + attribs: { + alpha: true, + antialias: false, + }, + internalformat: 'RGB8', + clearColor: [ 0.0, 1.0, 0.0, 1.0 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // No-alpha, no-antialias, RGBA8 source + // premultiplyAlpha:false just to avoid semantically incorrect + // colors (should only affect rendering, not contents of WebGL + // back buffer) + { + attribs: { + alpha: false, + antialias: false, + premultiplyAlpha: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 1.0 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // Alpha, no-antialias, RGBA8 source + // premultiplyAlpha:false just to avoid semantically incorrect + // colors (should only affect rendering, not contents of WebGL + // back buffer) + { + attribs: { + alpha: true, + antialias: false, + premultiplyAlpha: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 0.0 ], + resultColor: [ 0, 255, 0, 0 ], + shouldSucceed: true, + }, + + // All attempts to blit to an antialiased back buffer should fail. + + // No-alpha, antialias, RGB8 source + { + attribs: { + alpha: false, + antialias: true, + }, + internalformat: 'RGB8', + clearColor: [ 0.0, 1.0, 0.0, 1.0 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // Alpha, antialias, RGB8 source + { + attribs: { + alpha: true, + antialias: true, + }, + internalformat: 'RGB8', + clearColor: [ 0.0, 1.0, 0.0, 1.0 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // No-alpha, antialias, RGBA8 source + // premultiplyAlpha:false just to avoid semantically incorrect + // colors (should only affect rendering, not contents of WebGL + // back buffer) + { + attribs: { + alpha: false, + antialias: true, + premultiplyAlpha: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 1.0 ], + resultColor: [ 0, 255, 0, 255 ], + shouldSucceed: false, + }, + // Alpha, antialias, RGBA8 source + // premultiplyAlpha:false just to avoid semantically incorrect + // colors (should only affect rendering, not contents of WebGL + // back buffer) + { + attribs: { + alpha: true, + antialias: true, + premultiplyAlpha: false, + }, + internalformat: 'RGBA8', + clearColor: [ 0.0, 1.0, 0.0, 0.0 ], + resultColor: [ 0, 255, 0, 0 ], + shouldSucceed: false, + }, +]; + +for (var ii in tests) { + runTest(tests[ii]); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-scissor-enabled.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-scissor-enabled.html new file mode 100644 index 0000000000..d0e2dfaefa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-scissor-enabled.html @@ -0,0 +1,160 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer when scissor test is enabled."); + +var gl = wtu.create3DContext("example", undefined, 2); + +// Define the src region and dst region for blitFramebuffer +var blit_src = [0, 0, 4, 4]; +var blit_dst = [2, 2, 6, 6]; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var bounds = [ + [0, 0, 4, 4], // Partially intersects with blitFramebuffer's dst region + [0, 0, 2, 2], // No intersection with blitFramebuffer's dst region + ]; + + // We can compute the real drawing area by intersecting the scissor bound with dst region of blitting. + var intersections = [ + [2, 2, 4, 4], + [0, 0, 0, 0], + ]; + + for (var ii = 0; ii < bounds.length; ++ii) { + blitframebuffer_scissor(gl.RGBA8, gl.RGBA8, bounds[ii], intersections[ii]); + blitframebuffer_scissor(gl.RGBA8, gl.SRGB8_ALPHA8, bounds[ii], intersections[ii]); + blitframebuffer_scissor(gl.SRGB8_ALPHA8, gl.RGBA8, bounds[ii], intersections[ii]); + blitframebuffer_scissor(gl.SRGB8_ALPHA8, gl.SRGB8_ALPHA8, bounds[ii], intersections[ii]); + } +} + +function checkPixel(color, expectedColor) { + var tolerance = 3; + return (Math.abs(color[0] - expectedColor[0]) <= tolerance && + Math.abs(color[1] - expectedColor[1]) <= tolerance && + Math.abs(color[2] - expectedColor[2]) <= tolerance && + Math.abs(color[3] - expectedColor[3]) <= tolerance); +} + +function blitframebuffer_scissor(readbufferFormat, drawbufferFormat, bound, intersection) { + debug(""); + debug("read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat) + ", draw buffer format is: " + wtu.glEnumToString(gl, drawbufferFormat)); + + + // Initiate data to read framebuffer + var size = 8; + var data = new Uint8Array(size * size * 4); + var color = [250, 100, 15, 255]; + for (var ii = 0; ii < size * size * 4; ii += 4) { + for (var jj = 0; jj < 4; ++jj) { + data[ii + jj] = color[jj]; + } + } + + // Feed data to read buffer. Feed 0 to draw buffer. + var tex_read = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_read); + gl.texImage2D(gl.TEXTURE_2D, 0, readbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); + + var fbo_read = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + + var tex_draw = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + gl.texImage2D(gl.TEXTURE_2D, 0, drawbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + var fbo_draw = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_draw, 0); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + // Enable scissor test. Then blit framebuffer. + gl.enable(gl.SCISSOR_TEST); + gl.scissor(bound[0], bound[1], bound[2], bound[3]); + gl.blitFramebuffer(blit_src[0], blit_src[1], blit_src[2], blit_src[3], blit_dst[0], blit_dst[1], blit_dst[2], blit_dst[3], gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitframebuffer should succeed"); + + // Read pixels and Comparison + var pixels = new Uint8Array(size * size * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + gl.readPixels(0, 0, size, size, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should succeed"); + + var blitColor; + var expectedColor; + var clearColor = [0, 0, 0, 0]; + + if (readbufferFormat == drawbufferFormat) { + blitColor = color; + } else if (readbufferFormat == gl.SRGB8_ALPHA8) { + blitColor = wtu.sRGBToLinear(color); + } else { + blitColor = wtu.linearToSRGB(color); + } + + var failed = false; + for (var ii = 0; ii < size; ++ii) { + for (var jj = 0; jj < size; ++jj) { + if (ii >= intersection[0] && jj >= intersection[1] && ii < intersection[2] && jj < intersection[3]) { + expectedColor = blitColor; + } else { + expectedColor = clearColor; + } + var index = (ii * size + jj) * 4; + var pixelColor = [pixels[index], pixels[index + 1], pixels[index + 2], pixels[index + 3]]; + if (checkPixel(pixelColor, expectedColor) == false) { + failed = true; + debug("Pixels comparison failed. Pixel at [" + jj + ", " + ii + "] should be (" + expectedColor + "), but the actual color is (" + pixelColor + ")"); + } + } + } + if (failed == false) { + testPassed("All pixels comparision passed!"); + } + + // Deinit + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteFramebuffer(fbo_read); + gl.deleteFramebuffer(fbo_draw); + gl.deleteTexture(tex_read); + gl.deleteTexture(tex_draw); +}; + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-size-overflow.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-size-overflow.html new file mode 100644 index 0000000000..512946cb82 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-size-overflow.html @@ -0,0 +1,98 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies blitFramebuffer won't cause a crash when the computed sizes might overflow."); + +var width = 8; +var height = 8; + +var gl = wtu.create3DContext("example", undefined, 2); +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + blit_region_test(); +} + +function blit_region_test() { + + debug(""); + debug("Begin to run blitFramebuffer. The computed width/height of src and/or dst region might overflow during blitting."); + var tex0 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex0); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + var fb0 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex0, 0); + + var tex1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + var fb1 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex1, 0); + if ((gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) || + (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)) { + testFailed("Framebuffer incomplete."); + return; + } + + var max = 0x7fffffff; + gl.blitFramebuffer(0, 0, max, max, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.blitFramebuffer(0, 0, width, height, 0, 0, max, max, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.blitFramebuffer(0, 0, max, max, 0, 0, max, max, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Using max 32-bit integer as blitFramebuffer parameter should succeed."); + + gl.blitFramebuffer(-1, -1, max - 1, max - 1, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.blitFramebuffer(0, 0, width, height, -1, -1, max - 1, max - 1, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.blitFramebuffer(-1, -1, max - 1, max - 1, -1, -1, max - 1, max - 1, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Using blitFramebuffer parameters where calculated width/height matches max 32-bit integer should succeed."); + + gl.blitFramebuffer(-1, -1, max, max, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using source width/height greater than max 32-bit integer should fail."); + gl.blitFramebuffer(max, max, -1, -1, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using source width/height greater than max 32-bit integer should fail."); + gl.blitFramebuffer(0, 0, width, height, -1, -1, max, max, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using destination width/height greater than max 32-bit integer should fail."); + gl.blitFramebuffer(0, 0, width, height, max, max, -1, -1, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using destination width/height greater than max 32-bit integer should fail."); + gl.blitFramebuffer(-1, -1, max, max, -1, -1, max, max, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using both source and destination width/height greater than max 32-bit integer should fail."); + gl.blitFramebuffer(-max - 1, -max - 1, max, max, -max - 1, -max - 1, max, max, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "Using minimum and maximum integers for all boundaries should fail."); + + gl.bindTexture(gl.TEXTURE_2D, null) + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteTexture(tex0); + gl.deleteTexture(tex1); + gl.deleteFramebuffer(fb0); + gl.deleteFramebuffer(fb1); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-srgb-and-linear-drawbuffers.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-srgb-and-linear-drawbuffers.html new file mode 100644 index 0000000000..35b8c3ddab --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-srgb-and-linear-drawbuffers.html @@ -0,0 +1,207 @@ +<!-- +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 BlitFramebuffer Tests</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="canvas" width="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer with multiple draw buffers (srgb image and linear image)."); + +var gl = wtu.create3DContext("canvas", undefined, 2); +var linearMask = 1; +var srgbMask = 2; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var filters = [gl.LINEAR, gl.NEAREST]; + var drawbuffersFormats = [linearMask, srgbMask, linearMask | srgbMask]; + for (var ii = 0; ii < filters.length; ++ii) { + for (var jj = 0; jj < drawbuffersFormats.length; ++jj) { + blitframebuffer_srgb_and_linear_drawbuffers(gl.SRGB8_ALPHA8, drawbuffersFormats[jj], filters[ii]); + blitframebuffer_srgb_and_linear_drawbuffers(gl.RGBA8, drawbuffersFormats[jj], filters[ii]); + } + } +} + +function blitframebuffer_srgb_and_linear_drawbuffers(readbufferFormat, drawbuffersFormatMask, filter) { + debug(""); + debug("The filter is: " + wtu.glEnumToString(gl, filter)); + debug("Read buffer format is: " + wtu.glEnumToString(gl, readbufferFormat)); + var drawbuffersFormat = "\0"; + if (drawbuffersFormatMask & linearMask) { + drawbuffersFormat += " linear "; + } + if (drawbuffersFormatMask & srgbMask) { + drawbuffersFormat += " srgb "; + } + debug("The test have multiple draw buffers, the images are: " + drawbuffersFormat); + + var tex_srgb0 = gl.createTexture(); + var tex_srgb1 = gl.createTexture(); + var tex_linear0 = gl.createTexture(); + var tex_linear1 = gl.createTexture(); + var tex_read = gl.createTexture(); + var fbo_read = gl.createFramebuffer(); + var fbo_draw = gl.createFramebuffer(); + + // Create read buffer and feed data to the read buffer + var size = 8; + var data = new Uint8Array(size * size * 4); + var color = [250, 100, 15, 255]; + for (var ii = 0; ii < size * size * 4; ii += 4) { + for (var jj = 0; jj < 4; ++jj) { + data[ii + jj] = color[jj]; + } + } + gl.bindTexture(gl.TEXTURE_2D, tex_read); + gl.texImage2D(gl.TEXTURE_2D, 0, readbufferFormat, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, data); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup read framebuffer should succeed"); + + // Create multiple textures. Attach them as fbo's draw buffers. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + + var drawbuffers = [gl.NONE, gl.NONE, gl.NONE, gl.NONE]; + if (drawbuffersFormatMask & srgbMask) { + gl.bindTexture(gl.TEXTURE_2D, tex_srgb0); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_srgb0, 0); + gl.bindTexture(gl.TEXTURE_2D, tex_srgb1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, tex_srgb1, 0); + drawbuffers[0] = gl.COLOR_ATTACHMENT0; + drawbuffers[2] = gl.COLOR_ATTACHMENT2; + } + + if (drawbuffersFormatMask & linearMask) { + gl.bindTexture(gl.TEXTURE_2D, tex_linear0); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, tex_linear0, 0); + gl.bindTexture(gl.TEXTURE_2D, tex_linear1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT3, gl.TEXTURE_2D, tex_linear1, 0); + drawbuffers[1] = gl.COLOR_ATTACHMENT1; + drawbuffers[3] = gl.COLOR_ATTACHMENT3; + } + + gl.drawBuffers(drawbuffers); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup draw framebuffer should succeed"); + + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete when setup draw framebuffer."); + return; + } + + // Blit to multiple draw buffers with srgb images and linear images + var dstSize = size - 1; + gl.blitFramebuffer(0, 0, size, size, 0, 0, dstSize, dstSize, gl.COLOR_BUFFER_BIT, filter); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitframebuffer should succeed"); + + // Read pixels from srgb images and linear images + var srgbPixels0 = new Uint8Array(dstSize * dstSize * 4); + var srgbPixels1 = new Uint8Array(dstSize * dstSize * 4); + var linearPixels0 = new Uint8Array(dstSize * dstSize * 4); + var linearPixels1 = new Uint8Array(dstSize * dstSize * 4); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_draw); + if (drawbuffersFormatMask & srgbMask) { + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.readPixels(0, 0, dstSize, dstSize, gl.RGBA, gl.UNSIGNED_BYTE, srgbPixels0); + gl.readBuffer(gl.COLOR_ATTACHMENT2); + gl.readPixels(0, 0, dstSize, dstSize, gl.RGBA, gl.UNSIGNED_BYTE, srgbPixels1); + } + + if (drawbuffersFormatMask & linearMask) { + gl.readBuffer(gl.COLOR_ATTACHMENT1); + gl.readPixels(0, 0, dstSize, dstSize, gl.RGBA, gl.UNSIGNED_BYTE, linearPixels0); + gl.readBuffer(gl.COLOR_ATTACHMENT3); + gl.readPixels(0, 0, dstSize, dstSize, gl.RGBA, gl.UNSIGNED_BYTE, linearPixels1); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readpixels should succeed"); + + // Compare + var expectedSRGBColor = (readbufferFormat == gl.SRGB8_ALPHA8) ? color : wtu.linearToSRGB(color); + var expectedLinearColor = (readbufferFormat == gl.SRGB8_ALPHA8) ? wtu.sRGBToLinear(color) : color; + var failed = false; + for (var ii = 0; ii < dstSize; ++ii) { + for (var jj = 0; jj < dstSize; ++jj) { + var index = (ii * dstSize + jj) * 4; + if (drawbuffersFormatMask & srgbMask) { + var srgbColor0 = [srgbPixels0[index], srgbPixels0[index + 1], srgbPixels0[index + 2], srgbPixels0[index + 3]]; + if (checkPixel(srgbColor0, expectedSRGBColor) == false) { + failed = true; + debug("Pixels comparison failed for the 1st sRGB image. Pixel at [" + jj + ", " + ii + "] should be (" + expectedSRGBColor + "), but the actual color is (" + srgbColor0 + ")"); + } + var srgbColor1 = [srgbPixels1[index], srgbPixels1[index + 1], srgbPixels1[index + 2], srgbPixels1[index + 3]]; + if (checkPixel(srgbColor1, expectedSRGBColor) == false) { + failed = true; + debug("Pixels comparison failed for the 2nd sRGB image. Pixel at [" + jj + ", " + ii + "] should be (" + expectedSRGBColor + "), but the actual color is (" + srgbColor1 + ")"); + } + } + + if (drawbuffersFormatMask & linearMask) { + var linearColor0 = [linearPixels0[index], linearPixels0[index + 1], linearPixels0[index + 2], linearPixels0[index + 3]]; + if (checkPixel(linearColor0, expectedLinearColor) == false) { + failed = true; + debug("Pixel comparison failed for the 1st linear image. Pixel at [" + jj + ", " + ii + "] should be (" + color + "), but the actual color is (" + linearColor0 + ")"); + } + var linearColor1 = [linearPixels1[index], linearPixels1[index + 1], linearPixels1[index + 2], linearPixels1[index + 3]]; + if (checkPixel(linearColor1, expectedLinearColor) == false) { + failed = true; + debug("Pixel comparison failed for the 2nd linear image. Pixel at [" + jj + ", " + ii + "] should be (" + color + "), but the actual color is (" + linearColor1 + ")"); + } + } + } + } + if (failed == false) { + testPassed("All pixels comparision passed!"); + } + + // deinit + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteTexture(tex_srgb0); + gl.deleteTexture(tex_linear0); + gl.deleteTexture(tex_srgb1); + gl.deleteTexture(tex_linear1); + gl.deleteTexture(tex_read); + gl.deleteFramebuffer(fbo_read); + gl.deleteFramebuffer(fbo_draw); +} + +function checkPixel(color, expectedColor) { + var tolerance = 3; + return (Math.abs(color[0] - expectedColor[0]) <= tolerance && + Math.abs(color[1] - expectedColor[1]) <= tolerance && + Math.abs(color[2] - expectedColor[2]) <= tolerance && + Math.abs(color[3] - expectedColor[3]) <= tolerance); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-stencil-only.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-stencil-only.html new file mode 100644 index 0000000000..b14acf3456 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-stencil-only.html @@ -0,0 +1,170 @@ +<!-- +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 BlitFramebuffer Stencil-only Tests</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> + +<script id="vs" type="x-shader/x-vertex">#version 300 es +in vec4 position; +void main() { + gl_Position = position; +} +</script> +<script id="fs" type="x-shader/x-fragment">#version 300 es +out mediump vec4 colorOut; +uniform mediump vec3 color; +void main() { + colorOut = vec4(color, 1.0); +} +</script> + +</head> +<body> +<canvas id="example" width="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test covers some edge cases of blitFramebuffer with stencil."); + +var gl = wtu.create3DContext("example", undefined, 2); + +var program, colorLoc; + +function init_buffer(format) { + var buf = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, buf) + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 16, 16); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + var rbo = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); + gl.renderbufferStorage(gl.RENDERBUFFER, format, 16, 16); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, + gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo); + + gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 1.0, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after buffer init"); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + return { fbo: buf, color: tex, depthStencil: rbo }; +} + +var quadVB; + +function drawQuad(depth) { + if (!quadVB) { + quadVB = gl.createBuffer() + } + + var quadVerts = new Float32Array(3 * 6); + quadVerts[0] = -1.0; quadVerts[1] = 1.0; quadVerts[2] = depth; + quadVerts[3] = -1.0; quadVerts[4] = -1.0; quadVerts[5] = depth; + quadVerts[6] = 1.0; quadVerts[7] = -1.0; quadVerts[8] = depth; + quadVerts[9] = -1.0; quadVerts[10] = 1.0; quadVerts[11] = depth; + quadVerts[12] = 1.0; quadVerts[13] = -1.0; quadVerts[14] = depth; + quadVerts[15] = 1.0; quadVerts[16] = 1.0; quadVerts[17] = depth; + + gl.bindBuffer(gl.ARRAY_BUFFER, quadVB); + gl.bufferData(gl.ARRAY_BUFFER, quadVerts, gl.STATIC_DRAW); + gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 0, 0); + gl.enableVertexAttribArray(0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after drawQuad"); +} + +// Test based on dEQP-GLES3.functional.blit.depth_stencil.depth_24_stencil8_stencil_only +function test_stencil_only_blit(format) { + debug("testing format: " + wtu.glEnumToString(gl, format)) + + var src = init_buffer(format); + var dest = init_buffer(format); + + gl.bindFramebuffer(gl.FRAMEBUFFER, src.fbo); + gl.viewport(0, 0, 16, 16); + + // Fill source with red, depth = 0.5, stencil = 7 + gl.enable(gl.DEPTH_TEST); + gl.enable(gl.STENCIL_TEST); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); + gl.stencilFunc(gl.ALWAYS, 7, 0xFF); + gl.uniform3f(colorLoc, 1.0, 0.0, 0.0); + drawQuad(0.5); + + // Fill dest with yellow, depth = 0.0, stencil = 1 + gl.bindFramebuffer(gl.FRAMEBUFFER, dest.fbo); + gl.stencilFunc(gl.ALWAYS, 1, 0xff); + gl.uniform3f(colorLoc, 1.0, 1.0, 0.0); + drawQuad(0.0); + + // Perform copy. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, src.fbo); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, dest.fbo); + gl.blitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, gl.STENCIL_BUFFER_BIT, gl.NEAREST); + + // Render blue where depth < 0, decrement on depth failure. + gl.bindFramebuffer(gl.FRAMEBUFFER, dest.fbo); + gl.stencilOp(gl.KEEP, gl.DECR, gl.KEEP); + gl.stencilFunc(gl.ALWAYS, 0, 0xff); + + gl.uniform3f(colorLoc, 0.0, 0.0, 1.0); + drawQuad(0.0); + + // Render green where stencil == 6. + gl.disable(gl.DEPTH_TEST); + gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); + gl.stencilFunc(gl.EQUAL, 6, 0xff); + + gl.uniform3f(colorLoc, 0.0, 1.0, 0.0); + drawQuad(0.0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after test"); + wtu.checkCanvasRect(gl, 0, 0, 16, 16, [0, 255, 0, 255], + "stencil test should be green"); + + gl.deleteFramebuffer(src.fbo); + gl.deleteFramebuffer(dest.fbo); + gl.deleteTexture(src.color); + gl.deleteTexture(dest.color); + gl.deleteRenderbuffer(src.depthStencil); + gl.deleteRenderbuffer(dest.depthStencil); +} + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + program = wtu.setupProgram(gl, ["vs", "fs"], ["position"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after program initialization"); + shouldBe('gl.getProgramParameter(program, gl.LINK_STATUS)', 'true'); + + colorLoc = gl.getUniformLocation(program, "color") + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "query uniform location"); + shouldBeNonNull('colorLoc') + + test_stencil_only_blit(gl.DEPTH24_STENCIL8); + test_stencil_only_blit(gl.DEPTH32F_STENCIL8); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-test.html new file mode 100644 index 0000000000..c470b02e4e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-test.html @@ -0,0 +1,361 @@ +<!-- +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 BlitFramebuffer Tests</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of blitFramebuffer for some corner cases."); + +var width = 8; +var height = 8; + +var gl = wtu.create3DContext("example", undefined, 2); +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + blit_framebuffer_repeated(); + blit_framebuffer_feedback_loop(); + blit_framebuffer_multisampling_srgb(); +} + +function blit_framebuffer_repeated() { + debug(""); + debug("This test verifies repeated calls to blitFramebuffer."); + + // Create offscreen fbo and its color attachment. + var tex_2d = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_2d); + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height); + 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); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + var prog = wtu.setupColorQuad(gl, 0); + wtu.setFloatDrawColor(gl, [ 1.0, 0.0, 0.0, 1.0 ]); + wtu.drawUnitQuad(gl); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + wtu.checkCanvas(gl, [ 255, 0, 0, 255 ], "should be red at first"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + wtu.setFloatDrawColor(gl, [ 0.0, 1.0, 0.0, 1.0 ]); + wtu.drawUnitQuad(gl); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + wtu.checkCanvas(gl, [ 0, 255, 0, 255 ], "should be green"); +} + +function blit_framebuffer_feedback_loop() { + + debug(""); + debug("This test checks whether the src resource and dst resource have identical images."); + // Create read fbo and its color attachment. + var tex_2d = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex_2d); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.generateMipmap(gl.TEXTURE_2D); + + var fb0 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + // Create draw fbo and its color attachment. + var rb0 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb0); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, width, height); + + var fb1 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + // Blit framebuffer, all conditions are OK. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed."); + + // Blit framebuffer, the src buffer and the dst buffer should not be identical. + // Exactly the same read/draw fbo + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb0); + gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffer are identical."); + + // Exactly the same read/draw framebuffer: default framebuffer + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffer are identical."); + + // The same image with the same level bound to read/draw buffer. + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw color buffer are identical."); + + // The same image in read/draw buffer, but different levels are bound to read/draw buffer respectively. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 1); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same image with different levels."); + + // The same cube_map image in read/draw buffer, but different faces are bound to read/draw buffer respectively. + var tex_cube_map = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex_cube_map); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0); + if ((gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) || + (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same CUBE_MAP image with different faces."); + + // The same 3D/2D_ARRAY image in read/draw buffer, but different layers are bound to read/draw buffer respectively. + var tex_2d_array = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex_2d_array); + var depth = 2; + gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, width, height, depth, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + var level = 0, layer = 0; + gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_2d_array, level, layer); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + layer = 1; + gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_2d_array, level, layer); + if ((gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) || + (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same 3D/2D_ARRAY image with different layers."); + + // The same image are bound as depth buffer in both read framebuffer and draw framebuffer + var rb1 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0); + gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + // But the mask doesn't have depth buffer bit. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed."); + + // The mask has depth buffer bit. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw framebuffer have identical depth buffer attachment."); + + // The same image are bound as stencil buffer in both read framebuffer and draw framebuffer + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb1); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + // But the mask doesn't have stencil buffer bit. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed."); + + // The mask has stencil buffer bit. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw framebuffer have identical stencil buffer attachment."); + + // The same image are bound as color buffer in both read framebuffer and draw framebuffer + var rb2 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb2); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0); + gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null); + gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb2); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + // But the mask doesn't have color buffer bit. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed."); + + // The mask has color buffer bit, but the same image is not specified as draw buffer. + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed."); + + // The mask has color buffer bit, the same image is specified as both read buffer and draw buffer. + gl.drawBuffers([gl.COLOR_ATTACHENT0, gl.COLOR_ATTACHMENT1]); + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffers have identical color buffer attachment."); + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, null); + gl.bindTexture(gl.TEXTURE_2D_ARRAY, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteTexture(tex_2d); + gl.deleteTexture(tex_cube_map); + gl.deleteTexture(tex_2d_array); + gl.deleteRenderbuffer(rb0); + gl.deleteRenderbuffer(rb1); + gl.deleteRenderbuffer(rb2); + gl.deleteFramebuffer(fb0); + gl.deleteFramebuffer(fb1); +}; + +function blit_framebuffer_multisampling_srgb() { + + debug(""); + debug("This test vefify the functionality of blitframebuffer from or to a multisampled srgb image."); + + // Read buffer can have multisampled srgb image, but draw buffers can not. + var rb0 = gl.createRenderbuffer(); + var fb0 = gl.createFramebuffer(); + var rb1 = gl.createRenderbuffer(); + var fb1 = gl.createFramebuffer(); + var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, gl.SAMPLES); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb0); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.SRGB8_ALPHA8, width, height); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0); + gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0); + + gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, width, height); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE || + gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer from multisampled srgb image should succeed."); + + gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.SRGB8_ALPHA8, width, height); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer to a multisampled srgb image should generate INVALID_OPERATION."); + + // BlitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same. + gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, width, height); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 2, 2, 4, 4, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same."); + + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 4, 4, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same."); + + // BlitFramebuffer from a multisampled srgb image, the format/type must be exactly the same. So blit from a multisampled srgb image to a linear image is invalid. + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the format/type must be exactly the same."); + + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.deleteRenderbuffer(rb0); + gl.deleteRenderbuffer(rb1); + gl.deleteTexture(tex); + gl.deleteFramebuffer(fb0); + gl.deleteFramebuffer(fb1); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-unaffected-by-colormask.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-unaffected-by-colormask.html new file mode 100644 index 0000000000..3d2d7f54bc --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/blitframebuffer-unaffected-by-colormask.html @@ -0,0 +1,102 @@ +<!-- +Copyright (c) 2021 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>BlitFramebuffer Should Be Unaffected by ColorMask</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="canvas" width="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +const wtu = WebGLTestUtils; +description("This test verifies that the blitFramebuffer is unaffected by the colorMask state."); + +debug('Regression test for <a href="https://crbug.com/1257769">https://crbug.com/1257769</a> and <a href="https://bugs.webkit.org/show_bug.cgi?id=220129">https://bugs.webkit.org/show_bug.cgi?id=220129</a>'); + +function allocateTexture(gl, size) { + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + return tex; +} + +function allocateFBO(gl, tex) { + const fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + return fbo; +} + +function run() { + const gl = wtu.create3DContext("canvas", { antialias: false }, 2); + + if (!gl) { + testFailed("WebGL context does not exist"); + finishTest(); + return; + } + + const size = 8; + + testPassed("WebGL context exists"); + + // Allocate source and destination textures and framebuffer objects. + const sourceTex = allocateTexture(gl, size); + const sourceFBO = allocateFBO(gl, sourceTex); + + const destTex = allocateTexture(gl, size); + const destFBO = allocateFBO(gl, destTex); + + const program = wtu.setupColorQuad(gl); + + gl.bindFramebuffer(gl.FRAMEBUFFER, sourceFBO); + + // Clear the source framebuffer to red. + gl.clearColor(1, 0, 0, 1); + gl.colorMask(true, true, true, true); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Draw a transparent green quad. + gl.useProgram(program); + wtu.drawFloatColorQuad(gl, [ 0, 255, 0, 0 ]); + + // Clear the alpha channel. + gl.colorMask(false, false, false, true); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + // At this point, even setting the colorMask to all-true won't + // work around the bug, since that state is latched inside ANGLE + // only during draws / clears. + + // Blit source to dest. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, destFBO); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, destFBO); + + // Note that the on-screen canvas is always black - we don't blit the result to it. + wtu.checkCanvas(gl, [ 0, 255, 0, 255 ], "should be green", 1); + finishTest(); +} + +var successfullyParsed = true; + +requestAnimationFrame(run); + +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html new file mode 100644 index 0000000000..cc64c9034b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html @@ -0,0 +1,408 @@ +<!-- +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. +--> + +<!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=e_canvas width=1 height=1 style="width: 100px; height: 100px;"></canvas> +<div id=description></div> +<div id=console></div> +<script> +"use strict"; +description('gl_VertexID and gl_InstanceID should behave as per spec.'); + +// + +/* +So what are gl_VertexID and gl_InstanceID supposed to do? +In ES 3.0 and GL 4.1 (Core), this is all we get: + +# ES 3.0 + +> (p78) gl_VertexID holds the integer index i implicitly passed by DrawArrays or +> one of the other drawing commands defined in section 2.9.3. The value of +> gl_VertexID is defined if and only if all enabled vertex arrays have non-zero buffer +> object bindings. +> gl_InstanceID holds the integer instance number of the current primitive in +> an instanced draw call (see section 2.9.3). + + +# GL 4.1 (Core) + +> (p102) gl_VertexID holds the integer index i implicitly passed by DrawArrays or +> one of the other drawing commands defined in section 2.8.3. +> gl_InstanceID holds the integer index of the current primitive in an +> instanced draw call (see section 2.8.3). + + +# ES 3.1 + +ES 3.1 retains the wording from ES 3.0, but adds the following clarifications: + +gl_VertexID: +> (p252) The index of any element transferred to the GL by DrawArraysOneInstance +> is referred to as its vertex ID, and may be read by a vertex shader as gl_VertexID. +> The vertex ID of the ith element transferred is first + i. + +> (p254) The index of any element transferred to the GL by +> DrawElementsOneInstance is referred to as its vertex ID, and may be read by a vertex shader as +> gl_VertexID. If no element array buffer is bound, the vertex ID of the ith element +> transferred is indices[i] + basevertex. Otherwise, the vertex ID of the ith +> element transferred is the sum of basevertex and the value stored in the currently +> bound element array buffer at offset indices + i. + +gl_InstanceID +> (p255) If an enabled vertex attribute array is instanced (it has a non-zero divisor as +> specified by VertexAttribDivisor), the element index that is transferred to the GL, +> for all vertices, is given by +> `floor(instance / divisor) + baseinstance` + + +# Errata + +Drivers generally do implement the ES 3.1 behavior. +A notable exception is Mac's legacy GL (4.1) driver which has two bugs here. +(Both ANGLE-on-Metal and the system M1+ GL driver seem correct though) + +## gl_InstanceID random for DrawArrays calls +Use ERRATA.IGNORE_GL_INSTANCE_ID to cause these tests to pass. + +## Adds `first` to user-attrib instanced fetch ids in DrawArrays calls. +Use ERRATA.FIRST_ADDS_TO_INSTANCE to cause these tests to pass. +*/ + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext('e_canvas'); + +const ERRATA = {}; +//ERRATA.IGNORE_GL_INSTANCE_ID = true; // Chrome on ANGLE-on-Mac-GL needs this. +//ERRATA.FIRST_ADDS_TO_INSTANCE = true; // Firefox with MOZ_WEBGL_WORKAROUND_FIRST_AFFECTS_INSTANCE_ID=0 would need this. + +debug(`ERRATA: ${JSON.stringify(ERRATA)}`); + +function make_vs_point(vid, iid) { + return `\ + #version 300 es + + ${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name}; + ${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name}; + out vec4 v_color; + + void main() { + gl_PointSize = 1.0; + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); + v_color = vec4(1.0, float(${vid.name}) / 255.0, float(${iid.name}) / 255.0, 1.0); +#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0} + v_color.b = 0.0; +#endif + }`; +} + +function make_vs_tri(vid, iid) { + return `\ + #version 300 es + + ${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name}; + ${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name}; + out vec4 v_color; + + void main() { + int prim_vert_id = ${vid.name} % 3; + int flat_vert_id = ${vid.name} - prim_vert_id + 2; + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); + gl_Position.x = (prim_vert_id == 1) ? 2.0 : -1.0; + gl_Position.y = (prim_vert_id == 2) ? 2.0 : -1.0; + v_color = vec4(1.0, float(flat_vert_id) / 255.0, float(${iid.name}) / 255.0, 1.0); +#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0} + v_color.b = 0.0; +#endif + }`; +} + +const FS = `\ + #version 300 es + precision mediump float; + + in vec4 v_color; + out vec4 o_color; + + void main() { + o_color = v_color; + } +`; + + +function crossCombine(...args) { + function crossCombine2(listA, listB) { + const listC = []; + for (const a of listA) { + for (const b of listB) { + const c = Object.assign({}, a, b); + listC.push(c); + } + } + return listC; + } + + let res = [{}]; + while (args.length) { + const next = args.shift(); + next[0].defined; + res = crossCombine2(res, next); + } + return res; +} + +/// makeCombiner('foo', [5, 3]) -> [{foo: 5}, {foo: 3}] +function makeCombiner(key, vals) { + const ret = []; + for (const val of vals) { + const cur = {}; + cur[key] = val; + ret.push(cur); + } + return ret; +} + +debug('Draw a point with a shader that takes no attributes and verify it fills the whole canvas.'); + + +let TESTS = [ + makeCombiner('vid', [ + {name: 'a_VertexID', loc:0}, + {name: 'a_VertexID', loc:2}, // Test 2, so that we're not only testing 0. + {name: 'gl_VertexID', loc:-1}, + {name: 'gl_VertexID', loc:0}, // Enable a vertex array, despite not using it. + {name: 'gl_VertexID', loc:2}, // Enable a vertex array, despite not using it. + ]), + makeCombiner('iid', [ + {name: 'a_InstanceID', loc:1}, + {name: 'gl_InstanceID', loc:-1}, + {name: 'gl_InstanceID', loc:1}, // Enable a vertex array, despite not using it. + ]), + makeCombiner('separate_vbufs', [true, false]), +]; +//console.log('a', {TESTS}); +TESTS = crossCombine(...TESTS); +//console.log('b', {TESTS}); + + +let vdata = new Int32Array(1000); +vdata = vdata.map((v,i) => i); +const vbuf = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); +gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW); + + +const vbuf2 = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2); +gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW); + + +let index_data = new Uint32Array(1000); +index_data = index_data.map((x,i) => 10+i); +const index_buffer = gl.createBuffer(); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); +gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_data, gl.STATIC_DRAW); + + +gl.disable(gl.DEPTH_TEST); + +(async () => { + for (const desc of TESTS) { + await wtu.dispatchPromise(); // Yield, for responsiveness. + debug(''); + debug('---------------------'); + debug(`desc: ${JSON.stringify(desc)}`); + + let fn = (vs) => { + //console.log({vs}); + const prog = wtu.setupProgram(gl, [vs, FS]); + + { + const WEBGL_debug_shaders = gl.getExtension('WEBGL_debug_shaders'); + let i = -1; + for (const s of gl.getAttachedShaders(prog)) { + i += 1; + debug(''); + debug(`shader[${i}] getShaderSource() -> `); + debug(gl.getShaderSource(s)); + if (WEBGL_debug_shaders) { + debug(`shader[${i}] getTranslatedShaderSource() -> `); + debug(WEBGL_debug_shaders.getTranslatedShaderSource(s)); + } + } + } + return prog; + }; + const point_prog = fn(make_vs_point(desc.vid, desc.iid)); + const tri_prog = fn(make_vs_tri(desc.vid, desc.iid)); + + // - + + gl.bindBuffer(gl.ARRAY_BUFFER, null); + for (let i = 0; i <= 2; i++) { + gl.disableVertexAttribArray(i); + gl.vertexAttribPointer(i, 4, gl.FLOAT, false, 0, 0); + gl.vertexAttribDivisor(i, 0); + } + + gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); + let loc = desc.vid.loc; + if (loc != -1) { + gl.enableVertexAttribArray(loc); + gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0); + }; + + if (desc.separate_vbufs) { + gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2); + } + loc = desc.iid.loc; + if (loc != -1) { + gl.enableVertexAttribArray(loc); + gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0); + gl.vertexAttribDivisor(loc, 1); + }; + + { + const err = gl.getError(); + if (err) throw err; // Broken init. + } + + // - + + fn = (eval_str, expected_arr) => { + if (ERRATA.IGNORE_GL_INSTANCE_ID) { + if (desc.iid.name == 'gl_InstanceID') { + expected_arr = expected_arr.map(x => x); + expected_arr[2] = 0; + } + } + + debug(''); + //debug(`${eval_str} -> [${expected_arr.join(', ')}]`); + eval(eval_str); + + const err = gl.getError(); + if (err) throw err; // Broken subtest. + + wtu.checkCanvas(gl, expected_arr, eval_str); + } + + gl.useProgram(point_prog); + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArrays(gl.POINTS, 0, 0)`, [0, 0, 0, 0]); + fn(`gl.drawArrays(gl.POINTS, 0, 1)`, [255, 0, 0, 255]); + fn(`gl.drawArrays(gl.POINTS, 0, 2)`, [255, 1, 0, 255]); + if (ERRATA.FIRST_ADDS_TO_INSTANCE) { + fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 100, 255]); + } else { + fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 0, 255]); + } + fn(`gl.drawArrays(gl.POINTS, 0, 255)`, [255, 254, 0, 255]); + fn(`gl.drawArrays(gl.POINTS, 0, 256)`, [255, 255, 0, 255]); + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 0, 1)`, [0, 0, 0, 0]); + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 0)`, [0, 0, 0, 0]); + + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 1)`, [255, 0, 0, 255]); + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 1)`, [255, 1, 0, 255]); + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 2)`, [255, 0, 1, 255]); + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 2)`, [255, 1, 1, 255]); + if (ERRATA.FIRST_ADDS_TO_INSTANCE) { + fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 101, 255]); + } else { + fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 1, 255]); + } + fn(`gl.drawArraysInstanced(gl.POINTS, 0, 255, 255)`, [255, 254, 254, 255]); + + // - + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]); + fn(`gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0)`, [255, 10+0, 0, 255]); + fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0)`, [255, 10+1, 0, 255]); + fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+1, 0, 255]); + fn(`gl.drawElements(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0)`, [255, 10+244, 0, 255]); + fn(`gl.drawElements(gl.POINTS, 246, gl.UNSIGNED_INT, 4*0)`, [255, 10+245, 0, 255]); + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElementsInstanced(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]); + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]); + + fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+0, 0, 255]); + fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1, 0, 255]); + fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+0, 1, 255]); + fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1, 1, 255]); + fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+1, 1, 255]); + fn(`gl.drawElementsInstanced(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0, 255)`, [255, 10+244, 254, 255]); + + // - + + gl.useProgram(tri_prog); + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArrays(gl.TRIANGLES, 0, 0*3)`, [0, 0, 0, 0]); + fn(`gl.drawArrays(gl.TRIANGLES, 0, 1*3)`, [255, 1*3-1, 0, 255]); + fn(`gl.drawArrays(gl.TRIANGLES, 0, 2*3)`, [255, 2*3-1, 0, 255]); + if (ERRATA.FIRST_ADDS_TO_INSTANCE) { + fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 90, 255]); + } else { + fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 0, 255]); + } + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 0, 1)`, [0, 0, 0, 0]); + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 0)`, [0, 0, 0, 0]); + + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 1)`, [255, 1*3-1, 0, 255]); + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 1)`, [255, 2*3-1, 0, 255]); + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 2)`, [255, 1*3-1, 1, 255]); + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 2)`, [255, 2*3-1, 1, 255]); + if (ERRATA.FIRST_ADDS_TO_INSTANCE) { + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 91, 255]); + } else { + fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 1, 255]); + } + + // - + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElements(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]); + fn(`gl.drawElements(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+1*3-1, 0, 255]); + fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+2*3-1, 0, 255]); + fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+2*3-1, 0, 255]); + + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]); + gl.clear(gl.COLOR_BUFFER_BIT); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]); + + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1*3-1, 0, 255]); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+2*3-1, 0, 255]); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1*3-1, 1, 255]); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+2*3-1, 1, 255]); + fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+2*3-1, 1, 255]); + } + + finishTest(); +})(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/canvas-resizing-with-pbo-bound.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/canvas-resizing-with-pbo-bound.html new file mode 100644 index 0000000000..713c88f515 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/canvas-resizing-with-pbo-bound.html @@ -0,0 +1,111 @@ +<!-- +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 Resizing With PBO Bound 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> +<canvas id="canvas1" style="width: 256px; height: 256px;"> </canvas> +<canvas id="canvas2" style="width: 256px; height: 256px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; + +description("Verifies that resizing the canvas (recreating the backing framebuffer) works correctly while a PBO is bound."); + +debug(""); +debug("Regression test for Chromium <a href='https://bugs.chromium.org/p/chromium/issues/detail?id=644572'>Issue 644572</a>"); +debug(""); + +var wtu = WebGLTestUtils; +var canvas; +var largeSize = 256; +var smallSize = 128; +var currentSize; +var gl; +var numFrames = 0; +var testNumber = 0; +var pbo; + +function nextTest() { + ++testNumber; + numFrames = 0; + currentSize = largeSize; + if (testNumber > 2) { + finishTest(); + return; + } + + canvas = document.getElementById("canvas" + testNumber); + canvas.width = currentSize; + canvas.height = currentSize; + var usePreserveDrawingBuffer = (testNumber == 1) ? true : false; + debug("Testing preserveDrawingBuffer = " + usePreserveDrawingBuffer); + gl = wtu.create3DContext(canvas, { preserveDrawingBuffer: usePreserveDrawingBuffer }, 2); + + if (!gl) { + testFailed("context does not exist"); + + wtu.requestAnimFrame(nextTest); + } else { + testPassed("context exists"); + + gl.clearColor(0, 1, 0, 1); + + pbo = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo); + + wtu.requestAnimFrame(render); + } +} + +function render() { + if (++numFrames < 4) { + if (currentSize == largeSize) { + canvas.height = smallSize; + currentSize = smallSize; + } else { + canvas.height = largeSize; + currentSize = largeSize; + } + } + + gl.viewport(0, 0, largeSize, currentSize); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Check the four corners + var green = [ 0, 255, 0, 255 ]; + var inset = 3; + wtu.checkCanvasRect(gl, inset, inset, 1, 1, green, "lower left should be green", 1); + wtu.checkCanvasRect(gl, largeSize - inset, inset, 1, 1, green, "lower right should be green", 1); + wtu.checkCanvasRect(gl, inset, currentSize - inset, 1, 1, green, "upper left should be green", 1); + wtu.checkCanvasRect(gl, largeSize - inset, currentSize - inset, 1, 1, green, "upper right should be green", 1); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error"); + if (gl.getParameter(gl.PIXEL_UNPACK_BUFFER_BINDING) != pbo) { + testFailed("Pixel unpack buffer binding was lost"); + } + + if (numFrames < 4) { + wtu.requestAnimFrame(render); + } else { + wtu.requestAnimFrame(nextTest); + } +} + +wtu.requestAnimFrame(nextTest); + +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-func-buffer-type-match.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-func-buffer-type-match.html new file mode 100644 index 0000000000..8054d74df2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-func-buffer-type-match.html @@ -0,0 +1,145 @@ +<!-- +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 clear and clearBuffer functions have to match fbo's buffer format</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> +"use strict"; +description("This tests the WebGL2 specific constraint that clear or clearBuffer* functions have to be compatible with fbo's buffer format"); + +var setupRenderbuffer = function(attachment, format) { + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, format, canvas.width, canvas.height); + return renderbuffer; +} + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, undefined, 2); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + debug(""); + debug("Signed integer buffer"); + + var colorbuffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0, gl.RGBA8); + var colorbuffer1 = setupRenderbuffer(gl.COLOR_ATTACHMENT1, gl.RGBA32I); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clear and INT buffer"); + + gl.clearBufferfv(gl.COLOR, 1, new Float32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferfv and INT buffer"); + + gl.clearBufferuiv(gl.COLOR, 1, new Uint32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferuiv and INT buffer"); + + debug("Set up draw buffer so INT buffer is set to NONE"); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clear and INT buffer is NONE"); + + gl.clearBufferfv(gl.COLOR, 1, new Float32Array(4)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBufferfv and INT buffer is NONE"); + + gl.clearBufferuiv(gl.COLOR, 0, new Uint32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferuiv and float buffer"); + + debug(""); + debug("Unsigned integer buffer"); + + colorbuffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0, gl.RGBA32UI); + colorbuffer1 = setupRenderbuffer(gl.COLOR_ATTACHMENT1, gl.RGBA8); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clear and UINT buffer"); + + gl.clearBufferfv(gl.COLOR, 0, new Float32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferfv and UINT buffer"); + + gl.clearBufferiv(gl.COLOR, 0, new Int32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferiv and UINT buffer"); + + debug("Set up draw buffer so INT buffer is set to NONE"); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clear and UINT buffer is NONE"); + + gl.clearBufferfv(gl.COLOR, 0, new Float32Array(4)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBufferfv and UINT buffer is NONE"); + + gl.clearBufferiv(gl.COLOR, 1, new Int32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferiv and float buffer"); + + debug(""); + debug("Float buffer"); + + colorbuffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0, gl.RGBA8); + var numAttachments = 1; + var ext = gl.getExtension("EXT_color_buffer_float"); + var bufferType = "float buffer"; + if (ext) { + debug("EXT_color_buffer_float is available: testing RGBA8 + RGBA32F"); + colorbuffer1 = setupRenderbuffer(gl.COLOR_ATTACHMENT1, gl.RGBA32F); + ++numAttachments; + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + } else { + debug("EXT_color_buffer_float is unavailable: testing RGBA8"); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + bufferType = "RGBA8 buffer"; + } + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clear and " + bufferType); + + for (var ii = 0; ii < numAttachments; ++ii) { + gl.clearBufferfv(gl.COLOR, ii, new Float32Array(4)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBufferfv and " + bufferType); + + gl.clearBufferiv(gl.COLOR, ii, new Int32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferiv and " + bufferType); + + gl.clearBufferuiv(gl.COLOR, ii, new Uint32Array(4)); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "clearBufferuiv and " + bufferType); + } + + gl.deleteFramebuffer(fb); + gl.deleteRenderbuffer(colorbuffer); + gl.deleteRenderbuffer(colorbuffer1); +} + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error"); +var successfullyParsed = true; + +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-srgb-color-buffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-srgb-color-buffer.html new file mode 100644 index 0000000000..b88518635e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clear-srgb-color-buffer.html @@ -0,0 +1,89 @@ +<!-- +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>Clear sRGB Color 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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of clearing srgb color buffer."); + +var gl = wtu.create3DContext("example", undefined, 2); + +var tex = gl.createTexture(); +var fbo = gl.createFramebuffer(); +var size = 8; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Create a srgb color buffer + init(); + + clear_srgb_color_buffer(0); + clear_srgb_color_buffer(1); +} + +function init() { + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } else { + testPassed("framebuffer complete!"); + } +} + +function clear_srgb_color_buffer(iter) { + debug(""); + debug("Clear sRGB color buffer through glClear or glClearBufferfv"); + + var color = [0x33, 0x88, 0xbb, 0xff]; + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + + if (iter == 0) { + gl.clearColor(color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); + gl.clear(gl.COLOR_BUFFER_BIT); + } else { + var data = new Float32Array([color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255]); + gl.clearBufferfv(gl.COLOR, 0, data); + } + + var color_ref = wtu.linearToSRGB(color); + var tolerance = 3; + var msg = ""; + wtu.checkCanvasRect(gl, 0, 0, size, size, color_ref, msg, tolerance); +} + +gl.bindTexture(gl.TEXTURE_2D, null); +gl.bindFramebuffer(gl.FRAMEBUFFER, null); +gl.deleteTexture(tex); +gl.deleteFramebuffer(fbo); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-and-draw.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-and-draw.html new file mode 100644 index 0000000000..051066c8ea --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-and-draw.html @@ -0,0 +1,216 @@ +<!-- +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 clearBuffer with drawing</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 onload="runTest()"> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="64" height="64" style="position:fixed;left:0;top:0"> </canvas> +<script> +"use strict"; +description("This tests the operation of clearBuffer followed by a draw call."); + +debug("Verifies that these combined with preserveDrawingBuffer's implicit clears work properly together."); +debug("Regression test for <a href='http://crbug.com/828262'>Chromium bug 828262</a>."); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl; +var testIndex = 0; +var iterations = 0; +var maxIterations = 10; +var prog; +var tests; +var stage = 0; +var blackUint8 = [0, 0, 0, 255]; +var redFloat = [1.0, 0.0, 0.0, 0.0]; +var redUint8 = [255, 0, 0, 255]; +var greenFloat = [0.0, 1.0, 0.0, 0.0]; +var greenUint8 = [0, 255, 0, 255]; + +function verifyOnePixel(kind, x, y, readFormat, readType, arrayType, expectedColor) { + var buffer = new arrayType(4); + gl.readPixels(Math.floor(x), Math.floor(y), 1, 1, readFormat, readType, buffer); + if (buffer[0] == expectedColor[0] && + buffer[1] == expectedColor[1] && + buffer[2] == expectedColor[2] && + buffer[3] == expectedColor[3]) { + testPassed(kind + " succeeded"); + } else { + testFailed(kind + " failed. Expected: " + expectedColor + ", got: " + buffer); + } +} + +function testClearBufferAndDraw(test) { + gl.stencilFunc(gl.EQUAL, 0, 0xFF); + test['clear'](); + wtu.setFloatDrawColor(gl, greenFloat); + wtu.drawUnitQuad(gl); + // Back buffer has no alpha channel. + let readFormat = gl.RGBA; + let readType = gl.UNSIGNED_BYTE; + if (stage == 2) { + verifyOnePixel("Clearing outside scissor",63, 63, readFormat, readType, Uint8Array, blackUint8); + verifyOnePixel("Drawing outside scissor", 40, 40, readFormat, readType, Uint8Array, blackUint8); + } + verifyOnePixel("Clearing", 0, 0, readFormat, readType, Uint8Array, test['bgColor']); + verifyOnePixel("Drawing", 32, 32, readFormat, readType, Uint8Array, test['drawColor']); +} + +function runNextTest() { + if (testIndex >= tests.length) { + // Restore after the last clearBufferiv test + gl.enable(gl.DEPTH_TEST); + if (stage == 0) { + debug(''); + debug('Enabling full-canvas scissor'); + gl.enable(gl.SCISSOR_TEST); + } else if (stage == 1) { + debug(''); + debug('Limiting scissor rect'); + gl.scissor(0, 0, 33, 33); + } else if (stage == 2) { + finishTest(); + return; + } + testIndex = 0; + stage++; + } + + + let test = tests[testIndex]; + if (iterations == 0) { + debug(''); + debug('Testing: ' + test['desc']) + } + testClearBufferAndDraw(test); + + if (++iterations == maxIterations) { + iterations = 0; + ++testIndex; + + // Clear to yellow between the tests to ensure that + // subsequent tests do not rely on past results. + gl.clearColor(1.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + } + + wtu.waitForComposite(runNextTest); +} + +function runTest() { + gl = wtu.create3DContext(canvas, { alpha: false, stencil: true }, 2); + + if (!gl) { + testFailed("context does not exist"); + return; + } else { + testPassed("context exists"); + } + + prog = wtu.setupColorQuad(gl, 0, { scale: 0.5 }); + + tests = [ + { + desc: 'Implicit clear', + clear: function() {}, + bgColor: blackUint8, + // The implicit clear clears depth to 1.0, and since the quad is + // drawn at a depth of 0.0, it's always discarded. + drawColor: blackUint8, + }, + { + desc: 'clearBufferfi only', + clear: function() { + gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 0.0, 1); + gl.stencilFunc(gl.EQUAL, 1, 0xFF); + }, + bgColor: blackUint8, + drawColor: greenUint8, + }, + { + desc: 'clearBufferfv only', + clear: function() { + gl.clearBufferfv(gl.DEPTH, 0, [0.0]); + }, + bgColor: blackUint8, + drawColor: greenUint8, + }, + { + desc: 'clearBufferfv and clear', + clear: function() { + gl.clearBufferfv(gl.COLOR, 0, redFloat); + gl.clearDepth(0.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + }, + bgColor: redUint8, + drawColor: greenUint8, + }, + { + desc: 'clearBufferfv (no-op) and clear', + clear: function() { + gl.clearBufferfv(gl.COLOR, 1, greenFloat); + gl.clearDepth(0.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + }, + bgColor: blackUint8, + drawColor: greenUint8, + }, + { + desc: 'clearBuffer{fv} and {fi}', + clear: function() { + gl.clearBufferfv(gl.COLOR, 0, redFloat); + gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 0.0, 2); + gl.stencilFunc(gl.EQUAL, 2, 0xFF); + }, + bgColor: redUint8, + drawColor: greenUint8, + }, + { + desc: 'clearBufferiv only', + clear: function() { + gl.disable(gl.DEPTH_TEST); + gl.clearBufferiv(gl.STENCIL, 0, [3]); + gl.stencilFunc(gl.EQUAL, 3, 0xFF); + }, + bgColor: blackUint8, + drawColor: greenUint8, + }, + ]; + + // Clear canvas to something other than black to start. + gl.clearColor(0.0, 0.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.enable(gl.DEPTH_TEST); + // Unreal Engine's depth test is reversed from the + // default. Including the clear of the depth buffer in this test + // case highlights the rendering error more clearly, since neither + // the background nor any rendered object show up. + gl.depthFunc(gl.GEQUAL); + + gl.enable(gl.STENCIL_TEST); + + // Must run in a requestAnimationFrame loop to provoke implicit + // clears of the canvas. + wtu.waitForComposite(runNextTest); +} + +debug(""); +var successfullyParsed = true; + +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-sub-source.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-sub-source.html new file mode 100644 index 0000000000..616d2bda4c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbuffer-sub-source.html @@ -0,0 +1,110 @@ +<!-- +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 clearBuffer functions with optional srcOffset argument</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> +"use strict"; +description("This tests clearBuffer* functions with optional srcOffset argument"); + +function verifyOnePixel(readFormat, readType, arrayType, expectedColor) { + var buffer = new arrayType(4); + gl.readPixels(0, 0, 1, 1, readFormat, readType, buffer); + if (buffer[0] == expectedColor[0] && + buffer[1] == expectedColor[1] && + buffer[2] == expectedColor[2] && + buffer[3] == expectedColor[3]) { + testPassed("clearBuffer sets the renderbuffer with the correct data"); + } else { + testFailed("clearBuffer fails to work. Expected: " + expectedColor + ", got: " + buffer); + } +} + +function testClearBuffer(func, format, arrayType, readFormat, readType) { + debug(""); + debug("Testing " + func); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, format, canvas.width, canvas.height); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + var srcData = new arrayType([1, 2, 3, 4, 5, 6]); + gl[func](gl.COLOR, 0, srcData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBuffer with no srcOffset should succeed"); + verifyOnePixel(readFormat, readType, arrayType, [1,2,3,4]); + + gl[func](gl.COLOR, 0, srcData, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBuffer with srcOffset = 0 should succeed"); + verifyOnePixel(readFormat, readType, arrayType, [1,2,3,4]); + + gl[func](gl.COLOR, 0, srcData, 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBuffer with srcOffset = 2 should succeed"); + verifyOnePixel(readFormat, readType, arrayType, [3,4, 5, 6]); + + gl[func](gl.COLOR, 0, srcData, 4); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "clearBuffer with srcOffset = 4 should fail: out of bounds"); + + gl.deleteFramebuffer(fb); + gl.deleteRenderbuffer(renderbuffer); +} + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, undefined, 2); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var testCases = [ + { + func: "clearBufferiv", format: gl.RGBA32I, arrayType: Int32Array, + readFormat: gl.RGBA_INTEGER, readType: gl.INT, + }, + { + func: "clearBufferuiv", format: gl.RGBA32UI, arrayType: Uint32Array, + readFormat: gl.RGBA_INTEGER, readType: gl.UNSIGNED_INT, + }, + { + func: "clearBufferfv", format: gl.RGBA32F, arrayType: Float32Array, + readFormat: gl.RGBA, readType: gl.FLOAT, + extension: "EXT_color_buffer_float", + }, + ]; + + for (var tt = 0; tt < testCases.length; ++tt) { + var test = testCases[tt]; + if (test.extension && !gl.getExtension(test.extension)) + continue; + testClearBuffer(test.func, test.format, test.arrayType, test.readFormat, test.readType); + } +} + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error"); +var successfullyParsed = true; + +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbufferfv-with-alpha-false.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbufferfv-with-alpha-false.html new file mode 100644 index 0000000000..a50b6f8e2f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clearbufferfv-with-alpha-false.html @@ -0,0 +1,80 @@ +<!-- +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 clearBufferfv with alpha:false canvas</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> +"use strict"; +description("This tests the operation of clearBufferfv with the back buffer of an alpha:false canvas."); + +function verifyOnePixel(readFormat, readType, arrayType, expectedColor) { + var buffer = new arrayType(4); + gl.readPixels(0, 0, 1, 1, readFormat, readType, buffer); + if (buffer[0] == expectedColor[0] && + buffer[1] == expectedColor[1] && + buffer[2] == expectedColor[2] && + buffer[3] == expectedColor[3]) { + testPassed("clearBufferfv set the color buffer to the correct value"); + } else { + testFailed("clearBufferfv failed to work. Expected: " + expectedColor + ", got: " + buffer); + } +} + +function testClearBuffer(func, format, arrayType, readFormat, readType, readArrayType) { + debug(""); + debug("Testing " + func); + + var srcData = new arrayType([0, 1, 0, 0]); + gl[func](gl.COLOR, 0, srcData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBuffer with no srcOffset should succeed"); + // Back buffer has no alpha channel + verifyOnePixel(readFormat, readType, Uint8Array, [0, 255, 0, 255]); +} + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, { alpha:false }, 2); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var testCases = [ + { + func: "clearBufferfv", format: gl.RGBA, arrayType: Float32Array, + readFormat: gl.RGBA, readType: gl.UNSIGNED_BYTE, readArrayType: Uint8Array, + }, + ]; + + for (var tt = 0; tt < testCases.length; ++tt) { + var test = testCases[tt]; + if (test.extension && !gl.getExtension(test.extension)) + continue; + testClearBuffer(test.func, test.format, test.arrayType, + test.readFormat, test.readType, test.readArrayType); + } +} + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error"); +var successfullyParsed = true; + +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clipping-wide-points.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clipping-wide-points.html new file mode 100644 index 0000000000..ab2457a6a6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/clipping-wide-points.html @@ -0,0 +1,26 @@ +<!-- +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> +<title>Clipping wide points test</title> +<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="testbed" width="1" height="1"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +var contextVersion = 2; +</script> +<script src="../../js/tests/clipping-wide-points.js"></script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/depth-stencil-feedback-loop.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/depth-stencil-feedback-loop.html new file mode 100644 index 0000000000..e7678017ee --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/depth-stencil-feedback-loop.html @@ -0,0 +1,165 @@ +<!-- +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 Rendering and Sampling Feedback Loop Tests for Depth/Stencil 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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script id="vshader" type="x-shader/x-vertex">#version 300 es +in highp vec4 aPosition; +in vec2 aTexCoord; +out vec2 texCoord; +void main() { + gl_Position = aPosition; + texCoord = aTexCoord; +} +</script> + +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +uniform sampler2D tex; +in vec2 texCoord; +out vec4 oColor; +void main() { + oColor = texture(tex, texCoord); +} +</script> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of rendering to the same texture where it samples from."); + +var gl = wtu.create3DContext("example", undefined, 2); + +var width = 8; +var height = 8; +var tex0; +var tex1; +var tex2; +var fbo; +var program; +var positionLoc; +var texCoordLoc; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + init(); + detect_depth_stencil_feedback_loop(); + deinit(); +} + +function init() { + // Setup program + program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['aPosition', 'aTexCoord'], [0, 1]); + positionLoc = gl.getAttribLocation(program, "aPosition"); + texCoordLoc = gl.getAttribLocation(program, "aTexCoord"); + if (!program || positionLoc < 0 || texCoordLoc < 0) { + testFailed("Set up program failed"); + return; + } + testPassed("Set up program succeeded"); + + wtu.setupUnitQuad(gl, 0, 1); + gl.viewport(0, 0, width, height); + + var texLoc = gl.getUniformLocation(program, "tex"); + gl.uniform1i(texLoc, 0); + + // Create textures and allocate storage + tex0 = gl.createTexture(); + tex1 = gl.createTexture(); + tex2 = gl.createTexture(); + wtu.fillTexture(gl, tex0, width, height, [0x0, 0xff, 0x0, 0xff], 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.RGBA); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + wtu.fillTexture(gl, tex1, width, height, [0x80], 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, gl.DEPTH_COMPONENT16); + 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_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + wtu.fillTexture(gl, tex2, width, height, [0x40], 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, gl.DEPTH24_STENCIL8); + 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_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Succeed to create textures."); + + fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex0, 0); +} + +function detect_depth_stencil_feedback_loop() { + // Test rendering and sampling feedback loop for depth buffer + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, tex1, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + gl.enable(gl.DEPTH_TEST); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test samples from a image. The same image is used as depth buffer during rendering."); + + gl.depthMask(gl.FALSE); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test samples from a image. The same image is used as depth buffer. A feedback loop is formed regardless of the status of depth mask."); + + gl.depthMask(gl.TRUE); + gl.disable(gl.DEPTH_TEST); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test samples from a image. The same image is used as depth buffer. A feedback loop is formed regardless of whether the depth test is enabled."); + + // Test rendering and sampling feedback loop for stencil buffer + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, null, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, tex2, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + gl.enable(gl.STENCIL_TEST); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test samples from a image. The same image is used as stencil buffer during rendering."); + + gl.stencilMask(0x0); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test sampls from a image. The same image is used as stencil buffer. A feedback loop is formed regardless of the status of stencil mask."); + + gl.stencilMask(0xffff); + gl.disable(gl.STENCIL_TEST); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "The test samples from a image. The same image is used as stencil buffer. A feedback loop is formed regardless of whether the stencil test is enabled."); +} + +function deinit() { + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteTexture(tex0); + gl.deleteTexture(tex1); + gl.deleteTexture(tex2); + gl.deleteFramebuffer(fbo); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-dirty-state-bug.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-dirty-state-bug.html new file mode 100644 index 0000000000..2b54d4c255 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-dirty-state-bug.html @@ -0,0 +1,111 @@ +<!-- +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 Draw Buffers Dirty State Bug Conformance Tests</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> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +in vec4 a_position; +void main() { + gl_Position = a_position; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; + +out vec4 my_FragColor; +void main() { + my_FragColor = vec4(1, 0, 0, 1); +} +</script> +<script> +"use strict"; +description("This test verifies a bug in draw buffers dirty state management in Chrome (crbug.com/678153)."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runTest(); + + debug(""); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runTest() { + debug(""); + + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_position"]); + wtu.setupUnitQuad(gl); + + var width = 2, height = 2; + + var colorTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, colorTex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + var depthTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, depthTex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT24, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup should cause no GL errors"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Clear and draw should cause no GL errors"); + wtu.checkCanvasRect(gl, 0, 0, width, height, [255, 0, 0, 255], "should be red"); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, null, 0); + + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Clear and draw should cause no GL errors"); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, null, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0); + + wtu.checkCanvasRect(gl, 0, 0, width, height, [255, 0, 0, 255], "should be red"); + + gl.clearColor(0.0, 1.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + // Previously in Chrome, switching around the attachments on a framebuffer + // affected a DrawBuffers cache that was maintained properly during draw + // calls, but not clears. + wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], "should be green"); + + gl.deleteProgram(program); + gl.deleteFramebuffer(fb); + gl.deleteTexture(colorTex); + gl.deleteTexture(depthTex); +} + +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/rendering/draw-buffers-driver-hang.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-driver-hang.html new file mode 100644 index 0000000000..70a8360d6e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-driver-hang.html @@ -0,0 +1,187 @@ +<!-- +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 Draw Buffers Driver Hang Conformance 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> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +in vec4 a_position; +void main() { + gl_Position = a_position; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; + +out vec4 my_FragColor; +void main() { + my_FragColor = vec4(1, 0, 0, 1); +} +</script> +<script> +"use strict"; +description("This is a regression test for a driver bug causing a hang in the driver and thereby the browser (crbug.com/696187).") + +debug("Thanks to Andre Weissflog (@FlohOfWoe / @floooh) for this test."); +debug("If the bug exists, this test doesn't fail or time out per the harness; the browser basically hangs."); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, { depth: true, stencil: true, alpha: false }, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runTest(); +} + +function runTest() { + // create a global VAO + let vao = gl.createVertexArray(); + gl.bindVertexArray(vao); + + // create a 3 MSAA 'offscreen render targets', each consisting of: + // - 1 color texture which will hold the MSAA resolve result + // - 1 MSAA color renderbuffer + // plus one depth-stencil renderbuffer + let tex0 = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, tex0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + 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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 200, 200, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + let c_rb0 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, c_rb0); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 200, 200); + let ds_rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, ds_rb); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.DEPTH24_STENCIL8, 200, 200); + + // 2nd offscreen render target + let tex1 = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + 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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 200, 200, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + let c_rb1 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, c_rb1); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 200, 200); + + // 3rd offscreen render target + let tex2 = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + 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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 200, 200, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + let c_rb2 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, c_rb2); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 200, 200); + + // an MRT framebuffer with the 3 MSAA renderbuffers and MSAA depth/stencil attachments + let mrt_fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, mrt_fb); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c_rb0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, c_rb1); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.RENDERBUFFER, c_rb2); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, ds_rb); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, ds_rb); + gl.checkFramebufferStatus(gl.FRAMEBUFFER); + + // 3 'MSAA resolve framebuffers' which are the target for the MSAA-resolve-blit, + // with the 3 color textures as color attachments + let res_fb0 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, res_fb0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex0, 0); + gl.checkFramebufferStatus(gl.FRAMEBUFFER); + let res_fb1 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, res_fb1); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, tex1, 0); + gl.checkFramebufferStatus(gl.FRAMEBUFFER); + let res_fb2 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, res_fb2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, tex2, 0); + let frameNumber = 0; + + function draw() { + // draw one frame + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.viewport(0, 0, 1024, 768); + + //--- BEGIN: comment out the block begin BEGIN/END to make the demo run + // clear the 3 MSAA offscreen color renderbuffers and depth/stencil renderbuffer + gl.bindFramebuffer(gl.FRAMEBUFFER, mrt_fb); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT2]); + gl.viewport(0, 0, 200, 200); + gl.depthMask(true); + gl.clearBufferfv(gl.COLOR, 0, [0.25,0.0,0.0,1.0]); + gl.clearBufferfv(gl.COLOR, 1, [0.0,0.25,0.0,1.0]); + gl.clearBufferfv(gl.COLOR, 2, [0.0,0.0,0.25,1.0]); + gl.clearBufferfi(gl.DEPTH_STENCIL, 0, 1.0, 0); + + // the MSAA resolve operation + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, mrt_fb); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, res_fb0); + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.blitFramebuffer(0, 0, 200, 200, 0, 0, 200, 200, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, res_fb1); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.blitFramebuffer(0, 0, 200, 200, 0, 0, 200, 200, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, res_fb2); + gl.readBuffer(gl.COLOR_ATTACHMENT2); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.blitFramebuffer(0, 0, 200, 200, 0, 0, 200, 200, gl.COLOR_BUFFER_BIT, gl.NEAREST); +//--- END + + // bind and clear the default framebuffer + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.viewport(0, 0, 1024, 768); + gl.clearColor(0.5, 0.5, 0.5, 1.0); + gl.clearDepth(1.0); + gl.clearStencil(0); + gl.clear(gl.COLOR_BUFFER_BIT|gl.STENCIL_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); + + if (++frameNumber < 10) { + requestAnimationFrame(draw); + } else { + finishTest(); + } + } + + // Start the rendering loop + draw(); +} + +debug(""); +var successfullyParsed = true; +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-sparse-output-locations.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-sparse-output-locations.html new file mode 100644 index 0000000000..4c679df6a2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers-sparse-output-locations.html @@ -0,0 +1,108 @@ +<!-- +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 Conformance Tests: Verify drawBuffers sparse output locations</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> +<canvas id="canvas" width="1" height="1" style="width: 4px; height: 4px;"> </canvas> +<div id="console"></div> + +<script id="vs" type="x-shader/x-vertex">#version 300 es +void main() { + gl_PointSize = 100.0; + gl_Position = vec4(0, 0, 0, 1); +} +</script> + +<script id="fs" type="x-shader/x-fragment">#version 300 es +// fragment shader only outputs to attachments 1 and 3 +precision highp float; +layout(location = 1) out vec4 output1; +layout(location = 3) out vec4 output2; +void main() +{ + output1 = vec4(0.0, 1.0, 0.0, 1.0); + output2 = vec4(0.0, 0.0, 1.0, 1.0); +} + +</script> +<script> +"use strict"; +description("This test verifies sparse output locations of fragment shaders render correctly"); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + runTests(); +} + +function testAttachment(attachment, expected) { + gl.readBuffer(gl.COLOR_ATTACHMENT0 + attachment); + wtu.checkCanvas(gl, expected, `check COLOR_ATTACHMENT${attachment}`, 1); +} + +function runTests() { + var program = wtu.setupProgram(gl, ["vs", "fs"]); + if (!program) { + testFailed("Set up program failed"); + return; + } + gl.useProgram(program); + + // create a framebuffer with 4 1x1 pixel color attachments + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + for (let i = 0; i < 4; ++i) { + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, tex, 0); + } + + // draw only to the 1st and 3rd attachments + gl.drawBuffers([ + gl.NONE, + gl.COLOR_ATTACHMENT1, + gl.NONE, + gl.COLOR_ATTACHMENT3, + ]); + + // draw + gl.drawArrays(gl.POINTS, 0, 1); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error from set up"); + + // check we got the correct values + testAttachment(0, [0, 0, 0, 0]); + testAttachment(1, [0, 255, 0, 255]); + testAttachment(2, [0, 0, 0, 0]); + testAttachment(3, [0, 0, 255, 255]); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No GL error from testing"); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers.html new file mode 100644 index 0000000000..0e2ecb8f47 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-buffers.html @@ -0,0 +1,598 @@ +<!-- +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 Draw Buffers Conformance Tests</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> +<script src="../../js/tests/webgl-draw-buffers-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="vshaderESSL3" type="x-shader/x-vertex">#version 300 es +in vec4 a_position; +void main() { + gl_Position = a_position; +} +</script> +<script id="vshaderESSL1" type="x-shader/x-vertex"> +attribute vec4 a_position; +void main() { + gl_Position = a_position; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +uniform vec4 u_colors[$(numDrawingBuffers)]; + +// Only one out variable - does not need explicit output layout (ESSL 3 section 4.3.8.2) +out vec4 my_FragData[$(numDrawingBuffers)]; +void main() { +$(assignUColorsToFragData) +} +</script> +<script id="fshaderDiscard" type="x-shader/x-fragment">#version 300 es +precision mediump float; +uniform vec4 u_colors[$(numDrawingBuffers)]; +uniform float u_zero; + +// Only one out variable - does not need explicit output layout (ESSL 3 section 4.3.8.2) +out vec4 my_FragData[$(numDrawingBuffers)]; +void main() { +$(assignUColorsToFragData) + if (u_zero < 1.0) { + discard; + } +} +</script> +<script id="fshaderRed" type="x-shader/x-fragment">#version 300 es +precision mediump float; + +out vec4 my_FragColor; +void main() { + my_FragColor = vec4(1, 0, 0, 1); +} +</script> +<script id="fshaderBlueESSL1" type="x-shader/x-fragment"> +precision mediump float; + +void main() { + gl_FragColor = vec4(0, 0, 1, 1); +} +</script> +<script id="fshaderBuiltInConstEnabled" type="x-shader/x-fragment">#version 300 es +precision mediump float; + +out vec4 my_FragColor; +void main() { + my_FragColor = (gl_MaxDrawBuffers == $(numDrawingBuffers)) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); +} +</script> +<script> +"use strict"; +description("This test verifies the functionality of Multiple Render Targets."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); +var drawBuffersUtils; +let fb; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + drawBuffersUtils = WebGLDrawBuffersUtils(gl); + + if (testParameters()) { + runShadersTest(); + runAttachmentTest(); + runDrawTests(); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function createDrawBuffersProgram(scriptId, sub) { + var fsource = wtu.getScript(scriptId); + fsource = wtu.replaceParams(fsource, sub); + return wtu.setupProgram(gl, ["vshaderESSL3", fsource], ["a_position"], undefined, true); +} + +function runShadersTest() { + debug(""); + debug("test shaders"); + + var sub = {numDrawingBuffers: gl.getParameter(gl.MAX_DRAW_BUFFERS)}; + var program = createDrawBuffersProgram("fshaderBuiltInConstEnabled", sub); + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + gl.deleteProgram(program); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function makeArray(size, value) { + var array = [] + for (var ii = 0; ii < size; ++ii) { + array.push(value); + } + return array; +} + +function runAttachmentTest() { + debug(""); + debug("test attachment enabled"); + + var maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS); + var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + + var tex = gl.createTexture(); + fb = gl.createFramebuffer(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "should not be able to attach pass the max attachment point: gl.COLOR_ATTACHMENT0 + " + maxColorAttachments); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments - 1, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to attach to the max attachment point: gl.COLOR_ATTACHMENT0 + " + (maxColorAttachments - 1)); + gl.drawBuffers(makeArray(maxDrawingBuffers, gl.NONE)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with array NONE of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxDrawingBuffers); + gl.drawBuffers(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with array attachments of size " + maxColorAttachments); + bufs[0] = gl.NONE; + gl.drawBuffers(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with mixed array attachments of size " + maxColorAttachments); + if (maxDrawingBuffers > 1) { + bufs[0] = gl.COLOR_ATTACHMENT1; + bufs[1] = gl.COLOR_ATTACHMENT0; + gl.drawBuffers(bufs); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be able to call drawBuffers with out of order attachments of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(Math.floor(maxDrawingBuffers / 2)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with short array of attachments of size " + bufs.length); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug("Testing drawBuffers and getParameter with bindFramebuffer, without drawing."); + fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0+1)", "gl.NONE"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawBuffers([gl.NONE])"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.BACK"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.NONE"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawBuffers([gl.NONE,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.NONE"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawBuffers([gl.COLOR_ATTACHMENT0,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteFramebuffer(fb)"); + shouldBe("gl.getParameter(gl.DRAW_BUFFER0)", "gl.BACK"); +} + +function makeColorByIndex(index) { + var low = (index - 1) % 15 + 1; + var high = (index - 1) / 15; + + var zeroOrOne = function(v) { + return v ? 1 : 0; + }; + + var oneOrTwo = function(v) { + return v ? 2 : 1; + } + + var makeComponent = function(b0, b1, b2) { + return Math.floor(255 * zeroOrOne(b0) / oneOrTwo(b1) / oneOrTwo(b2)); + }; + return [ + makeComponent(low & (1 << 0), high & (1 << 0), high & (1 << 4)), + makeComponent(low & (1 << 1), high & (1 << 1), high & (1 << 5)), + makeComponent(low & (1 << 2), high & (1 << 2), high & (1 << 6)), + makeComponent(low & (1 << 3), high & (1 << 3), high & (1 << 7)), + ]; +} + +function runDrawTests() { + debug(""); + debug("--------- draw tests -----------"); + var fb = gl.createFramebuffer(); + var fb2 = gl.createFramebuffer(); + var halfFB1 = gl.createFramebuffer(); + var halfFB2 = gl.createFramebuffer(); + var endsFB = gl.createFramebuffer(); + var middleFB = gl.createFramebuffer(); + + var maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS); + var maxUsable = drawBuffersUtils.getMaxUsableColorAttachments(); + var half = Math.floor(maxUsable / 2); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + var nones = makeArray(maxUsable, gl.NONE); + + [fb, fb2, halfFB1, halfFB2, endsFB, middleFB].forEach(function(fbo) { + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.drawBuffers(bufs); + }); + + var checkProgram = wtu.setupTexturedQuad(gl); + var redProgram = wtu.setupProgram(gl, ["vshaderESSL3", "fshaderRed"], ["a_position"]); + var blueProgramESSL1 = wtu.setupProgram(gl, ["vshaderESSL1", "fshaderBlueESSL1"], ["a_position"]); + + var assignCode = []; + for (var i = 0; i < maxDrawingBuffers; ++i) { + assignCode.push(" my_FragData[" + i + "] = u_colors[" + i + "];"); + } + + var drawProgram = createDrawBuffersProgram("fshader", + {numDrawingBuffers: maxDrawingBuffers, assignUColorsToFragData: assignCode.join("\n")}); + var width = 64; + var height = 64; + var attachments = []; + // Makes 6 framebuffers. + // fb and fb2 have all the attachments. + // halfFB1 has the first half of the attachments + // halfFB2 has the second half of the attachments + // endsFB has the first and last attachments + // middleFB has all but the first and last attachments + for (var ii = 0; ii < maxUsable; ++ii) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + 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); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, ii < half ? halfFB1 : halfFB2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, (ii == 0 || ii == (maxUsable - 1)) ? endsFB : middleFB); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + var location = gl.getUniformLocation(drawProgram, "u_colors[" + ii + "]"); + var color = makeColorByIndex(ii + 1); + var floatColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255]; + gl.uniform4fv(location, floatColor); + attachments.push({ + texture: tex, + color: color + }); + } + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + var drawAndCheckAttachments = function(testFB, msg, testFn) { + debug("test clearing " + msg); + + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + + attachments.forEach(function(attachment, index) { + debug("attachment: " + index + " = " + wtu.glEnumToString(gl, gl.getParameter(gl.DRAW_BUFFER0 + index)) + + ", " + wtu.glEnumToString(gl, gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + index, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE))); + }); + + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + debug("framebuffer not complete"); + debug(""); + return; + } + + // Clear all the attachments + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + //drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + // return [0, 0, 0, 0]; + //}); + //debug("--"); + + // Clear some attachments using testFB + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return testFn(attachment, index) ? [0, 255, 0, 255] : [0, 0, 0, 0]; + }); + + debug("test drawing to " + msg); + + // Draw to some attachments using testFB + gl.useProgram(drawProgram); + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return testFn(attachment, index) ? attachment.color : [0, 0, 0, 0]; + }); + }; + + gl.useProgram(drawProgram); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.drawBuffers(bufs); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + + wtu.drawUnitQuad(gl); + + debug("test that each texture got the correct color."); + + drawBuffersUtils.checkAttachmentsForColor(attachments); + + debug("test clearing clears all the textures"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + + debug("test that NONE draws nothing"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(nones); + gl.useProgram(redProgram); + wtu.clearAndDrawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + + // GLES3 spec section 3.9.2 Shader Outputs + debug("test that gl_FragColor only writes to color number zero"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.useProgram(blueProgramESSL1); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Active draw buffers with missing frag outputs."); + gl.enable(gl.RASTERIZER_DISCARD); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors when RASTERIZER_DISCARD is enabled."); + gl.disable(gl.RASTERIZER_DISCARD); + gl.colorMask(false, false, false, false); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors when all 4 channels of color mask are disabled."); + gl.colorMask(false, true, false, false); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "partially diabled color mask shall have no impact."); + gl.colorMask(true, true, true, true); + + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index == 0) ? [0, 0, 255, 255] : [0, 255, 0, 255]; + }); + + // If there is only a single output, the location defaults to zero if not specified. + // See GLSL ES Spec 3.00.4, Section 4.3.8.2, Output Layout Qualifiers. + debug("test that an OpenGL ES Shading Language 3.00 shader with a single output color defaults to color number zero"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.useProgram(redProgram); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Active draw buffers with missing frag outputs."); + + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index == 0) ? [255, 0, 0, 255] : [0, 255, 0, 255]; + }); + + if (maxUsable > 1) { + // Prepare for following tests by clearing all attachments to red. + debug("prepare by clearing all attachments to red"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + var bufs1 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + var bufs2 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + for (var ii = 0; ii < maxUsable; ++ii) { + if (ii < half) { + bufs1[ii] = gl.NONE; + } else { + bufs2[ii] = gl.NONE; + } + } + + debug("test setting first half to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs1); + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [255, 0, 0, 255] : [0, 255, 0, 255]; + }); + + debug("test setting first half to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [255, 0, 0, 255] : attachment.color; + }); + + debug("test setting second half to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawBuffers(bufs2); + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [0, 0, 255, 255] : [255, 0, 0, 255]; + }); + + debug("test setting second half to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? attachment.color : [255, 0, 0, 255]; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB1); + gl.drawBuffers(bufs); + drawAndCheckAttachments( + halfFB1, "framebuffer that only has first half of attachments", + function(attachment, index) { + return index < half; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB2); + gl.drawBuffers(bufs); + drawAndCheckAttachments( + halfFB2, "framebuffer that only has second half of attachments", + function(attachment, index) { + return index >= half; + }); + + if (maxUsable > 2) { + gl.bindFramebuffer(gl.FRAMEBUFFER, endsFB); + gl.drawBuffers(bufs); + drawAndCheckAttachments( + endsFB, "framebuffer that only has first and last attachments", + function(attachment, index) { + return index == 0 || index == (maxUsable - 1); + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, middleFB); + gl.drawBuffers(bufs); + drawAndCheckAttachments( + middleFB, + "framebuffer that has all but the first and last attachments", + function(attachment, index) { + return index != 0 && index != (maxUsable - 1); + }); + } + } + + debug("test switching between fbos keeps drawbuffer state"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.drawBuffers(nones); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + drawBuffersUtils.checkAttachmentsForColor(attachments); + + debug("test that none of the attachments are written in case the fragment shader discards"); + var discardProgram = createDrawBuffersProgram("fshaderDiscard", + {numDrawingBuffers: maxDrawingBuffers, assignUColorsToFragData: assignCode.join("\n")}); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.drawBuffers(bufs); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(discardProgram); + wtu.drawUnitQuad(gl); + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 0, 0, 0]); + + debug("test queries"); + debug("check framebuffer with all attachments on"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + for (var ii = 0; ii < maxUsable; ++ii) { + shouldBe("gl.getParameter(gl.DRAW_BUFFER0 + " + ii + ")", "gl.COLOR_ATTACHMENT0 + " + ii); + } + + debug("check framebuffer with all attachments off"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + for (var ii = 0; ii < maxUsable; ++ii) { + shouldBe("gl.getParameter(gl.DRAW_BUFFER0 + " + ii + ")", "gl.NONE"); + } + + // WebGL generates FRAMEBUFFER_INCOMPLETE_DIMENSIONS when attached images have different sizes. + // This behavior differs from GLES 3. + debug(""); + debug("test attachment size mis-match"); + gl.bindTexture(gl.TEXTURE_2D, attachments[0].texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width * 2, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS"); + + // TODO: Rendering when framebuffer attachments have mismatched size should be tested, maybe in a separate test. + + gl.deleteFramebuffer(fb); + gl.deleteFramebuffer(fb2); + gl.deleteFramebuffer(halfFB1); + gl.deleteFramebuffer(halfFB2); + attachments.forEach(function(attachment) { + gl.deleteTexture(attachment.texture); + }); + gl.deleteProgram(checkProgram); + gl.deleteProgram(redProgram); + gl.deleteProgram(drawProgram); +} + +function testParameters() { + debug(""); + debug("check that MAX_DRAW_BUFFERS and MAX_COLOR_ATTACHMENTS are valid"); + var maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS); + var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS); + debug("MAX_DRAW_BUFFERS = " + maxDrawBuffers); + debug("MAX_COLOR_ATTACHMENTS = " + maxColorAttachments); + if (maxDrawBuffers != maxColorAttachments) { + testFailed("MAX_DRAW_BUFFERS and MAX_COLOR_ATTACHMENTS should be the same"); + return false; + } + if (maxDrawBuffers < 4) { + testFailed("MAX_DRAW_BUFFERS should be at least 4"); + return false; + } + return true; +} + +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/rendering/draw-with-integer-texture-base-level.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-with-integer-texture-base-level.html new file mode 100644 index 0000000000..e2f9fd13c2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/draw-with-integer-texture-base-level.html @@ -0,0 +1,65 @@ +<!-- +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 Draw With Integer Texture Base Level Tests</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> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +</head> +<body> +<canvas id="example" width="24" height="24"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +var canvas; +var wtu = WebGLTestUtils; +canvas = document.getElementById("example"); +var gl = wtu.create3DContext(canvas, undefined, 2); +var tiu = TexImageUtils; + +// Both Chrome and Firefox fail on this test on NVIDIA Windows, see crbug.com/679639. +function testDrawIntegerTextureBaseLevel() +{ + description("This test verifies the functionality of rendering with integer texture non-zero base level."); + + var green = [0, 255, 0, 255]; + + var width = 16; + var height = 16; + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, width, height); + + var texture = gl.createTexture(); + var level = 1; + 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_BASE_LEVEL, level); + wtu.fillTexture(gl, texture, width, height, green, level, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, gl.RGBA8UI); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var program = tiu.setupTexturedQuad(gl, "RGBA8UI"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + wtu.checkCanvas(gl, green); +} + +testDrawIntegerTextureBaseLevel(); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> + diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/element-index-uint.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/element-index-uint.html new file mode 100644 index 0000000000..123254f4cd --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/element-index-uint.html @@ -0,0 +1,432 @@ +<!-- +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 Uint element indices Conformance Tests</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> + +<script id="vs" type="x-shader/x-vertex"> +attribute vec4 vPosition; +attribute vec4 vColor; +varying vec4 color; +void main() { + gl_Position = vPosition; + color = vColor; +} +</script> +<script id="fs" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 color; +void main() { + gl_FragColor = color; +} +</script> +<script id="vsCheckOutOfBounds" type="x-shader/x-vertex"> + precision mediump float; + attribute vec2 position; + attribute vec4 vecRandom; + varying vec4 v_color; + + // Per the spec, each component can either contain existing contents + // of the buffer or 0. + bool testFloatComponent(float component) { + return (component == 0.2 || component == 0.0); + } + // The last component is additionally allowed to be 1.0. + bool testLastFloatComponent(float component) { + return testFloatComponent(component) || component == 1.0; + } + + void main() { + if (testFloatComponent(vecRandom.x) && + testFloatComponent(vecRandom.y) && + testFloatComponent(vecRandom.z) && + testLastFloatComponent(vecRandom.w)) { + v_color = vec4(0.0, 1.0, 0.0, 1.0); // green -- Out of range + } else { + v_color = vec4(1.0, 0.0, 0.0, 1.0); // red -- Unexpected value + } + gl_Position = vec4(position, 0.0, 1.0); + } +</script> + +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the Uint element indices."); + +debug(""); + +var wtu = WebGLTestUtils; +var gl = null; +var canvas = null; + +// Test both STATIC_DRAW and DYNAMIC_DRAW as a regression test +// for a bug in ANGLE which has since been fixed. +for (var ii = 0; ii < 2; ++ii) { + canvas = document.createElement("canvas"); + canvas.width = 50; + canvas.height = 50; + + gl = wtu.create3DContext(canvas, null, 2); + + if (!gl) { + testFailed("WebGL context does not exist"); + } else { + testPassed("WebGL context exists"); + + var drawType = (ii == 0) ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW; + debug("Testing " + ((ii == 0) ? "STATIC_DRAW" : "DYNAMIC_DRAW")); + + runDrawTests(drawType); + + // These tests are tweaked duplicates of the buffers/index-validation* tests + // using unsigned int indices to ensure that behavior remains consistent + runIndexValidationTests(drawType); + runIndexOutOfRangeTests(drawType); + runResizedBufferTests(drawType); + runCrashWithBufferSubDataTests(drawType); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + } +} + +function runDrawTests(drawType) { + debug("Test that draws with unsigned integer indices produce the expected results"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + + var program = wtu.setupNoTexCoordTextureProgram(gl); + + function setupDraw(s) { + // Create a vertex buffer that cannot be fully indexed via shorts + var quadArrayLen = 65537 * 3; + var quadArray = new Float32Array(quadArrayLen); + + // Leave all but the last 4 values zero-ed out + var idx = quadArrayLen - 12; + + // Initialized the last 4 values to a quad + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 0.0; + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, quadArray, drawType); + + // Create an unsigned int index buffer that indexes the last 4 vertices + var baseIndex = (quadArrayLen / 3) - 4; + + var indexObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array([ + baseIndex + 0, + baseIndex + 1, + baseIndex + 2, + baseIndex + 2, + baseIndex + 3, + baseIndex + 0]), drawType); + + var opt_positionLocation = 0; + gl.enableVertexAttribArray(opt_positionLocation); + gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0); + }; + function readLocation(x, y) { + var pixels = new Uint8Array(1 * 1 * 4); + gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); + return pixels; + }; + function testPixel(blockList, allowList) { + function testList(list, expected) { + for (var n = 0; n < list.length; n++) { + var l = list[n]; + var x = -Math.floor(l * canvas.width / 2) + canvas.width / 2; + var y = -Math.floor(l * canvas.height / 2) + canvas.height / 2; + var source = readLocation(x, y); + if (Math.abs(source[0] - expected) > 2) { + return false; + } + } + return true; + } + return testList(blockList, 0) && testList(allowList, 255); + }; + function verifyDraw(drawNumber, s) { + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0); + + var blockList = []; + var allowList = []; + var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; + for (var n = 0; n < points.length; n++) { + if (points[n] <= s) { + blockList.push(points[n]); + } else { + allowList.push(points[n]); + } + } + if (testPixel(blockList, allowList)) { + testPassed("Draw " + drawNumber + " passed pixel test"); + } else { + testFailed("Draw " + drawNumber + " failed pixel test"); + } + }; + + setupDraw(0.5); + verifyDraw(0, 0.5); +} + +function runIndexValidationTests(drawType) { + description("Tests that index validation verifies the correct number of indices"); + + function sizeInBytes(type) { + switch (type) { + case gl.BYTE: + case gl.UNSIGNED_BYTE: + return 1; + case gl.SHORT: + case gl.UNSIGNED_SHORT: + return 2; + case gl.INT: + case gl.UNSIGNED_INT: + case gl.FLOAT: + return 4; + default: + throw "unknown type"; + } + } + + var program = wtu.loadStandardProgram(gl); + + // 3 vertices => 1 triangle, interleaved data + var dataComplete = new Float32Array([0, 0, 0, 1, + 0, 0, 1, + 1, 0, 0, 1, + 0, 0, 1, + 1, 1, 1, 1, + 0, 0, 1]); + var dataIncomplete = new Float32Array([0, 0, 0, 1, + 0, 0, 1, + 1, 0, 0, 1, + 0, 0, 1, + 1, 1, 1, 1]); + var indices = new Uint32Array([0, 1, 2]); + + debug("Testing with valid indices"); + + var bufferComplete = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferComplete); + gl.bufferData(gl.ARRAY_BUFFER, dataComplete, drawType); + var elements = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elements); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + gl.useProgram(program); + var vertexLoc = gl.getAttribLocation(program, "a_vertex"); + var normalLoc = gl.getAttribLocation(program, "a_normal"); + gl.vertexAttribPointer(vertexLoc, 4, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 0); + gl.enableVertexAttribArray(vertexLoc); + gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 4 * sizeInBytes(gl.FLOAT)); + gl.enableVertexAttribArray(normalLoc); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var bufferIncomplete = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferIncomplete); + gl.bufferData(gl.ARRAY_BUFFER, dataIncomplete, drawType); + gl.vertexAttribPointer(vertexLoc, 4, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 0); + gl.enableVertexAttribArray(vertexLoc); + gl.disableVertexAttribArray(normalLoc); + debug("Enable vertices, valid"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Test with enabled attribute that does not belong to current program"); + + gl.disableVertexAttribArray(normalLoc); + var extraLoc = Math.max(vertexLoc, normalLoc) + 1; + gl.enableVertexAttribArray(extraLoc); + debug("Enable an extra attribute with null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); + debug("Enable an extra attribute with insufficient data buffer"); + gl.vertexAttribPointer(extraLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 4 * sizeInBytes(gl.FLOAT)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + debug("Pass large negative index to vertexAttribPointer"); + gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), -2000000000 * sizeInBytes(gl.FLOAT)); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); +} + +function runIndexOutOfRangeTests(drawType) { + debug("Testing with out-of-range indices"); + + var bufferPos = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferPos); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 1.0, 1.0, + -1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0]), drawType); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + + var bufferIncomplete = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferIncomplete); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2]), drawType); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0); + + var glProgram = wtu.setupProgram(gl, ["vsCheckOutOfBounds", wtu.simpleVertexColorFragmentShader], ["position", "vecRandom"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Shader and buffer setup successfully"); + + var indices = new Uint32Array([0, 1, 2, 0, 2, 3]); + var elements = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elements); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + + gl.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched. + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + + + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0); + var error = gl.getError(); + if (error === gl.INVALID_OPERATION) { + testPassed("drawElements flagged INVALID_OPERATION, which is valid so long as all canvas pixels were not touched."); + wtu.checkCanvas(gl, [0, 0, 255, 255]); + } else if (error === gl.NO_ERROR) { + testPassed("drawElements flagged NO_ERROR, which is valid so long as all canvas pixels are green."); + wtu.checkCanvas(gl, [0, 255, 0, 255]); + } else { + testFailed("Invalid error flagged by drawElements. Should be INVALID_OPERATION or NO_ERROR"); + } + + debug("Test that client data is always copied during bufferData and bufferSubData calls"); + + indices[5] = 1; + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0); + var error = gl.getError(); + if (error === gl.INVALID_OPERATION) { + testPassed("drawElements flagged INVALID_OPERATION, which is valid so long as all canvas pixels were not touched."); + wtu.checkCanvas(gl, [0, 0, 255, 255]); + } else if (error === gl.NO_ERROR) { + testPassed("drawElements flagged NO_ERROR, which is valid so long as all canvas pixels are green."); + wtu.checkCanvas(gl, [0, 255, 0, 255]); + } else { + testFailed("Invalid error flagged by drawElements. Should be INVALID_OPERATION or NO_ERROR"); + } +} + +function runResizedBufferTests(drawType) { + debug("Test that updating the size of a vertex buffer is properly noticed by the WebGL implementation."); + + var program = wtu.setupProgram(gl, ["vs", "fs"], ["vPosition", "vColor"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after initialization"); + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( + [-1,1,0, 1,1,0, -1,-1,0, + -1,-1,0, 1,1,0, 1,-1,0]), drawType); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after vertex setup"); + + var texCoordObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( + [0,0, 1,0, 0,1, + 0,1, 1,0, 1,1]), drawType); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after texture coord setup"); + + // Now resize these buffers because we want to change what we're drawing. + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1,1,0, 1,1,0, -1,-1,0, 1,-1,0, + -1,1,0, 1,1,0, -1,-1,0, 1,-1,0]), drawType); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after vertex redefinition"); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordObject); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array([ + 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255]), drawType); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after texture coordinate / color redefinition"); + + var numQuads = 2; + var indices = new Uint32Array(numQuads * 6); + for (var ii = 0; ii < numQuads; ++ii) { + var offset = ii * 6; + var quad = (ii == (numQuads - 1)) ? 4 : 0; + indices[offset + 0] = quad + 0; + indices[offset + 1] = quad + 1; + indices[offset + 2] = quad + 2; + indices[offset + 3] = quad + 2; + indices[offset + 4] = quad + 1; + indices[offset + 5] = quad + 3; + } + var indexObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after setting up indices"); + gl.drawElements(gl.TRIANGLES, numQuads * 6, gl.UNSIGNED_INT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after drawing"); +} + +function runCrashWithBufferSubDataTests(drawType) { + debug('Verifies that the index validation code which is within bufferSubData does not crash.') + + var elementBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 256, drawType); + var data = new Uint32Array(127); + gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 64, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "after attempting to update a buffer outside of the allocated bounds"); + testPassed("bufferSubData, when buffer object was initialized with null, did not crash"); +} + +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/rendering/framebuffer-completeness-draw-framebuffer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-completeness-draw-framebuffer.html new file mode 100644 index 0000000000..1c0709e0b8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-completeness-draw-framebuffer.html @@ -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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Test draw framebuffer completeness when an incomplete framebuffer is bound to read framebuffer</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> +// This exposes a bug in Chrome 67: If the read framebuffer is incomplete, then draw can fail even if the draw framebuffer is complete. +// http://anglebug.com/2737 + +"use strict"; +description(); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext(undefined, undefined, 2); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var incompleteFb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, incompleteFb); + var incompleteTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, incompleteTex); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, incompleteTex, 0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT'); + + wtu.setupUnitQuad(gl, 0, 1); + + var testProgram = wtu.setupSimpleColorProgram(gl, 0); + + // If this is changed to gl.FRAMEBUFFER, the rendering succeeds on Chrome 67. + var drawFbTarget = gl.DRAW_FRAMEBUFFER; + + var completeFb = gl.createFramebuffer(); + gl.bindFramebuffer(drawFbTarget, completeFb); + var completeTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, completeTex); + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 128, 128); + gl.framebufferTexture2D(drawFbTarget, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, completeTex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error after setup"); + + shouldBe('gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER)', 'gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT'); + shouldBe('gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.viewport(0, 0, 128, 128); + gl.uniform4f(gl.getUniformLocation(testProgram, 'u_color'), 0, 1, 0, 1); + wtu.drawUnitQuad(gl); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error after draw"); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, completeFb); + wtu.checkCanvasRect(gl, 0, 0, 128, 128, [0, 255, 0, 255], 'should be green', 2); +} + +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/rendering/framebuffer-completeness-unaffected.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-completeness-unaffected.html new file mode 100644 index 0000000000..74b6106419 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-completeness-unaffected.html @@ -0,0 +1,89 @@ +<!-- +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 drawBuffers, readBuffer, and fbo completeness</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> +// In MacOSX, if drawBuffers() and readBuffer() both select an attachment with no image attached, +// fbo becomes incomplete. However, drawBuffers() and readBuffer() should not affect fbo completeness. + +"use strict"; +description("This tests drawBuffers, readBuffer, and fbo completeness"); + +var setupRenderbuffer = function(attachment) { + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, canvas.width, canvas.height); + return renderbuffer; +} + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, undefined, 2); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + debug("fbo with two color images attached should be complete"); + var colorbuffer = setupRenderbuffer(gl.COLOR_ATTACHMENT0); + var colorbuffer1 = setupRenderbuffer(gl.COLOR_ATTACHMENT1); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("drawBuffers selects ATTACHMENT1, fbo should be complete"); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("remove image attached to ATTACHMENT1, fbo should be complete"); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, null); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("set read buffer to ATTACHMENT1, fbo should be complete"); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("drawBuffers selects ATTACHMENT0, fbo should be complete"); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("drawBuffers selects ATTACHMENT1, fbo should be complete"); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + debug("set read buffer to ATTACHMENT0, fbo should be complete"); + gl.readBuffer(gl.COLOR_ATTACHMENT0); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + gl.deleteFramebuffer(fb); + gl.deleteRenderbuffer(colorbuffer); + gl.deleteRenderbuffer(colorbuffer1); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error after setup and clear render buffer"); +} + +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/rendering/framebuffer-mismatched-attachment-targets.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-mismatched-attachment-targets.html new file mode 100644 index 0000000000..a8a28d6444 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-mismatched-attachment-targets.html @@ -0,0 +1,162 @@ +<!-- +Copyright (c) 2020 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>WebGL2 can render to framebuffer attachments with different targets</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> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +void main(void) { + gl_Position = vec4(-0.5, -0.5, 0, 1); + gl_PointSize = 1.0; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +out vec4 outColor; +void main() { + outColor = vec4(0, 1, 0, 1); +} +</script> +</head> +<body> +<canvas id="example" width="1", height="1"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +debug(""); + +description("Test framebuffer attachments with different targets"); + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext("example", undefined, 2); + +if (!gl) { + testFailed("WebGL context creation failed"); +} else { + testPassed("WebGL context creation succeeded"); + runTest(); +} + +function newResource(target, mipLevels, format, size) { + let ret; + switch (target) { + case gl.RENDERBUFFER: { + ret = gl.createRenderbuffer(); + ret.mips = [ ret ]; + for (let i = 1; i < mipLevels; i++) { + ret.mips.push(gl.createRenderbuffer()); + } + for (const i in ret.mips) { + const rb = ret.mips[i]; + gl.bindRenderbuffer(target, rb); + gl.renderbufferStorage(target, format, size>>i, size>>i); + } + ret.attach = (attachEnum, mipLevel) => { + const rb = ret.mips[mipLevel]; + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachEnum, gl.RENDERBUFFER, rb); + }; + break; + } + case gl.TEXTURE_2D: + case gl.TEXTURE_CUBE_MAP: { + ret = gl.createTexture(); + gl.bindTexture(target, ret); + gl.texStorage2D(target, mipLevels, format, size, size); + let imageTarget = target; + if (imageTarget == gl.TEXTURE_CUBE_MAP) { + imageTarget = gl.TEXTURE_CUBE_MAP_POSITIVE_X+2; // Deliberately don't choose the first image. + } + ret.attach = (attachEnum, mipLevel) => { + gl.framebufferTexture2D(gl.FRAMEBUFFER, attachEnum, imageTarget, ret, mipLevel); + }; + break; + } + case gl.TEXTURE_3D: + case gl.TEXTURE_2D_ARRAY: { + ret = gl.createTexture(); + gl.bindTexture(target, ret); + gl.texStorage3D(target, mipLevels, format, size, size, 1); + ret.attach = (attachEnum, mipLevel) => { + gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachEnum, ret, mipLevel, 0); + }; + break; + } + default: + throw new Error(); + } + ret.target = wtu.glEnumToString(gl, target); + ret.format = wtu.glEnumToString(gl, format); + return ret; +} + +function runTest() { + const MIP_LEVELS = 2; + const SIZE = 2; + + gl.clearColor(1, 0, 0, 1); + + const program = wtu.setupProgram(gl, ['vshader','fshader'], [], console.log.bind(console)); + gl.useProgram(program); + + const colorResList = [ + newResource(gl.RENDERBUFFER, MIP_LEVELS, gl.RGBA8, SIZE), + newResource(gl.TEXTURE_2D, MIP_LEVELS, gl.RGBA8, SIZE), + newResource(gl.TEXTURE_CUBE_MAP, MIP_LEVELS, gl.RGBA8, SIZE), + newResource(gl.TEXTURE_3D, MIP_LEVELS, gl.RGBA8, SIZE), + newResource(gl.TEXTURE_2D_ARRAY, MIP_LEVELS, gl.RGBA8, SIZE), + ]; + + const depthResList = [ + newResource(gl.RENDERBUFFER, MIP_LEVELS, gl.DEPTH_COMPONENT16, SIZE), + newResource(gl.TEXTURE_2D, MIP_LEVELS, gl.DEPTH_COMPONENT16, SIZE), + newResource(gl.TEXTURE_CUBE_MAP, MIP_LEVELS, gl.DEPTH_COMPONENT16, SIZE), + //newResource(gl.TEXTURE_3D, MIP_LEVELS, gl.DEPTH_COMPONENT16, SIZE), // Depth formats forbidden for TEXTURE_3D. + newResource(gl.TEXTURE_2D_ARRAY, MIP_LEVELS, gl.DEPTH_COMPONENT16, SIZE), + ]; + + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + for (const color of colorResList) { + for (const depth of depthResList) { + debug(`\ncolor: ${color.target}; depth: ${depth.target}`); + for (let mipLevel = 0; mipLevel < MIP_LEVELS; mipLevel++) { + debug(`mipLevel: ${mipLevel}`); + color.attach(gl.COLOR_ATTACHMENT0, mipLevel); + depth.attach(gl.DEPTH_ATTACHMENT, mipLevel); + const maybeStatus = wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, [gl.FRAMEBUFFER_COMPLETE, gl.FRAMEBUFFER_UNSUPPORTED]); + if (!maybeStatus || maybeStatus[0] != gl.FRAMEBUFFER_COMPLETE) { + continue; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [255, 0, 0, 255], `framebuffer layer ${mipLevel} should be cleared red`); + + gl.drawArrays(gl.POINTS, 0, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], `framebuffer layer ${mipLevel} should be drawn green`); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors`); + } + } + } + + // make sure we were not rendering to the canvas. + gl.bindFramebuffer(gl.FRAMEBUFFER, null) + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); +} + +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/rendering/framebuffer-render-to-layer-angle-issue.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-render-to-layer-angle-issue.html new file mode 100644 index 0000000000..1fbdb6bbeb --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-render-to-layer-angle-issue.html @@ -0,0 +1,90 @@ +<!-- +Copyright (c) 2020 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>WebGL2 can render to layers in 3D texture angle issue check</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> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +void main(void) { + gl_Position = vec4(0, 0, 0, 1); + gl_PointSize = 1.0; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +out vec4 outColor; +void main() { + outColor = vec4(0, 1, 0, 1); +} +</script> +</head> +<body> +<canvas id="example" width="1", height="1"></canvas> +<div id="description"></div> +<a href='https://bugs.chromium.org/p/angleproject/issues/detail?id=4417'>ANGLE issue #4417</a> +<div id="console"></div> +<script> +"use strict"; +debug(""); + +description("Test that WebGL2 can render to layers in 3D textures"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("example", undefined, 2); + +if (!gl) { + testFailed("WebGL context creation failed"); +} else { + testPassed("WebGL context creation succeeded"); + runTest(); +} + +function runTest() { + const fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_3D, tex); + gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, gl.canvas.width, gl.canvas.height, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + for (let i = 0; i < 2; i++) { + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, 0, i); + + const rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, gl.canvas.width, gl.canvas.height); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb); + + wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE); + + const program = wtu.setupProgram(gl, ['vshader','fshader'], [], console.log.bind(console)); + gl.useProgram(program); + + gl.drawArrays(gl.POINTS, 0, 1); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors`); + wtu.checkCanvas(gl, [0, 255, 0, 255], `framebuffer layer ${i} should be green`); + } + + // make sure we were not rendering to the canvas. + gl.bindFramebuffer(gl.FRAMEBUFFER, null) + wtu.checkCanvas(gl, [0, 0, 0, 0], "canvas should be zero"); +} + +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/rendering/framebuffer-render-to-layer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-render-to-layer.html new file mode 100644 index 0000000000..c34f2a4143 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-render-to-layer.html @@ -0,0 +1,440 @@ +<!-- +Copyright (c) 2020 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>WebGL2 can render to layers in 3D and 2D_ARRAY textures</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> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +void main(void) { + gl_Position = vec4(0, 0, 0, 1); + gl_PointSize = 1.0; +} +</script> +</head> +<body> +<canvas id="example" width="100", height="100"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +debug(""); + +description("Test that WebGL2 can render to layers in 3D and 2D_ARRAY textures"); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("example", undefined, 2); + +if (!gl) { + testFailed("WebGL context creation failed"); +} else { + testPassed("WebGL context creation succeeded"); + runTest(); +} + +function runTest() { + const texWidth = 1; + const texHeight = 1; + const texDepth = 2; + + function makeFragmentShader(typeInfo) { + const src = `#version 300 es + precision mediump float; + out ${typeInfo.outType} color; + void main() { + color = ${typeInfo.outValue}; + } + `; + return src; + } + + const textureInternalFormatInfo = {}; + { + const t = textureInternalFormatInfo; + // unsized formats + // If understand correctly these 3 unsized formats are not required to be color renderable + t[gl.ALPHA] = { textureFormat: gl.ALPHA, colorRenderable: false, textureFilterable: true, bytesPerElement: [1, 2, 2, 4], type: [gl.UNSIGNED_BYTE, gl.HALF_FLOAT, gl.HALF_FLOAT_OES, gl.FLOAT], }; + t[gl.LUMINANCE] = { textureFormat: gl.LUMINANCE, colorRenderable: false, textureFilterable: true, bytesPerElement: [1, 2, 2, 4], type: [gl.UNSIGNED_BYTE, gl.HALF_FLOAT, gl.HALF_FLOAT_OES, gl.FLOAT], }; + t[gl.LUMINANCE_ALPHA] = { textureFormat: gl.LUMINANCE_ALPHA, colorRenderable: false, textureFilterable: true, bytesPerElement: [2, 4, 4, 8], type: [gl.UNSIGNED_BYTE, gl.HALF_FLOAT, gl.HALF_FLOAT_OES, gl.FLOAT], }; + + t[gl.RGB] = { textureFormat: gl.RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3, 6, 6, 12, 2], type: [gl.UNSIGNED_BYTE, gl.HALF_FLOAT, gl.HALF_FLOAT_OES, gl.FLOAT, gl.UNSIGNED_SHORT_5_6_5], }; + t[gl.RGBA] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 8, 8, 16, 2, 2], type: [gl.UNSIGNED_BYTE, gl.HALF_FLOAT, gl.HALF_FLOAT_OES, gl.FLOAT, gl.UNSIGNED_SHORT_4_4_4_4, gl.UNSIGNED_SHORT_5_5_5_1], }; + + // sized formats + t[gl.R8] = { textureFormat: gl.RED, colorRenderable: true, textureFilterable: true, bytesPerElement: [1], type: [gl.UNSIGNED_BYTE], }; + t[gl.R8_SNORM] = { textureFormat: gl.RED, colorRenderable: false, textureFilterable: true, bytesPerElement: [1], type: [gl.BYTE], }; + t[gl.R16F] = { textureFormat: gl.RED, colorRenderable: false, textureFilterable: true, bytesPerElement: [4, 2], type: [gl.FLOAT, gl.HALF_FLOAT], }; + t[gl.R32F] = { textureFormat: gl.RED, colorRenderable: false, textureFilterable: false, bytesPerElement: [4], type: [gl.FLOAT], }; + t[gl.R8UI] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [1], type: [gl.UNSIGNED_BYTE], }; + t[gl.R8I] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [1], type: [gl.BYTE], }; + t[gl.R16UI] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [gl.UNSIGNED_SHORT], }; + t[gl.R16I] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [gl.SHORT], }; + t[gl.R32UI] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_INT], }; + t[gl.R32I] = { textureFormat: gl.RED_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.INT], }; + t[gl.RG8] = { textureFormat: gl.RG, colorRenderable: true, textureFilterable: true, bytesPerElement: [2], type: [gl.UNSIGNED_BYTE], }; + t[gl.RG8_SNORM] = { textureFormat: gl.RG, colorRenderable: false, textureFilterable: true, bytesPerElement: [2], type: [gl.BYTE], }; + t[gl.RG16F] = { textureFormat: gl.RG, colorRenderable: false, textureFilterable: true, bytesPerElement: [8, 4], type: [gl.FLOAT, gl.HALF_FLOAT], }; + t[gl.RG32F] = { textureFormat: gl.RG, colorRenderable: false, textureFilterable: false, bytesPerElement: [8], type: [gl.FLOAT], }; + t[gl.RG8UI] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [gl.UNSIGNED_BYTE], }; + t[gl.RG8I] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [2], type: [gl.BYTE], }; + t[gl.RG16UI] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_SHORT], }; + t[gl.RG16I] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.SHORT], }; + t[gl.RG32UI] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [gl.UNSIGNED_INT], }; + t[gl.RG32I] = { textureFormat: gl.RG_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [gl.INT], }; + t[gl.RGB8] = { textureFormat: gl.RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3], type: [gl.UNSIGNED_BYTE], }; + t[gl.SRGB8] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [3], type: [gl.UNSIGNED_BYTE], }; + t[gl.RGB565] = { textureFormat: gl.RGB, colorRenderable: true, textureFilterable: true, bytesPerElement: [3, 2], type: [gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT_5_6_5], }; + t[gl.RGB8_SNORM] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [3], type: [gl.BYTE], }; + t[gl.R11F_G11F_B10F] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6, 4], type: [gl.FLOAT, gl.HALF_FLOAT, gl.UNSIGNED_INT_10F_11F_11F_REV], }; + t[gl.RGB9_E5] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6, 4], type: [gl.FLOAT, gl.HALF_FLOAT, gl.UNSIGNED_INT_5_9_9_9_REV], }; + t[gl.RGB16F] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: true, bytesPerElement: [12, 6], type: [gl.FLOAT, gl.HALF_FLOAT], }; + t[gl.RGB32F] = { textureFormat: gl.RGB, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [gl.FLOAT], }; + t[gl.RGB8UI] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [3], type: [gl.UNSIGNED_BYTE], }; + t[gl.RGB8I] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [3], type: [gl.BYTE], }; + t[gl.RGB16UI] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [6], type: [gl.UNSIGNED_SHORT], }; + t[gl.RGB16I] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [6], type: [gl.SHORT], }; + t[gl.RGB32UI] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [gl.UNSIGNED_INT], }; + t[gl.RGB32I] = { textureFormat: gl.RGB_INTEGER, colorRenderable: false, textureFilterable: false, bytesPerElement: [12], type: [gl.INT], }; + t[gl.RGBA8] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [gl.UNSIGNED_BYTE], }; + t[gl.SRGB8_ALPHA8] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [gl.UNSIGNED_BYTE], }; + t[gl.RGBA8_SNORM] = { textureFormat: gl.RGBA, colorRenderable: false, textureFilterable: true, bytesPerElement: [4], type: [gl.BYTE], }; + t[gl.RGB5_A1] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 2, 4], type: [gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT_5_5_5_1, gl.UNSIGNED_INT_2_10_10_10_REV], }; + t[gl.RGBA4] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4, 2], type: [gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT_4_4_4_4], }; + t[gl.RGB10_A2] = { textureFormat: gl.RGBA, colorRenderable: true, textureFilterable: true, bytesPerElement: [4], type: [gl.UNSIGNED_INT_2_10_10_10_REV], }; + t[gl.RGBA16F] = { textureFormat: gl.RGBA, colorRenderable: false, textureFilterable: true, bytesPerElement: [16, 8], type: [gl.FLOAT, gl.HALF_FLOAT], }; + t[gl.RGBA32F] = { textureFormat: gl.RGBA, colorRenderable: false, textureFilterable: false, bytesPerElement: [16], type: [gl.FLOAT], }; + t[gl.RGBA8UI] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_BYTE], }; + t[gl.RGBA8I] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.BYTE], }; + t[gl.RGB10_A2UI] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_INT_2_10_10_10_REV], }; + t[gl.RGBA16UI] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [gl.UNSIGNED_SHORT], }; + t[gl.RGBA16I] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [8], type: [gl.SHORT], }; + t[gl.RGBA32I] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [16], type: [gl.INT], }; + t[gl.RGBA32UI] = { textureFormat: gl.RGBA_INTEGER, colorRenderable: true, textureFilterable: false, bytesPerElement: [16], type: [gl.UNSIGNED_INT], }; + + // Sized Internal + t[gl.DEPTH_COMPONENT16] = { textureFormat: gl.DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [2, 4], type: [gl.UNSIGNED_SHORT, gl.UNSIGNED_INT], }; + t[gl.DEPTH_COMPONENT24] = { textureFormat: gl.DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_INT], }; + t[gl.DEPTH_COMPONENT32F] = { textureFormat: gl.DEPTH_COMPONENT, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.FLOAT], }; + t[gl.DEPTH24_STENCIL8] = { textureFormat: gl.DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.UNSIGNED_INT_24_8], }; + t[gl.DEPTH32F_STENCIL8] = { textureFormat: gl.DEPTH_STENCIL, colorRenderable: true, textureFilterable: false, bytesPerElement: [4], type: [gl.FLOAT_32_UNSIGNED_INT_24_8_REV], }; + + Object.keys(t).forEach(function(internalFormat) { + const info = t[internalFormat]; + info.bytesPerElementMap = {}; + info.bytesPerElement.forEach(function(bytesPerElement, ndx) { + const type = info.type[ndx]; + info.bytesPerElementMap[type] = bytesPerElement; + }); + }); + } + + const validChannelsByTextureFormat = {}; + { + const v = validChannelsByTextureFormat; + v[gl.RED] = [1, 0, 0, 0]; + v[gl.RG] = [1, 1, 0, 0]; + v[gl.RGB] = [1, 1, 1, 0]; + v[gl.RGBA] = [1, 1, 1, 1]; + v[gl.RED_INTEGER] = [1, 0, 0, 0]; + v[gl.RG_INTEGER] = [1, 1, 0, 0]; + v[gl.RGB_INTEGER] = [1, 1, 1, 0]; + v[gl.RGBA_INTEGER] = [1, 1, 1, 1]; + } + + const depthTextureFormats = [ + gl.DEPTH_COMPONENT16, + gl.DEPTH_COMPONENT24, + gl.DEPTH_COMPONENT32F, + gl.DEPTH24_STENCIL8, + gl.DEPTH32F_STENCIL8, + ]; + + const intTextureFormats = [ + gl.R8I, + gl.R16I, + gl.R32I, + gl.RG8I, + gl.RG16I, + gl.RG32I, + gl.RGB8I, + gl.RGB16I, + gl.RGB32I, + gl.RGBA8I, + gl.RGBA16I, + gl.RGBA32I, + ]; + + const unsignedIntTextureFormats = [ + gl.R8UI, + gl.R16UI, + gl.R32UI, + gl.RG8UI, + gl.RG16UI, + gl.RG32UI, + gl.RGB8UI, + gl.RGB16UI, + gl.RGB32UI, + gl.RGBA8UI, + gl.RGB10_A2UI, + gl.RGBA16UI, + gl.RGBA32UI, + ]; + + const floatTextureFormats = Object.keys(textureInternalFormatInfo).map(function(v) { + return parseInt(v); + }).filter(function(format) { + return intTextureFormats.indexOf(format) < 0 && + unsignedIntTextureFormats.indexOf(format) < 0 && + depthTextureFormats.indexOf(format); + }); + + const expectedColorByInternalFormat = {}; + expectedColorByInternalFormat[gl.SRGB8_ALPHA8] = [225, 188, 137, 255]; + + function clearFloat(gl) { + gl.clearBufferfv(gl.COLOR, 0, [0, 0, 0, 0]); + } + + function clearInt(gl) { + gl.clearBufferiv(gl.COLOR, 0, [0, 0, 0, 0]); + } + + function clearUint(gl) { + gl.clearBufferuiv(gl.COLOR, 0, [0, 0, 0, 0]); + } + + + function checkData(data, expected, internalFormat, tolerance) { + const internalFormatInfo = textureInternalFormatInfo[internalFormat]; + const validChannels = validChannelsByTextureFormat[internalFormatInfo.textureFormat]; + if (!validChannels) { + testFailed('oops'); + return; + } + for (let y = 0; y < texHeight; ++y) { + for (let x = 0; x < texWidth; ++x) { + for (let c = 0; c < validChannels.length; ++c) { + if (validChannels[c]) { + const offset = (y * texWidth + x) * 4 + c; + const pixel = data[offset]; + const diff = Math.abs(pixel - expected[c]); + if (diff > tolerance) { + testFailed(`pixel ${x},${y} channel ${c} was ${pixel} expected ${expected[c]} +/- ${tolerance}`); + return; + } + } + } + } + } + testPassed(`data was ${expected.join(',')}`); + } + + function checkFloat(gl, textureInfo, expected) { + const data = new Uint8Array(texWidth * texHeight * 4); + gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); + const internalFormat = textureInfo.internalFormat; + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from readPixels with ${wtu.glEnumToString(gl, internalFormat)}`); + checkData(data, expected, internalFormat, 9); + } + + function checkInt(gl, textureInfo, expected) { + const data = new Int32Array(texWidth * texHeight * 4); + gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA_INTEGER, gl.INT, data); + const internalFormat = textureInfo.internalFormat; + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from readPixels with ${wtu.glEnumToString(gl, internalFormat)}`); + checkData(data, expected, internalFormat, 0); + } + + function checkUint(gl, textureInfo, expected) { + const data = new Uint32Array(texWidth * texHeight * 4); + gl.readPixels(0, 0, texWidth, texHeight, gl.RGBA_INTEGER, gl.UNSIGNED_INT, data); + const internalFormat = textureInfo.internalFormat; + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from readPixels with ${wtu.glEnumToString(gl, internalFormat)}`); + checkData(data, expected, internalFormat, 0); + } + + const expectedFloatColor = [.75 * 255 | 0, .5 * 255 | 0, .25 * 255 | 0, 1 * 255 | 0]; + const floatTypes = [ + { outType: 'vec4', outValue: 'vec4(.75, .5, .25, 1)', expected: expectedFloatColor, clear: clearFloat, check: checkFloat, target: gl.TEXTURE_2D, }, + { outType: 'vec4', outValue: 'vec4(.75, .5, .25, 1)', expected: expectedFloatColor, clear: clearFloat, check: checkFloat, target: gl.TEXTURE_3D, }, + { outType: 'vec4', outValue: 'vec4(.75, .5, .25, 1)', expected: expectedFloatColor, clear: clearFloat, check: checkFloat, target: gl.TEXTURE_2D_ARRAY, }, + ]; + + const expectedIntColor = [1, 2, 4, 3]; + const signedIntTypes = [ + { outType: 'ivec4', outValue: 'ivec4(1, 2, 4, 3)', expected: expectedIntColor, clear: clearInt, check: checkInt, target: gl.TEXTURE_2D, }, + { outType: 'ivec4', outValue: 'ivec4(1, 2, 4, 3)', expected: expectedIntColor, clear: clearInt, check: checkInt, target: gl.TEXTURE_3D, }, + { outType: 'ivec4', outValue: 'ivec4(1, 2, 4, 3)', expected: expectedIntColor, clear: clearInt, check: checkInt, target: gl.TEXTURE_2D_ARRAY, }, + ]; + + const expectedUintColor = [1, 2, 4, 3]; + const unsignedIntTypes = [ + { outType: 'uvec4', outValue: 'uvec4(1, 2, 4, 3)', expected: expectedUintColor, clear: clearUint, check: checkUint, target: gl.TEXTURE_2D, }, + { outType: 'uvec4', outValue: 'uvec4(1, 2, 4, 3)', expected: expectedUintColor, clear: clearUint, check: checkUint, target: gl.TEXTURE_3D, }, + { outType: 'uvec4', outValue: 'uvec4(1, 2, 4, 3)', expected: expectedUintColor, clear: clearUint, check: checkUint, target: gl.TEXTURE_2D_ARRAY, }, + ]; + + /** + * Gets the number of bytes per element for a given internalFormat / type + * @param {number} internalFormat The internalFormat parameter from texImage2D etc.. + * @param {number} type The type parameter for texImage2D etc.. + * @return {number} the number of bytes per element for the given internalFormat, type combo + * @memberOf module:twgl/textures + */ + function getBytesPerElementForInternalFormat(internalFormat, type) { + const info = textureInternalFormatInfo[internalFormat]; + if (!info) { + throw "unknown internal format"; + } + const bytesPerElement = info.bytesPerElementMap[type]; + if (bytesPerElement === undefined) { + throw "unknown internal format"; + } + return bytesPerElement; + } + + function make2DTexture(gl, target, internalFormat, format, type) { + gl.texImage2D(target, 0, internalFormat, texWidth, texHeight, 0, format, type, null); + } + + function make3DTexture(gl, target, internalFormat, format, type) { + gl.texImage3D(target, 0, internalFormat, texWidth, texHeight, texDepth, 0, format, type, null); + } + + function attach2DTexture(gl, target, texture) { + const level = 0; + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, target, texture, level); + } + + function attach3DTexture(gl, target, texture) { + const level = 0; + const slice = texDepth - 1; + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture, level, slice); + } + + const targets = {}; + targets[gl.TEXTURE_2D] = { make: make2DTexture, attach: attach2DTexture, }, + targets[gl.TEXTURE_3D] = { make: make3DTexture, attach: attach3DTexture, }, + targets[gl.TEXTURE_2D_ARRAY] = { make: make3DTexture, attach: attach3DTexture, }, + + debug("create textures"); + Object.keys(targets).forEach(function(target) { + debug(""); + target = parseInt(target); + debug(wtu.glEnumToString(gl, target)) + const targetInfo = targets[target]; + targetInfo.textures = []; + Object.keys(textureInternalFormatInfo).forEach(function(internalFormat) { + internalFormat = parseInt(internalFormat); + const isDepthFormat = depthTextureFormats.indexOf(internalFormat) >= 0; + if (isDepthFormat) { + return; + } + const info = textureInternalFormatInfo[internalFormat]; + if (!info.colorRenderable) { + return; + } + const texture = gl.createTexture(); + gl.bindTexture(target, texture); + targetInfo.make(gl, target, internalFormat, info.textureFormat, info.type[0]); + targetInfo.textures.push({ + internalFormat: internalFormat, + texture: texture, + }); + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from setup for ${wtu.glEnumToString(gl, target)} ${wtu.glEnumToString(gl, internalFormat)}`); + }); + }); + + // set the canvas to a known color + const half = 127 / 255; + gl.clearColor(half, half, half, half); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.clearColor(0, 0, 0, 0); + + const framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + gl.viewport(0, 0, texWidth, texHeight); + + const rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, texWidth, texHeight); + + testTypes('float', floatTypes, floatTextureFormats); + testTypes('int', signedIntTypes, intTextureFormats); + testTypes('unsigned', unsignedIntTypes, unsignedIntTextureFormats); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + wtu.checkCanvas(gl, [127, 127, 127, 127], "canvas should be [127, 127, 127, 127]"); + + function testTypes(label, types, compatibleFormats) { + debug(''); + types.forEach(function(typeInfo) { + debug(`\nchecking ${wtu.glEnumToString(gl, typeInfo.target)} with ${label} texture formats`); + const program = wtu.setupProgram(gl, ['vshader', makeFragmentShader(typeInfo)], [], console.log.bind(console)); + if (!program) { + testFailed("Loading program failed"); + return; + } + testPassed("Loading program succeeded"); + + const target = typeInfo.target; + const targetInfo = targets[target]; + targetInfo.textures.filter(function(textureInfo) { + return compatibleFormats.indexOf(textureInfo.internalFormat) >= 0; + }).forEach(function(textureInfo) { + const internalFormat = textureInfo.internalFormat; + const desc = `${wtu.glEnumToString(gl, target)} ${wtu.glEnumToString(gl, internalFormat)}`; + const expected = expectedColorByInternalFormat[internalFormat] || typeInfo.expected; + + debug(''); + debug(desc); + + targetInfo.attach(gl, target, textureInfo.texture); + wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE, `for ${desc}`); + typeInfo.clear(gl); + if (wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from clear to ${desc}`)) { + return; + } + typeInfo.check(gl, textureInfo, [0, 0, 0, 0]); + gl.drawArrays(gl.POINTS, 0, 1); + if (wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from render to ${desc}`)) { + return; + } + typeInfo.check(gl, textureInfo, expected); + + typeInfo.clear(gl); + if (wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from clear to ${desc}`)) { + return; + } + typeInfo.check(gl, textureInfo, [0, 0, 0, 0]); + + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb); + wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE, `for ${desc} with depth renderbuffer`); + gl.clearBufferfv(gl.DEPTH, 0, [1]); + gl.drawArrays(gl.POINTS, 0, 1); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, `No errors from render to ${desc}`); + + typeInfo.check(gl, textureInfo, expected); + }); + }); + } +} + +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/rendering/framebuffer-texture-changing-base-level.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-texture-changing-base-level.html new file mode 100644 index 0000000000..de462d6015 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-texture-changing-base-level.html @@ -0,0 +1,107 @@ +<!-- +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 framebuffer using a non-square texture with a changing base level</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="canvas" width="16" height="16"> </canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +// http://anglebug.com/2291 + +var wtu = WebGLTestUtils; +var gl; + +function testNonSquareFramebufferTextureWithChangingBaseLevel() { + var program = wtu.setupSimpleTextureProgram(gl); + wtu.setupUnitQuad(gl); + + var width = 8; + var height = 4; + + debug(""); + debug("Text texture width " + width + " x height " + height); + + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); + 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); + + // Create all mipmap levels for the texture from level 0 to the 1x1 pixel level. + var level = 0; + var levelW = width; + var levelH = height; + gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, levelW, levelH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + while (levelW > 1 || levelH > 1) + { + ++level; + levelW = Math.max(1, Math.floor(width / Math.pow(2, level))); + levelH = Math.max(1, Math.floor(height / Math.pow(2, level))); + gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, levelW, levelH, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + } + + // Clear each level of the texture using an FBO. Change the base level to match the level used for the FBO on each iteration. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + level = 0; + levelW = width; + levelH = height; + while (levelW > 1 || levelH > 1) + { + var levelW = Math.floor(width / Math.pow(2, level)); + var levelH = Math.floor(height / Math.pow(2, level)); + + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, level); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, level); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Clearing the texture level " + level + " to green should succeed."); + wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0, 255, 0, 255], "should be green"); + ++level; + } + + debug(""); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0); + wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Drawing the texture to default framebuffer with base level 0 should succeed."); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + gl.deleteTexture(texture); + gl.deleteFramebuffer(fbo); +} + +description(); + +var canvas = document.getElementById("canvas"); +shouldBeNonNull("gl = wtu.create3DContext(canvas, undefined, 2)"); + +testNonSquareFramebufferTextureWithChangingBaseLevel(); + +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/rendering/framebuffer-texture-level1.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-texture-level1.html new file mode 100644 index 0000000000..d5ac8cb1e7 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-texture-level1.html @@ -0,0 +1,64 @@ +<!-- +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 framebuffer using texture level 1</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="2" height="2"> </canvas> +<script> +"use strict"; +var wtu = WebGLTestUtils; +var gl; + +function testFramebufferTextureWithNonZeroBaseLevel(level) { + if (level < 1) { + throw "This test is incorrect if level < 1"; + } + var width = 32; + var height = 16; + 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); + gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, level); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, level); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed."); + + gl.deleteTexture(texture); + gl.deleteFramebuffer(fbo); +} + +description("Test fbo completeness when using non level 0 texture images"); + +var canvas = document.getElementById("canvas"); +shouldBeNonNull("gl = wtu.create3DContext(canvas, undefined, 2)"); + +testFramebufferTextureWithNonZeroBaseLevel(1); + +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/rendering/framebuffer-to-texture.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-to-texture.html new file mode 100644 index 0000000000..926e14beab --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-to-texture.html @@ -0,0 +1,201 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL framebuffer to texture conformance 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> +<canvas id="canvas"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("Test resolving and copying the framebuffer to a texture, and drawing the result."); +debug('Reduced test case for <a href="http://anglebug.com/6972">http://anglebug.com/6972</a>'); + +// Reproduces two behaviors: +// +// 1) The initial draw disappearing entirely from the default back +// buffer. The current test case does not show this behavior +// independently from the other, but a previous iteration, with the +// textured quad scaled to half size and translated (-0.5, -0.5), did. +// +// 2) With Metal debug layers and load/store validation turned on on +// Intel Macs, the transparent area of the texture prior to the bug +// fix was magenta = undefined. Similar behavior would presumably +// reproduce on M1 hardware without debug layers or validation. + +const size = 64; +const halfSize = size / 2; +const green = [ 0, 255, 0, 255 ]; +const transparent = [ 0, 0, 0, 0 ]; + +let wtu = WebGLTestUtils; +let canvas = document.getElementById("canvas"); +canvas.width = size; +canvas.height = size; + +let gl = wtu.create3DContext("canvas", { + // Antialiasing is crucial for reproducing the bug. + antialias: true, + // Depth testing is not. + depth: false, +}, 2); + +function allocateTexture(sz) { + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, sz, sz); + 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); + gl.bindTexture(gl.TEXTURE_2D, null); + return texture; +} + +// Allocate destination texture +let destTexture = allocateTexture(halfSize); + +// Set up half-size solid color quad in center +let colorQuadVAO = gl.createVertexArray(); +gl.bindVertexArray(colorQuadVAO); +let colorQuadProgram = wtu.setupColorQuad(gl, 0, { scale: 0.5 }); + +// Setup textured quad covering the entire renderable area +let quadVAO = gl.createVertexArray(); +gl.bindVertexArray(quadVAO); +let quadProgram = wtu.setupTexturedQuad(gl, 0, 1); +gl.useProgram(quadProgram); +let quadTexLoc = gl.getUniformLocation(quadProgram, "tex"); +gl.uniform1i(quadTexLoc, 0); + +gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); +gl.activeTexture(gl.TEXTURE0); // To match quadTexLoc=0 + +function runTest() { + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.disable(gl.BLEND); + gl.bindVertexArray(colorQuadVAO); + gl.useProgram(colorQuadProgram); + wtu.drawUByteColorQuad(gl, [ 0, 255, 0, 255 ]); + + gl.bindTexture(gl.TEXTURE_2D, destTexture); + // Copy the upper right corner of the framebuffer to the texture. + gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, halfSize, halfSize, halfSize, halfSize); + gl.bindTexture(gl.TEXTURE_2D, null); + gl.useProgram(quadProgram); + gl.enable(gl.BLEND); + gl.bindVertexArray(quadVAO); + gl.bindTexture(gl.TEXTURE_2D, destTexture); + // Magnify and blend this texture over the current framebuffer. + wtu.drawUnitQuad(gl); +} + +function runUserDefinedFBOTest() { + let fbo1 = gl.createFramebuffer(); + let fbo2 = gl.createFramebuffer(); + let rb = gl.createRenderbuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo1); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, size, size); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, [ gl.FRAMEBUFFER_COMPLETE ]); + + let tex = allocateTexture(size, size); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, [ gl.FRAMEBUFFER_COMPLETE ]); + + // Same rendering steps as in the default-framebuffer test, with appropriate framebuffer blits interspersed. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo1); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.disable(gl.BLEND); + gl.bindVertexArray(colorQuadVAO); + gl.useProgram(colorQuadProgram); + wtu.drawUByteColorQuad(gl, [ 0, 255, 0, 255 ]); + + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo1); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo2); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2); + + gl.bindTexture(gl.TEXTURE_2D, destTexture); + // Copy the upper right corner of the framebuffer to the texture. + gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, halfSize, halfSize, halfSize, halfSize); + gl.bindTexture(gl.TEXTURE_2D, null); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo1); + + gl.useProgram(quadProgram); + gl.enable(gl.BLEND); + gl.bindVertexArray(quadVAO); + gl.bindTexture(gl.TEXTURE_2D, destTexture); + // Magnify and blend this texture over the current framebuffer. + wtu.drawUnitQuad(gl); + + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo1); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo2); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2); + + // No longer easy to put these results on the canvas, because it's + // antialiased and we can't blitFramebuffer to it. Let's assume + // that if failures occur, they'll be straightforward to debug. +} + +function checkRenderingResults(prefix) { + // Center quad should be rendered correctly. + wtu.checkCanvasRect(gl, + halfSize / 2 + 1, halfSize / 2 + 1, + halfSize - 2, halfSize - 2, + green, + prefix + ": center quad should be green"); + + // Overlapping lower-left quad should be green as well. + wtu.checkCanvasRect(gl, + 1, 1, + halfSize - 2, halfSize - 2, + green, + prefix + ": lower left quad should be green"); + + // Leftmost area above the lower-left quad should be transparent. + wtu.checkCanvasRect(gl, + 1, halfSize + 1, + halfSize / 2 - 2, halfSize / 2 - 2, + transparent, + prefix + ": leftmost area above lower left quad should be transparent"); + + // Bottommost area to the right of the lower-left quad should be transparent. + wtu.checkCanvasRect(gl, + halfSize + 1, 1, + halfSize / 2 - 2, halfSize / 2 - 2, + transparent, + prefix + ": bottommost area to the right of lower left quad should be transparent"); +} + +runTest(); +checkRenderingResults("default back buffer"); + +runUserDefinedFBOTest(); +checkRenderingResults("user-defined framebuffer"); + +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at the end of the test."); + +finishTest(); + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-unsupported.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-unsupported.html new file mode 100644 index 0000000000..1780650ed6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/framebuffer-unsupported.html @@ -0,0 +1,134 @@ +<!-- +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 FRAMEBUFFER_UNSUPPORTED 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> +<canvas id="canvas" width="2" height="2"> </canvas> + +<script> +"use strict"; +var wtu = WebGLTestUtils; +var gl; +var canvas = document.getElementById("canvas"); + +function checkFramebuffer(expected) { + var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (expected.indexOf(actual) < 0) { + var msg = "checkFramebufferStatus expects ["; + for (var index = 0; index < expected.length; ++index) { + msg += wtu.glEnumToString(gl, expected[index]); + if (index + 1 < expected.length) + msg += ", "; + } + msg += "], was " + wtu.glEnumToString(gl, actual); + testFailed(msg); + } else { + var msg = "checkFramebufferStatus got " + wtu.glEnumToString(gl, actual) + + " as expected"; + testPassed(msg); + } +} + +function testImageAttachedTwoPoints() { + debug(""); + debug("Checking an image is attached to more than one color attachment in a framebuffer."); + + var tex1 = gl.createTexture(); + var tex2 = gl.createTexture(); + var fb = gl.createFramebuffer(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, + 0, // level + gl.RGBA, // internalFormat + 1, // width + 1, // height + 0, // border + gl.RGBA, // format + gl.UNSIGNED_BYTE, // type + new Uint8Array(4)); // data + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture creation should succeed."); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex1, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, tex2, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, tex1, 0); + checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteFramebuffer(fb); + fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + var texCube = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texCube); + for (var target = gl.TEXTURE_CUBE_MAP_POSITIVE_X; target < gl.TEXTURE_CUBE_MAP_POSITIVE_X + 6; target++) { + gl.texImage2D(target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4)); + } + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_CUBE_MAP_POSITIVE_X, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]); + + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteFramebuffer(fb); + fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + var tex3d = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_3D, tex3d); + gl.texImage3D(gl.TEXTURE_3D, + 0, // level + gl.RGBA, // internalFormat + 2, // width + 2, // height + 2, // depth + 0, // border + gl.RGBA, // format + gl.UNSIGNED_BYTE, // type + new Uint8Array(4 * 2 * 2 * 2)); // data + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex3d, 0, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, tex3d, 0, 1); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, tex3d, 0, 0); + checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]); + + // Clean up + gl.deleteTexture(tex1); + gl.deleteTexture(tex2); + gl.deleteTexture(texCube); + gl.deleteTexture(tex3d); + gl.deleteFramebuffer(fb); +} + +description("This tests FRAMEBUFFER_UNSUPPORTED."); + +shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, 2)"); + +testImageAttachedTwoPoints(); + +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/rendering/fs-color-type-mismatch-color-buffer-type.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html new file mode 100644 index 0000000000..36f5f50a72 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/fs-color-type-mismatch-color-buffer-type.html @@ -0,0 +1,169 @@ +<!-- +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>The Color Types of Fragment Shader's Outputs Should Match The Data Types of Color Buffers</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script id="vshader" type="x-shader/x-vertex">#version 300 es +in highp vec4 aPosition; +void main() { + gl_Position = aPosition; +} +</script> + +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +out vec4 oColor; +void main() { + oColor = vec4(1.0, 0.0, 0.0, 0.0); +} +</script> + +<script id="fshaderMRT" type="x-shader/x-fragment">#version 300 es +precision mediump float; +out vec4 oColor[2]; +void main() { + oColor[0] = vec4(1.0, 0.0, 0.0, 0.0); +} +</script> + +<script id="fshaderRealMRT" type="x-shader/x-fragment">#version 300 es +precision mediump float; +out vec4 oColor[2]; +void main() { + oColor[0] = vec4(1.0, 0.0, 0.0, 0.0); + oColor[1] = vec4(0.0, 1.0, 0.0, 0.0); +} +</script> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies that the color types of fragment shader's outputs should match color buffers' types."); + +var gl = wtu.create3DContext("example", undefined, 2); + +var width = 8; +var height = 8; +var tex0; +var tex1; +var rb0; +var rb1; +var fbo = gl.createFramebuffer(); +var program0; +var program1; +var program2; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + init(); + + // COLOR_ATTACHMENT0 is fixed-point data, which can be converted to float. + // COLOR_ATTACHMENT1 is integer data. The fragment outputs are all float. + allocate_textures(); + check_type_match(); + allocate_renderbuffers(); + check_type_match(); +} + +function check_type_match() { + gl.useProgram(program0); + rendering([gl.COLOR_ATTACHMENT0, gl.NONE], gl.NO_ERROR); + rendering([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1], gl.INVALID_OPERATION); + + gl.useProgram(program1); + rendering([gl.COLOR_ATTACHMENT0, gl.NONE], gl.NO_ERROR); + rendering([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1], gl.INVALID_OPERATION); + + gl.useProgram(program2); + rendering([gl.COLOR_ATTACHMENT0, gl.NONE], gl.NO_ERROR); + rendering([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1], gl.INVALID_OPERATION); +} + +function init() { + program0 = wtu.setupProgram(gl, ['vshader', 'fshader'], ['aPosition'], [0]); + program1 = wtu.setupProgram(gl, ['vshader', 'fshaderMRT'], ['aPosition'], [0]); + program2 = wtu.setupProgram(gl, ['vshader', 'fshaderRealMRT'], ['aPosition'], [0]); + if (!program0 || !program1 || !program2) { + testFailed("Failed to set up program"); + return; + } + testPassed("Succeed to set up program"); + + wtu.setupUnitQuad(gl, 0, 1); + gl.viewport(0, 0, width, height); +} + +function allocate_textures() { + tex0 = gl.createTexture(); + tex1 = gl.createTexture(); + wtu.fillTexture(gl, tex0, width, height, [0xff, 0x0, 0x0, 0xff], 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.RGBA); + wtu.fillTexture(gl, tex1, width, height, [0x0, 0xff, 0x0, 0xff], 0, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, gl.RGBA8UI); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex0, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, tex1, 0); +} + +function allocate_renderbuffers() { + rb0 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb0); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, width, height); + rb1 = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8UI, width, height); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, rb1); +} + +function rendering(draw_buffers, error) { + gl.drawBuffers(draw_buffers); + + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } + + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, error, "If color buffers' type mismatch the type of fragment shader's outputs, geneate INVALID_OPERATION. Otherwise, it should be NO_ERROR"); +} + +gl.bindTexture(gl.TEXTURE_2D, null); +gl.bindRenderbuffer(gl.RENDERBUFFER, null); +gl.bindFramebuffer(gl.FRAMEBUFFER, null); +gl.useProgram(null); +gl.deleteTexture(tex0); +gl.deleteTexture(tex1); +gl.deleteRenderbuffer(rb0); +gl.deleteRenderbuffer(rb1); +gl.deleteFramebuffer(fbo); +gl.deleteProgram(program0); +gl.deleteProgram(program1); +gl.deleteProgram(program2); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-arrays.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-arrays.html new file mode 100644 index 0000000000..65009ea5b3 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-arrays.html @@ -0,0 +1,271 @@ +<!-- +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 Instanced Arrays Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing instanced draws --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 aPosition; +attribute vec2 aOffset; +attribute vec4 aColor; +varying vec4 vColor; +void main() { + vColor = aColor; + gl_Position = aPosition + vec4(aOffset, 0.0, 0.0); +} +</script> + +<script id="outputFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 vColor; +void main() { + gl_FragColor = vColor; +} +</script> + +<script> +"use strict"; +description("This test verifies the functionality of Instanced Arrays."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runDivisorTest(); + runOutputTests(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runDivisorTest() { + debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR"); + + shouldBe("gl.VERTEX_ATTRIB_ARRAY_DIVISOR", "0x88FE"); + + var max_vertex_attribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + for (var i = 0; i < max_vertex_attribs; ++i) { + var queried_value = gl.getVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_DIVISOR); + if(queried_value == 0){ + testPassed("Vertex attribute " + i + " must has a default divisor of 0"); + } + else{ + testFailed("Default divisor of vertex attribute " + i + " should be: 0, returned value was: " + queried_value); + } + } + + gl.vertexAttribDivisor(max_vertex_attribs, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "vertexAttribDivisor index set greater than or equal to MAX_VERTEX_ATTRIBS should be an invalid value"); + + gl.vertexAttribDivisor(max_vertex_attribs-1, 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertexAttribDivisor index set less than MAX_VERTEX_ATTRIBS should succeed"); + + var queried_value = gl.getVertexAttrib(max_vertex_attribs-1, gl.VERTEX_ATTRIB_ARRAY_DIVISOR); + if(queried_value == 2){ + testPassed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR matches expecation"); + } + else{ + testFailed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR should be: 2, returned value was: " + queried_value); + } +} + +function runOutputTests() { + var e = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher + var instanceCount = 4; + + debug("Testing various draws for valid built-in function behavior"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0, 0, 0, 0); + + var positionLoc = 0; + var offsetLoc = 2; + var colorLoc = 3; + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['aPosition', 'aOffset', 'aColor'], [positionLoc, offsetLoc, colorLoc]); + + var offsets = new Float32Array([ + -1.0, 1.0, + 1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0, + ]); + var offsetBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + gl.enableVertexAttribArray(offsetLoc); + gl.vertexAttribPointer(offsetLoc, 2, gl.FLOAT, false, 0, 0); + gl.vertexAttribDivisor(offsetLoc, 1); + + var colors = new Float32Array([ + 1.0, 0.0, 0.0, 1.0, // Red + 0.0, 1.0, 0.0, 1.0, // Green + 0.0, 0.0, 1.0, 1.0, // Blue + 1.0, 1.0, 0.0, 1.0, // Yellow + // extra data when colorLoc divisor is set back to 0 + 1.0, 1.0, 0.0, 1.0, // Yellow + 1.0, 1.0, 0.0, 1.0, // Yellow + ]); + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + gl.vertexAttribDivisor(colorLoc, 1); + + // Draw 1: Draw Non-indexed instances + debug("Testing drawArraysInstanced"); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.setupUnitQuad(gl, 0); + + // Test drawArraysInstanced error conditions + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, canvas.height/2, canvas.width/2, canvas.height/2, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, 0, 0, canvas.width/2, canvas.height/2, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, 0, canvas.width/2, canvas.height/2, [255, 255, 0, 255]); + + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, -1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstanced cannot have a primcount less than 0"); + + gl.drawArraysInstanced(gl.TRIANGLES, 0, -1, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstanced cannot have a count less than 0"); + + gl.vertexAttribDivisor(positionLoc, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "It's allowed for all vertex attributes to have non-zero divisors when calling drawArraysInstanced"); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "It's allowed for all vertex attributes to have non-zero divisors when calling drawArrays"); + wtu.checkCanvas(gl, [0, 0, 0, 0], "Nothing should be drawn on the framebuffer when all attributes have non-zero divisors (not enough vertices per instance to form a triangle)"); + gl.vertexAttribDivisor(positionLoc, 0); + + gl.drawArraysInstanced(gl.POINTS, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced with POINTS should succeed"); + gl.drawArraysInstanced(gl.LINES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced with LINES should succeed"); + gl.drawArraysInstanced(gl.LINE_LIST, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced with LINE_LIST should return succeed"); + gl.drawArraysInstanced(gl.TRI_LIST, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced with TRI_LIST should succeed"); + + gl.drawArraysInstanced(desktopGL['QUAD_STRIP'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstanced with QUAD_STRIP should return INVALID_ENUM"); + gl.drawArraysInstanced(desktopGL['QUADS'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstanced with QUADS should return INVALID_ENUM"); + gl.drawArraysInstanced(desktopGL['POLYGON'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstanced with POLYGON should return INVALID_ENUM"); + + debug("Testing drawArraysInstanced with param 'first' > 0"); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.setupQuad(gl, { + positionLocation: 0, + scale: 0.5 + }); + var offsetsHalf = new Float32Array([ + -0.5, 0.5, + 0.5, 0.5, + -0.5, -0.5, + 0.5, -0.5 + ]); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsetsHalf, gl.STATIC_DRAW); + + gl.drawArraysInstanced(gl.TRIANGLES, 3, 3, instanceCount); + var w = Math.floor(0.25*canvas.width), + h = Math.floor(0.25*canvas.height); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0.5*canvas.height, w, h, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0.5*canvas.height, w, h, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0, w, h, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0, w, h, [255, 255, 0, 255]); + + debug("Testing drawArraysInstanced with attributes 'divisor' reset to 0"); + debug("Correct rendering output: 4 yellow triangles"); + debug("Possible incorrect rendering output: missing triangles, or triangles with different color at each vertex"); + gl.vertexAttribDivisor(colorLoc, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArraysInstanced(gl.TRIANGLES, 3, 3, instanceCount); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0.5*canvas.height, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0.5*canvas.height, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0, w, h, [255, 255, 0, 255]); + gl.vertexAttribDivisor(colorLoc, 1); + + wtu.setupUnitQuad(gl, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + + // Draw 2: Draw indexed instances + debug("Testing drawElementsInstanced"); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.setupIndexedQuad(gl, 1, 0); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, canvas.height/2, canvas.width/2, canvas.height/2, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, 0, 0, canvas.width/2, canvas.height/2, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, 0, canvas.width/2, canvas.height/2, [255, 255, 0, 255]); + + // Test drawElementsInstanced error conditions + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, -1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawElementsInstanced cannot have a primcount less than 0"); + + gl.drawElementsInstanced(gl.TRIANGLES, -1, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawElementsInstanced cannot have a count less than 0"); + + gl.vertexAttribDivisor(positionLoc, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "It's allowed for all vertex attributes to have non-zero divisors when calling drawElementsInstanced"); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "It's allowed for all vertex attributes to have non-zero divisors when calling drawElements"); + wtu.checkCanvas(gl, [0, 0, 0, 0], "Nothing should be drawn on the framebuffer when all attributes have non-zero divisors (not enough vertices per instance to form a triangle)"); + gl.vertexAttribDivisor(positionLoc, 0); + + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with UNSIGNED_BYTE should succeed"); + + gl.drawElementsInstanced(gl.POINTS, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with POINTS should succeed"); + gl.drawElementsInstanced(gl.LINES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with LINES should succeed"); + gl.drawElementsInstanced(gl.LINE_LIST, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with LINE_LIST should return succeed"); + gl.drawElementsInstanced(gl.TRI_LIST, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with TRI_LIST should succeed"); + + gl.drawElementsInstanced(desktopGL['QUAD_STRIP'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstanced with QUAD_STRIP should return INVALID_ENUM"); + gl.drawElementsInstanced(desktopGL['QUADS'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstanced with QUADS should return INVALID_ENUM"); + gl.drawElementsInstanced(desktopGL['POLYGON'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstanced with POLYGON should return INVALID_ENUM"); +} + +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/rendering/instanced-rendering-bug.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-rendering-bug.html new file mode 100644 index 0000000000..59f25096d5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-rendering-bug.html @@ -0,0 +1,254 @@ +<!-- +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 Instanced Arrays Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 128px; height: 128px;"> </canvas> +<div id="console"></div> +<script id="outputVertexShader" type="x-shader/x-vertex">#version 300 es +in highp vec2 aPosition; +in highp float aOffset; +in highp float aColor; +out mediump float vColor; +void main() { + gl_Position = vec4(aPosition, 0.0, 1.0) + vec4(aOffset, 0.0, 0.0, 0.0); + vColor = aColor; +} +</script> + +<script id="outputFragmentShader" type="x-shader/x-fragment">#version 300 es +layout(location = 0) out mediump vec4 oColor; +in mediump float vColor; +void main() { + oColor = vec4(vColor, 0.0, 0.0, 1.0); +} +</script> + +<script> +"use strict"; +description("This test verifies a bug related with instanced rendering on Mac AMD."); +debug("http://crbug.com/645298"); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +// The second and fourth test cases fail - it seems if the divisor doesn't change, +// the next instanced draw call behaves incorrectly. +// Also note that if we don't perform a readPixels (in wtu.checkCanvasRect), the bug +// isn't triggered. +var testCases = [ + { instanceCount: 8, divisor: 4 }, + { instanceCount: 6, divisor: 4 }, + { instanceCount: 6, divisor: 3 }, + { instanceCount: 8, divisor: 3 }, +]; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + for (var ii = 0; ii < testCases.length; ++ii) { + runDrawArraysTest(testCases[ii].instanceCount, testCases[ii].divisor); + } + + for (var ii = 0; ii < testCases.length; ++ii) { + runDrawElementsTest(testCases[ii].instanceCount, testCases[ii].divisor); + } +} + +function runDrawArraysTest(instanceCount, divisor) { + debug(""); + debug("Testing drawArraysInstanced: instanceCount = " + instanceCount + ", divisor = " + divisor); + + gl.viewport(0, 0, canvas.width, canvas.height); + + var vao = gl.createVertexArray(); + gl.bindVertexArray(vao); + + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"]); + var positionLoc = gl.getAttribLocation(program, "aPosition"); + var offsetLoc = gl.getAttribLocation(program, "aOffset"); + var colorLoc = gl.getAttribLocation(program, "aColor"); + if (!program || positionLoc < 0 || offsetLoc < 0 || colorLoc < 0) { + testFailed("Set up program failed"); + return; + } + testPassed("Set up program succeeded"); + + var scale = 1.0 / instanceCount; + + gl.enableVertexAttribArray(positionLoc); + gl.vertexAttribDivisor(positionLoc, 0); + var positions = new Float32Array([ + 1.0 * scale, 1.0, + -1.0 * scale, 1.0, + -1.0 * scale, -1.0, + 1.0 * scale, 1.0, + -1.0 * scale, -1.0, + 1.0 * scale, -1.0, + ]); + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(offsetLoc); + gl.vertexAttribDivisor(offsetLoc, 1); + var offsets = new Float32Array(instanceCount); + for (var ii = 0; ii < instanceCount; ++ii) { + offsets[ii] = scale * (1 - instanceCount + ii * 2); + } + var offsetBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + gl.vertexAttribPointer(offsetLoc, 1, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribDivisor(colorLoc, divisor); + var colorCount = instanceCount / divisor; + if ((instanceCount % divisor) != 0) + colorCount++; + var colors = new Float32Array(colorCount); + for (var ii = 0; ii < colorCount; ++ii) { + colors[ii] = 1.0 / colorCount * (ii + 1); + } + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.vertexAttribPointer(colorLoc, 1, gl.FLOAT, false, 0, 0); + + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced should succeed"); + + var colorIndex = -1; + for (var ii = 0; ii < instanceCount; ++ii) { + if ((ii % divisor) == 0) + colorIndex++; + var refColor = [ Math.floor(colors[colorIndex] * 255), 0, 0, 255 ]; + wtu.checkCanvasRect(gl, Math.floor(canvas.width / instanceCount * ii) + 1, 0, 1, canvas.height, refColor, + "instance " + ii + " should be " + refColor, 2); + } + + gl.deleteBuffer(positionBuffer); + gl.deleteBuffer(offsetBuffer); + gl.deleteBuffer(colorBuffer); + gl.deleteProgram(program); + gl.deleteVertexArray(vao); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.useProgram(null); + gl.bindVertexArray(null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clean up should succeed"); +} + +function runDrawElementsTest(instanceCount, divisor) { + debug(""); + debug("Testing drawElementsInstanced: instanceCount = " + instanceCount + ", divisor = " + divisor); + + gl.viewport(0, 0, canvas.width, canvas.height); + + var vao = gl.createVertexArray(); + gl.bindVertexArray(vao); + + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"]); + var positionLoc = gl.getAttribLocation(program, "aPosition"); + var offsetLoc = gl.getAttribLocation(program, "aOffset"); + var colorLoc = gl.getAttribLocation(program, "aColor"); + if (!program || positionLoc < 0 || offsetLoc < 0 || colorLoc < 0) { + testFailed("Set up program failed"); + return; + } + testPassed("Set up program succeeded"); + + var scale = 1.0 / instanceCount; + + gl.enableVertexAttribArray(positionLoc); + gl.vertexAttribDivisor(positionLoc, 0); + var positions = new Float32Array([ + 1.0 * scale, 1.0, + -1.0 * scale, 1.0, + -1.0 * scale, -1.0, + 1.0 * scale, -1.0, + ]); + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(offsetLoc); + gl.vertexAttribDivisor(offsetLoc, 1); + var offsets = new Float32Array(instanceCount); + for (var ii = 0; ii < instanceCount; ++ii) { + offsets[ii] = scale * (1 - instanceCount + ii * 2); + } + var offsetBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + gl.vertexAttribPointer(offsetLoc, 1, gl.FLOAT, false, 0, 0); + + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribDivisor(colorLoc, divisor); + var colorCount = instanceCount / divisor; + if ((instanceCount % divisor) != 0) + colorCount++; + var colors = new Float32Array(colorCount); + for (var ii = 0; ii < colorCount; ++ii) { + colors[ii] = 1.0 / colorCount * (ii + 1); + } + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.vertexAttribPointer(colorLoc, 1, gl.FLOAT, false, 0, 0); + + var indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + var indices = new Uint16Array([0, 1, 2, 0, 2, 3]); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); + + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced should succeed"); + + var colorIndex = -1; + for (var ii = 0; ii < instanceCount; ++ii) { + if ((ii % divisor) == 0) + colorIndex++; + var refColor = [ Math.floor(colors[colorIndex] * 255), 0, 0, 255 ]; + wtu.checkCanvasRect(gl, Math.floor(canvas.width / instanceCount * ii) + 1, 0, 1, canvas.height, refColor, + "instance " + ii + " should be " + refColor, 2); + } + + gl.deleteBuffer(positionBuffer); + gl.deleteBuffer(offsetBuffer); + gl.deleteBuffer(colorBuffer); + gl.deleteBuffer(indexBuffer); + gl.deleteProgram(program); + gl.deleteVertexArray(vao); + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + gl.useProgram(null); + gl.bindVertexArray(null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clean up should succeed"); +} + +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/rendering/instanced-rendering-large-divisor.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-rendering-large-divisor.html new file mode 100644 index 0000000000..229649ee3c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/instanced-rendering-large-divisor.html @@ -0,0 +1,145 @@ +<!-- +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 Instanced Arrays Conformance Tests - large vertex attrib divisors</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas"> </canvas> +<div id="console"></div> +<script id="outputVertexShader" type="x-shader/x-vertex">#version 300 es +layout(location = 0) in vec2 a_position; +layout(location = 1) in float a_positionOffset; +layout(location = 2) in vec4 a_color; +out vec4 v_color; +void main() +{ + gl_Position = vec4(a_position.x * 0.05 + a_positionOffset, a_position.y * 0.05, 0.0, 1.0); + v_color = a_color; +} +</script> + +<script id="outputFragmentShader" type="x-shader/x-fragment">#version 300 es +precision highp float; +in vec4 v_color; +out vec4 my_FragColor; +void main() +{ + my_FragColor = v_color; +} +</script> + +<script> +"use strict"; +description("Test switching vertex attrib divisor of one attribute between different large values"); +// This is a regression test for http://anglebug.com/2832 + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +canvas.width = 256; +canvas.height = 256; +var gl = wtu.create3DContext(canvas, null, 2); + +var colorDivisor = 65536 * 2; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runTest(); +} + +function runTest() { + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"]); + + gl.clearColor(0.0, 0.0, 1.0, 1.0); + + wtu.setupIndexedQuadWithOptions(gl, {positionLocation: 0}); + + var offsetBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuf); + // Note that only the first two offsets below should be used for rendering. + // We add some extra ones to the buffer since it can reveal if a too small divisor is used on the WebGL backend. + var offsetData = []; + for (var i = 0; i < 4; ++i) { + offsetData.push(0.0 + i * 0.25); + } + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(offsetData), gl.DYNAMIC_DRAW); + + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up vertex buffer should succeed"); + + var colorBuf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuf); + // Red and green colors. + var colorData = new Float32Array([1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0]); + gl.bufferData(gl.ARRAY_BUFFER, colorData, gl.DYNAMIC_DRAW); + + gl.enableVertexAttribArray(2); + gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0); + gl.vertexAttribDivisor(2, colorDivisor); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setting up color buffer should succeed"); + + var divisorsToTry = [ + 256, + 65536, + 65536 * 2 + ]; + + for (var i = 0; i < divisorsToTry.length; ++i) { + runDrawElementsTest(divisorsToTry[i]); + } +} + +function runDrawElementsTest(divisor) { + debug("Using divisor " + divisor); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.vertexAttribDivisor(1, divisor); + + var instanceCount = divisor + 1; + var quadsRendered = Math.floor((instanceCount - 1) / divisor) + 1; + + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Instanced draw should succeed"); + + for (var quadIndex = 0; quadIndex < quadsRendered + 1; ++quadIndex) { + var quadX = Math.floor((quadIndex / 8) * 256 + 128); + var quadY = 128; + if (quadIndex < quadsRendered) { + var quadColorIndex = Math.floor((quadIndex * divisor) / colorDivisor); + if (quadColorIndex == 0) { + wtu.checkCanvasRect(gl, quadX, quadY, 1, 1, [255, 0, 0, 255]); + } else { + wtu.checkCanvasRect(gl, quadX, quadY, 1, 1, [0, 255, 0, 255]); + } + } else { + wtu.checkCanvasRect(gl, quadX, quadY, 1, 1, [0, 0, 255, 255]); + } + } +} + +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/rendering/line-rendering-quality.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/line-rendering-quality.html new file mode 100644 index 0000000000..24442ea3c6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/line-rendering-quality.html @@ -0,0 +1,27 @@ +<!-- +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> +<title>Line rendering quality test</title> +<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="testbed" width="256" height="256"></canvas> +<canvas id="testbed2" width="256" height="256"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +var contextVersion = 2; +</script> +<script src="../../js/tests/line-rendering-quality.js"></script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-depth-resolve.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-depth-resolve.html new file mode 100644 index 0000000000..14aeab4f87 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-depth-resolve.html @@ -0,0 +1,179 @@ +<!-- +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. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL framebuffer to texture conformance 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> +<canvas id="canvas"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("Test resolving multisample depth buffer"); +debug('Reduced test case for <a href="https://bugs.webkit.org/show_bug.cgi?id=238118">https://bugs.webkit.org/show_bug.cgi?id=238118</a>'); + +// Reproduces an inconistent behavior where if: +// 1) You render into a multisampling frame buffer +// 2) Geometry is drawn with DEPTH_TEST disabled and then enabled +// 3) More than one frame is rendered via requestAnimationFrame + +const size = 64; +const halfSize = size / 2; + +let wtu = WebGLTestUtils; +let canvas = document.getElementById("canvas"); +canvas.width = size; +canvas.height = size; + +let gl = wtu.create3DContext("canvas", {}, 2); + +function createTexture(res, format, bytes) { + let texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texStorage2D(gl.TEXTURE_2D, 1, format, res, res); + 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); + gl.bindTexture(gl.TEXTURE_2D, null); + return texture; +} + +function createRenderBuffer(res, format, samples) { + let rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + if (samples > 1) + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, format, res, res); + else + gl.renderbufferStorage(gl.RENDERBUFFER, format, res, res); + return rb; +} + +let yellowQuadVAO = gl.createVertexArray(); +gl.bindVertexArray(yellowQuadVAO); +let yellowQuadProgram = wtu.setupColorQuad(gl, 0, { scale: 0.75 }); + +let blueQuadVAO = gl.createVertexArray(); +gl.bindVertexArray(blueQuadVAO); +let blueQuadProgram = wtu.setupColorQuad(gl, 0, { scale: 0.5 }); + +let fsVAO = gl.createVertexArray(); +gl.bindVertexArray(fsVAO); +let fsProgram = wtu.setupTexturedQuad(gl, 0, 1); +gl.useProgram(fsProgram); +let fsTexLoc = gl.getUniformLocation(fsProgram, "tex"); +gl.uniform1i(fsTexLoc, 0); + +// An incorrect render can occur if... + +// 1) You use renderbufferStorageMultisample. +const msaaSamples = 4; +const colorRB = createRenderBuffer(size, gl.RGBA8, msaaSamples); +const depthRB = createRenderBuffer(size, gl.DEPTH_COMPONENT16, msaaSamples); +const resolveTex = createTexture(size, gl.RGBA8); + +let renderFBO = gl.createFramebuffer(); +gl.bindFramebuffer(gl.FRAMEBUFFER, renderFBO); +gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRB); +gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthRB); + +let resolveFBO = gl.createFramebuffer(); +gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFBO); +gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, resolveTex, 0); +gl.bindFramebuffer(gl.FRAMEBUFFER, null); + +gl.disable(gl.CULL_FACE); +gl.disable(gl.BLEND); + +var frameCount = 0; +function runTest() { + // 2) Render from requestAnimationFrame, only starting with the 2nd frame. + gl.bindFramebuffer(gl.FRAMEBUFFER, renderFBO); + + // Clear background red + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT); + + // 3) You disable gl.DEPTH_TEST + gl.disable(gl.DEPTH_TEST); + gl.depthMask(false); + + gl.bindVertexArray(yellowQuadVAO); + gl.useProgram(yellowQuadProgram); + wtu.drawUByteColorQuad(gl, [ 255, 255, 0, 255 ]); + + // 4) And re-enable gl.DEPTH_TEST + gl.enable(gl.DEPTH_TEST); + gl.depthMask(true); + + gl.bindVertexArray(blueQuadVAO); + gl.useProgram(blueQuadProgram); + wtu.drawUByteColorQuad(gl, [ 0, 0, 255, 255 ]); + + // Resolve the multisample framebuffer to a texture + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, renderFBO); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resolveFBO); + gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 0.0]); + gl.blitFramebuffer(0, 0, size, size, + 0, 0, size, size, + gl.COLOR_BUFFER_BIT, gl.LINEAR); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + + // Draw the resolved texture to the backbuffer + gl.bindTexture(gl.TEXTURE_2D, resolveTex); + gl.useProgram(fsProgram); + gl.bindVertexArray(fsVAO); + wtu.drawUnitQuad(gl); + + // 5) The incorrect render can occur on the second rendered frame, called from + // requestAnimationFrame. + frameCount++; + if (frameCount == 2) { + checkRenderingResults("multisampling-depth-resolve"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at the end of the test."); + finishTest(); + } else { + requestAnimationFrame(runTest); + } +} + +requestAnimationFrame(runTest); + +function checkRenderingResults(prefix) { + // Outer color should be red + wtu.checkCanvasRect(gl, + 1, 1, + 2, 2, + [255, 0, 0, 255], + prefix + ": outer pixels should be red"); + + // Outer quad should be rendered yellow. + wtu.checkCanvasRect(gl, + 10, 10, + 2, 2, + [255, 255, 0, 255], + prefix + ": outer quad should be yellow"); + + // Center quad should be rendered blue. + wtu.checkCanvasRect(gl, + halfSize / 2 + 1, halfSize / 2 + 1, + 2, 2, + [0, 0, 255, 255], + prefix + ": center quad should be blue"); +} + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-fragment-evaluation.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-fragment-evaluation.html new file mode 100644 index 0000000000..df7bce9b09 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/multisampling-fragment-evaluation.html @@ -0,0 +1,143 @@ +<!-- +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 multisampling fragment shader evaluation</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> + +<script id="vshader" type="x-shader/x-vertex">#version 300 es +layout(location=0) in vec4 aPosition; +out vec4 vPosition; +void main() +{ + gl_Position = vec4(aPosition); + vPosition = aPosition; +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision highp float; +in vec4 vPosition; +layout(location=0) out vec4 oColor; +void main() +{ + if (vPosition.x < 0.0) { + oColor = vec4(1, 0, 0, 1); + } else if (vPosition.y < 0.0) { + oColor = vec4(0, 1, 0, 1); + } else { + oColor = vec4(0, 0, 1, 1); + } +} +</script> + +</head> +<body> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("Verify that fragment shader is evaluated only once per framebuffer pixel when multisampling is used."); + +// GLES 3.0.5 section 3.6.3. Polygon Multisample Rasterization: +// "Polygon rasterization produces a fragment for each framebuffer pixel with one or more sample points that satisfy +// the point sampling criteria described in section 3.6.1." + +debug("Regression test for <a href='http://crbug.com/682815'>http://crbug.com/682815</a>"); + +function runTest(testParams) { + let canvas = document.createElement('canvas'); + canvas.width = 1; + canvas.height = 1; + let gl = wtu.create3DContext(canvas, {antialias: false}, 2); + + // Find the supported samples for a multisampled renderbuffer of the appropriate internal format. + let samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl[testParams.internalformat], gl.SAMPLES); + if (!samples || !samples.length) { + testFailed("Could not query supported sample counts for required multisampling format " + testParams.internalformat); + return; + } + + // Note that supported sample counts are required to be reported in descending order. + debug('Testing with sample count ' + samples[0]); + // Create a framebuffer with a multisampled renderbuffer with the maximum supported number of samples. + let rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl[testParams.internalformat], 1, 1); + let fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Rendering to a multisampled renderbuffer of format " + testParams.internalformat + " is required."); + return; + } + + // Create a program that will choose between one of different possible colors in the fragment shader. + // It should be evaluated only once per framebuffer pixel, so only one of the colors will end up in the framebuffer. + // However, if the multisampling mode is incorrectly implemented by supersampling, the samples may have different + // colors. + let program = wtu.setupProgram(gl, ["vshader", "fshader"], ["aPosition"]); + + // Render one triangle using the program. The triangle needs to extend far outside the viewport on all sides, so + // that we can safely assume all samples fall inside the triangle. GLES 3.0.5: + // "The sample points associated with a pixel may be located inside or outside of the unit square that is considered to bound the pixel." + // Here we assume that sample points are less than 9999 pixels away from the pixel they are associated with. + let buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 10000, 30000, + -30000, -10000, + 10000, -10000]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + gl.drawArrays(gl.TRIANGLES, 0, 3); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); + gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); + + let readBuffer = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer); + + // Check that the canvas is one of the colors that the fragment shader may generate, and not a blend of them. + let possibleColors = [ + [255, 0, 0, 255], + [0, 255, 0, 255], + [0, 0, 255, 255] + ]; + let anyColorMatched = false; + for (let i = 0; i < possibleColors.length; ++i) { + let colorMatched = true; + for (let j = 0; j < 4; ++j) { + if (Math.abs(readBuffer[j] - possibleColors[i][j]) > 2) { + colorMatched = false; + } + } + if (colorMatched) { + anyColorMatched = true; + } + } + if (!anyColorMatched) { + testFailed("Color in framebuffer was not one of the colors generated by the fragment shader: " + readBuffer); + } else { + testPassed("Color in framebuffer was one of the colors generated by the fragment shader: " + readBuffer); + } +} + +runTest({internalformat: 'RGBA8'}); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/out-of-bounds-index-buffers-after-copying.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/out-of-bounds-index-buffers-after-copying.html new file mode 100644 index 0000000000..39bbb5e348 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/out-of-bounds-index-buffers-after-copying.html @@ -0,0 +1,187 @@ +<!-- +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> +<title>WebGL Out-of-Bounds Index Buffer Caused by CopyBufferSubData Conformance Test</title> +</head> +<body> +<canvas id="canvas" width="8" height="8" style="width: 100px; height: 100px;"></canvas> +<div id="description"></div> +<div id="console"></div> +<script id="vsCheckOutOfBounds" type="x-shader/x-vertex"> + #define TEST_CASE_IN_BOUND 1 + #define TEST_CASE_OUT_OF_BOUND 2 + + precision mediump float; + attribute vec2 position; + attribute vec4 vecRandom; + varying vec4 v_color; + uniform int u_testCase; + + bool testFloatComponentAccurate(float component) { + return component == 0.2; + } + // Per the spec, each component can either contain existing contents + // of the buffer or 0. + bool testFloatComponent(float component) { + return (component == 0.2 || component == 0.0); + } + // The last component is additionally allowed to be 1.0. + bool testLastFloatComponent(float component) { + return testFloatComponent(component) || component == 1.0; + } + + bool testData(vec4 data) { + if (u_testCase == TEST_CASE_IN_BOUND) { + return (testFloatComponentAccurate(data.x) && + testFloatComponentAccurate(data.y) && + testFloatComponentAccurate(data.z) && + testFloatComponentAccurate(data.w)); + } else if (u_testCase == TEST_CASE_OUT_OF_BOUND) { + return (testFloatComponent(data.x) && + testFloatComponent(data.y) && + testFloatComponent(data.z) && + testLastFloatComponent(data.w)); + } + return false; + } + + void main() { + if (testData(vecRandom)) { + v_color = vec4(0.0, 1.0, 0.0, 1.0); // green -- We're good + } else { + v_color = vec4(1.0, 0.0, 0.0, 1.0); // red -- Unexpected value + } + gl_Position = vec4(position, 0.0, 1.0); + } +</script> +<script> +"use strict"; +description("This test verifies that out-of-bounds index buffers caused by CopyBufferSubData behave according to spec."); + +// Ensure that drawElements flags either no error or INVALID_OPERATION. In the case of INVALID_OPERATION, +// no canvas pixels can be touched. In the case of NO_ERROR, all written values must either be the +// zero vertex or a value in the vertex buffer. See vsCheckOutOfBounds shader. +function verifyOutOfBoundsIndex(gl) { + var error = gl.getError(); + if (error === gl.INVALID_OPERATION) { + testPassed("drawElements flagged INVALID_OPERATION, which is valid so long as all canvas pixels were not touched."); + wtu.checkCanvas(gl, [0, 0, 255, 255]); + } else if (error === gl.NO_ERROR) { + testPassed("drawElements flagged NO_ERROR, which is valid so long as all canvas pixels are green."); + wtu.checkCanvas(gl, [0, 255, 0, 255]); + } else { + testFailed("Invalid error flagged by drawElements. Should be INVALID_OPERATION or NO_ERROR"); + } +} + +// Create an element array buffer with a tri-strip that starts at startIndex and make +// it the active element array buffer. +function prepareElementArrayBuffer(gl, startIndex) { + var glElementArrayBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glElementArrayBuffer); + var quadIndices = new Uint16Array(4); + for (var i = 0; i < quadIndices.length; i++) { + quadIndices[i] = startIndex + i; + } + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, quadIndices, gl.STATIC_DRAW); + return glElementArrayBuffer; +} + + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}, 2); + +var numberOfQuads = 200; + +// Create a vertex buffer with 200 properly formed tri-strip quads. These quads will cover the canvas texture +// such that every single pixel is touched by the fragment shader. +var quadBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer); +var quadPositions = new Float32Array(numberOfQuads * /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4); +for (var i = 0; i < quadPositions.length; i += /*ComponentsPerQuad*/2 * /*VerticesPerQuad*/4) { + quadPositions[i+0] = -1.0; // upper left + quadPositions[i+1] = 1.0; + quadPositions[i+2] = 1.0; // upper right + quadPositions[i+3] = 1.0; + quadPositions[i+4] = -1.0; // lower left + quadPositions[i+5] = -1.0; + quadPositions[i+6] = 1.0; // lower right + quadPositions[i+7] = -1.0; +} +gl.bufferData(gl.ARRAY_BUFFER, quadPositions, gl.STATIC_DRAW); +gl.enableVertexAttribArray(0); +gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + +// Create a small vertex buffer with determined-ahead-of-time "random" values (0.2). This buffer will be +// the one indexed off the end. +var vertexBuffer = gl.createBuffer(); +gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); +gl.bufferData(gl.ARRAY_BUFFER, + new Float32Array([0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2, + 0.2, 0.2, 0.2, 0.2]), + gl.STATIC_DRAW); +gl.enableVertexAttribArray(1); +gl.vertexAttribPointer(1, 4, gl.FLOAT, false, 0, 0); + +// Setup the verification program. +var program = wtu.setupProgram(gl, ["vsCheckOutOfBounds", wtu.simpleVertexColorFragmentShader], ["position", "vecRandom"]); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Shader and buffer setup should generate no errors"); +var loc = gl.getUniformLocation(program, "u_testCase"); +shouldBeNonNull(loc); + +debug(""); +debug("Test -- Vertex indices are in bounds."); +gl.uniform1i(loc, 1); // TEST_CASE_IN_BOUND == 1 +gl.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched. +gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +var elementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/0); +gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Draw call should generate no errors"); +wtu.checkCanvas(gl, [0, 255, 0, 255]); + +debug(""); +debug("Test -- Index off the end of the vertex buffer near the beginning of the out of bounds area."); +gl.uniform1i(loc, 2); // TEST_CASE_OUT_OF_BOUND == 2 +gl.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched. +gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +var outOfBoundsElementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/4); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, outOfBoundsElementArrayBuffer); +gl.bindBuffer(gl.COPY_WRITE_BUFFER, elementArrayBuffer); +gl.copyBufferSubData(gl.ELEMENT_ARRAY_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 4 * Uint16Array.BYTES_PER_ELEMENT); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should generate no errors"); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementArrayBuffer); +gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0); +verifyOutOfBoundsIndex(gl); + +debug(""); +debug("Test -- Index off the end of the vertex buffer near the end of the out of bounds area.") +gl.uniform1i(loc, 2); // TEST_CASE_OUT_OF_BOUND == 2 +gl.clearColor(0.0, 0.0, 1.0, 1.0); // Start with blue to indicate no pixels touched. +gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); +outOfBoundsElementArrayBuffer = prepareElementArrayBuffer(gl, /*StartIndex*/numberOfQuads - 4); +gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementArrayBuffer); +gl.bindBuffer(gl.COPY_READ_BUFFER, outOfBoundsElementArrayBuffer); +gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.ELEMENT_ARRAY_BUFFER, 0, 0, 4 * Uint16Array.BYTES_PER_ELEMENT); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "copyBufferSubData should generate no errors"); +gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_SHORT, /*offset*/0); +verifyOutOfBoundsIndex(gl); + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Running tests should generate no errors"); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rasterizer-discard-and-implicit-clear.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rasterizer-discard-and-implicit-clear.html new file mode 100644 index 0000000000..f605a25f26 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rasterizer-discard-and-implicit-clear.html @@ -0,0 +1,149 @@ +<!-- +Copyright (c) 2020 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>RASTERIZER_DISCARD doesn't affect implicit clears</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> +<script id="vshader" type="x-shader/x-vertex">#version 300 es +layout(location=0) in vec2 vPosition; +uniform float xTranslation; +void main(void) { + gl_Position = vec4(vPosition[0] + xTranslation, vPosition[1], 0.0, 1.0); +} +</script> +<script id="fshader" type="x-shader/x-fragment">#version 300 es +precision mediump float; +uniform vec4 color; +out vec4 outColor; +void main() { + outColor = color; +} +</script> +</head> +<body> +<canvas id="example"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +debug(""); + +description("Enabling RASTERIZER_DISCARD should not affect implicit clears"); + +const wtu = WebGLTestUtils; +const canvas = document.getElementById("example"); +const sz = canvas.width = canvas.height = 256; +const gl = wtu.create3DContext(canvas, undefined, 2); +const NUM_FRAMES = 15; +let framesToGo = NUM_FRAMES; +let xTranslationLoc; +let colorLoc; +const positionLocation = 0; +const red = [ 1.0, 0.0, 0.0, 1.0 ]; +const green = [ 0.0, 1.0, 0.0, 1.0 ]; +const transparentBlackRender = [ 0, 0, 0, 0 ]; +const greenRender = [ 0, 255, 0, 255 ]; + +if (!gl) { + testFailed("WebGL context creation failed"); + finishTest(); +} else { + testPassed("WebGL context creation succeeded"); + runDrawTest(); +} + +function runDrawTest() { + debug("Verify that draws with rasterizer discard enabled do not interfere with implicit clears"); + let prog = wtu.loadProgramFromScript(gl, "vshader", "fshader"); + gl.useProgram(prog); + xTranslationLoc = gl.getUniformLocation(prog, "xTranslation"); + colorLoc = gl.getUniformLocation(prog, "color"); + let leftRectBuffer = gl.createBuffer(); + gl.enableVertexAttribArray(positionLocation); + gl.bindBuffer(gl.ARRAY_BUFFER, leftRectBuffer); + // Create a rectangle covering the left half of the viewport, in + // normalized device coordinates. + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 1.0, + -1.0, 1.0, + -1.0, -1.0, + 0.0, 1.0, + -1.0, -1.0, + 0.0, -1.0]), gl.STATIC_DRAW); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + requestAnimationFrame(renderDrawTestFrame); +} + +function renderDrawTestFrame() { + // Animation is required in order to expose this bug. When it + // occurs, the rectangle leaves trails behind it. + gl.uniform1f(xTranslationLoc, 0.0); + gl.enable(gl.RASTERIZER_DISCARD); + gl.uniform4fv(colorLoc, red); + gl.drawArrays(gl.TRIANGLES, 0, 6); + gl.disable(gl.RASTERIZER_DISCARD); + + // Animate the rectangle from the left to the right half of the viewport. + gl.uniform1f(xTranslationLoc, (NUM_FRAMES - framesToGo) / NUM_FRAMES); + // Draw the last frame with green so any (incorrect) trails are visibly red. + if (framesToGo == 0) + gl.uniform4fv(colorLoc, green); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + if (framesToGo-- == 0) { + // The left half of the canvas should be transparent black, + // which comes from the implicit clear just before drawing the + // rectangle without rasterizer discard enabled. + wtu.checkCanvasRect(gl, 0, 0, sz / 2, sz, transparentBlackRender, "left half of canvas should be clear", 3); + // The right half of the canvas should be solid green, from + // the last render of the translated rectangle. + wtu.checkCanvasRect(gl, sz / 2, 0, sz / 2, sz, greenRender, "right half of canvas should be green", 3); + runReadPixelsTest(); + } else { + requestAnimationFrame(renderDrawTestFrame); + } +} + +function runReadPixelsTest() { + debug("Verify that readPixels with rasterizer discard enabled receives implicitly cleared data"); + framesToGo = NUM_FRAMES; // Reset state. + // Clear to transparent black. + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clear(gl.COLOR_BUFFER_BIT); + // Start with rasterizer discard enabled. + gl.enable(gl.RASTERIZER_DISCARD); + requestAnimationFrame(renderReadPixelsTestFrame); +} + +function renderReadPixelsTestFrame() { + // Rasterizer discard is enabled at the beginning of this test. + + // The canvas should always contain transparent black at the beginning of the frame. + wtu.checkCanvasRect(gl, 0, 0, sz, sz, transparentBlackRender, undefined, 3); + + gl.disable(gl.RASTERIZER_DISCARD); + // Clear to red. + gl.clearColor(1.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + // Enable rasterizer discard again. + gl.enable(gl.RASTERIZER_DISCARD); + + if (--framesToGo == 0) { + finishTest(); + } else { + requestAnimationFrame(renderReadPixelsTestFrame); + } +} + +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/read-draw-when-missing-image.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/read-draw-when-missing-image.html new file mode 100644 index 0000000000..a9d8c74b9d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/read-draw-when-missing-image.html @@ -0,0 +1,288 @@ +<!-- +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>Read or Draw when Attachment(s) Miss Image</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="8" height="8"></canvas> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +description("This test verifies the functionality of reading/drawing when color attachment(s) miss image."); + +var gl = wtu.create3DContext("example", undefined, 2); + +var tex_read = gl.createTexture(); +var tex_draw = gl.createTexture(); +var tex_depth = gl.createTexture(); +var tex_stencil = gl.createTexture(); +var fbo_read = gl.createFramebuffer(); +var fbo_draw = gl.createFramebuffer(); +var size = 8; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // READ_FRAMEBUFFER has image at COLOR_ATTACHMENT0. READ_FRAMEBUFFER is framebuffer complete. + // Read from COLOR_ATTACHMENT1, which has no image attached. + init_read_fbo(); + read(); + + // DRAW_FRAMEBUFFER has image at COLOR_ATTACHMENT0. DRAW_FRAMEBUFFER is framebuffer complete. + // Clear and draw COLOR_ATTACHMENT1 or COLOR_ATTACHMENT0 + COLOR_ATTACHMENT1 attaching point(s). + init_draw_fbo(); + clear(); + draw(); + + blit(); +} + +function init_read_fbo() { + gl.bindTexture(gl.TEXTURE_2D, tex_read); + wtu.fillTexture(gl, tex_read, size, size, [0x0, 0xff, 0xff, 0xff], 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.RGBA8); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_read, 0); + if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } else { + testPassed("framebuffer complete!"); + } +} + +function init_draw_fbo() { + gl.bindTexture(gl.TEXTURE_2D, tex_draw); + wtu.fillTexture(gl, tex_draw, size, size, [0x0, 0xff, 0xff, 0xff], 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.RGBA8); + wtu.fillTexture(gl, tex_depth, size, size, [0x80], 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, gl.DEPTH_COMPONENT16); + wtu.fillTexture(gl, tex_stencil, size, size, [0x40], 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, gl.DEPTH24_STENCIL8); + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_draw, 0); + if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("Framebuffer incomplete."); + return; + } else { + testPassed("framebuffer complete!"); + } +} + +function read() { + debug(""); + debug("read from a color buffer which has no image attached"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + + var data = new Uint8Array(size * size * 4); + gl.readPixels(0, 0, size, size, gl.RGBA, gl.UNSIGNED_BYTE, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when reading from a color buffer without image."); + + var copy_2d = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, copy_2d); + gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, 0, 0, size, size, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when reading from a color buffer without image."); + + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, size / 2, size / 2, 0, 0, size / 2, size / 2); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when reading from a color buffer without image."); + + var copy_3d = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_3D, copy_3d); + gl.texImage3D(gl.TEXTURE_3D, 0, gl.RGBA8, size, size, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.copyTexSubImage3D(gl.TEXTURE_3D, 0, size / 2, size / 2, 0, 0, 0, size / 2, size / 2); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when reading from a color buffer without image."); + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.deleteTexture(copy_2d); + gl.bindTexture(gl.TEXTURE_3D, null); + gl.deleteTexture(copy_3d); +} + +function checkTextureValue(fbo, buffer, value) { + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo); + gl.readBuffer(buffer); + wtu.checkCanvas(gl, value); + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); +} + +function clear() { + debug(""); + debug("clear a color buffer which has no image attached"); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + var color = [0.0, 1.0, 0.0, 1.0]; + gl.clearColor(color[0], color[1], color[2], color[3]); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 255, 255]); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 0, 255]); + + var data = new Float32Array(color); + gl.clearBufferfv(gl.COLOR, 1, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); +} + +function draw() { + debug(""); + debug("draw to a color buffer which has no image attached"); + + var program = wtu.setupSimpleColorProgram(gl, 0); + gl.uniform4f(gl.getUniformLocation(program, "u_color"), 1, 0, 0, 1); + wtu.setupUnitQuad(gl, 0); + + // Call to drawArrays + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 0, 255]); + + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [255, 0, 0, 255]); + + // Call to drawElements + gl.uniform4f(gl.getUniformLocation(program, "u_color"), 1, 1, 0, 1); + wtu.setupIndexedQuad(gl, 1); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + wtu.drawIndexedQuad(gl, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [255, 0, 0, 255]); + + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + wtu.drawIndexedQuad(gl, 1); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 should be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [255, 255, 0, 255]); +} + +function blit() { + debug(""); + debug("blit color buffer(s) which have no image attached"); + // Some or all draw buffers have no image. Read buffer have image. It should be OK. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 in draw fbo should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [255, 255, 0, 255]); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT0); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 in draw fbo should be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 255, 255]); + + // Draw buffer(s) have no image. Read buffer have no image. It should be OK. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + gl.drawBuffers([gl.NONE, gl.COLOR_ATTACHMENT1]); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // The image color at COLOR_ATTACHMENT0 in draw fbo should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 255, 255]); + + // Read buffer have no image. Some or all draw buffers have image. It should generate INVALID_OPERATION. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + gl.drawBuffers([gl.COLOR_ATTACHMENT0]); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when read buffer misses image."); + // The image color at COLOR_ATTACHMENT0 in draw fbo should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 255, 255]); + + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.readBuffer(gl.COLOR_ATTACHMENT1); + gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.COLOR_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when read buffer misses image."); + // The image color at COLOR_ATTACHMENT0 in draw fbo should not be changed. + checkTextureValue(fbo_draw, gl.COLOR_ATTACHMENT0, [0, 255, 255, 255]); + + // Depth buffer in read fbo has no image. It should generate INVALID_OPERATION if depth buffer in draw fbo has image. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, tex_depth, 0); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when depth buffer misses image."); + + // Depth buffer in read fbo has no image. It should be OK if depth buffer in draw fbo has no image too. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, null, 0); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.DEPTH_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // Validate some other parameters as usual + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.DEPTH_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid filter"); + + // Stencil buffer in read fbo has no image. It should generate INVALID_OPERATION if stencil buffer in draw fbo has image. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, tex_stencil, 0); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.STENCIL_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should generate INVALID_OPERATION when stencil buffer misses image."); + + // Stencil buffer in read fbo has no image. It should be OK if stencil buffer in draw fbo has no image too. + gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo_draw); + gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo_read); + gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, null, 0); + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.STENCIL_BUFFER_BIT, gl.NEAREST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no error."); + // Validate some other parameters as usual + gl.blitFramebuffer(0, 0, size, size, 0, 0, size, size, gl.STENCIL_BUFFER_BIT, gl.LINEAR); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid filter"); +} + +gl.bindTexture(gl.TEXTURE_2D, null); +gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); +gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); +gl.deleteTexture(tex_read); +gl.deleteTexture(tex_draw); +gl.deleteTexture(tex_depth); +gl.deleteTexture(tex_stencil); +gl.deleteFramebuffer(fbo_read); +gl.deleteFramebuffer(fbo_draw); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rgb-format-support.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rgb-format-support.html new file mode 100644 index 0000000000..46712ae68b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/rgb-format-support.html @@ -0,0 +1,111 @@ +<!-- +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> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +var wtu = WebGLTestUtils; +description("Verify RGB/RGB8 textures and renderbuffers support"); + +var gl = wtu.create3DContext(undefined, undefined, 2); + +function testRenderbuffer(width, height) { + var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGB8, gl.SAMPLES); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from getInternalformatParameter()"); + + if (!samples || samples.length == 0) { + testFailed("getInternalformatParameter on RGB8 fails to return valid samples"); + return; + } + + for (var idx = 0; idx < samples.length + 2; ++idx) { + debug(""); + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + var sampleCount = 0; + switch (idx) { + case samples.length: + sampleCount = 0; + break; + case samples.length + 1: + sampleCount = -1; // non multisampled + break; + default: + sampleCount = samples[idx]; + } + + if (sampleCount < 0) { + debug("test non-multisampled RGB8 renderbuffer"); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB8, 2, 2); + } else { + debug("test RGB8 renderbuffer with " + sampleCount + " samples"); + gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGB8, width, height); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from renderbufferStorage{Multisample}"); + + 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 renderbuffer is incomplete"); + } else { + testPassed("framebuffer with renderbuffer is complete"); + } + + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear()"); + } +} + +function testTexture(width, height) { + var formats = [ "RGB", "RGB8" ]; + for (var idx = 0; idx < formats.length; ++idx) { + debug(""); + debug("test texture format " + formats[idx]); + var internalformat = gl[formats[idx]]; + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, internalformat, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texture setup"); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("framebuffer with texture is incomplete"); + } else { + testPassed("framebuffer with texture is complete"); + } + + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BIT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from clear()"); + } +} + +if (!gl) { + testFailed('canvas.getContext() failed'); +} else { + testRenderbuffer(2, 2); + testTexture(2, 2); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/texture-switch-performance.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/texture-switch-performance.html new file mode 100644 index 0000000000..da7a9e2f57 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/texture-switch-performance.html @@ -0,0 +1,101 @@ +<!-- +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 Texture Switch Conformance Tests</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("Ensures that switching the texture referenced by a sampler uniform performs reasonably well."); +var wtu = WebGLTestUtils; +var canvas = document.createElement('canvas'); +canvas.width = 32; +canvas.height = 32; +var gl = wtu.create3DContext(canvas, undefined, 2); +if (!gl) { + testFailed("context does not exist"); + finishTest(); +} else { + var program = wtu.setupTexturedQuad(gl); + var tex = gl.createTexture(); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + var tex2 = gl.createTexture(); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + var loc = gl.getUniformLocation(program, "tex"); + + var RUNTIME = 2000; + var THRESHOLD = 0.8; + var baseStartTime = 0; + var baseFrameCount = 0; + var testStartTime = 0; + var testFrameCount = 0; + + baseStartTime = Date.now(); + function drawBaseline() { + for (var i = 0; i < 400; ++i) { + gl.uniform1i(loc, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + gl.uniform1i(loc, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + + ++baseFrameCount; + + if (Date.now() - baseStartTime > RUNTIME) { + testStartTime = Date.now(); + requestAnimationFrame(drawTest); + } else { + requestAnimationFrame(drawBaseline); + } + } + + function drawTest() { + for (var i = 0; i < 400; ++i) { + gl.uniform1i(loc, 0); + gl.drawArrays(gl.TRIANGLES, 0, 6); + gl.uniform1i(loc, 1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + + ++testFrameCount; + + if (Date.now() - testStartTime > RUNTIME) { + var perfString = " - achieved " + testFrameCount + " frames in " + ((Date.now() - testStartTime) / 1000.0) + + " seconds (" + (testFrameCount / baseFrameCount).toFixed(2) + " times baseline performance)"; + if (testFrameCount > baseFrameCount * THRESHOLD) { + testPassed("Texture switching did not significantly hurt performance" + perfString); + } else { + testFailed("Texture switching significantly hurt performance" + perfString); + } + console.log(testFrameCount); + finishTest(); + } else { + requestAnimationFrame(drawTest); + } + } + + requestAnimationFrame(drawBaseline); +} +var successfullyParsed = true; +</script> +</body> +</html> + diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/uniform-block-buffer-size.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/uniform-block-buffer-size.html new file mode 100644 index 0000000000..35cc5205d4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/uniform-block-buffer-size.html @@ -0,0 +1,228 @@ +<!-- +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 UniformBlock Buffer Size Conformance Tests</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> +<script id='vshader' type='x-shader/x-vertex'>#version 300 es +layout(location=0) in vec3 p; +void main() +{ + gl_Position = vec4(p.xyz, 1.0); +} +</script> +<script id='fshader' type='x-shader/x-fragment'>#version 300 es +precision mediump float; +layout(location=0) out vec4 oColor; + +uniform UBOData { + float UBORed; + float UBOGreen; + float UBOBlue; +}; + +void main() +{ + oColor = vec4(UBORed, UBOGreen, UBOBlue, 1.0); +} +</script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies an active UniformBlock should be populated with a large enough buffer object"); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, null, 2); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runDrawArraysTests(); + runDrawElementsTests(); +} + +function runDrawArraysTests() { + debug(""); + debug("Testing drawArrays and drawArraysInstanced"); + + var instanceCount = 4; + + var program = wtu.setupProgram(gl, ['vshader', 'fshader']); + if (!program) { + testFailed("Could not compile shader with uniform blocks without error"); + return; + } + + var blockIndex = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices = gl.getUniformIndices(program, ["UBORed", "UBOGreen", "UBOBlue"]); + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + var uboArray = new ArrayBuffer(blockSize); + var uboFloatView = new Float32Array(uboArray); + uboFloatView[uniformOffsets[0] / Float32Array.BYTES_PER_ELEMENT] = 1.0; // UBORed + uboFloatView[uniformOffsets[1] / Float32Array.BYTES_PER_ELEMENT] = 0.0; // UBOGreen + uboFloatView[uniformOffsets[2] / Float32Array.BYTES_PER_ELEMENT] = 0.0; // UBOBlue + + var binding = 1; + gl.uniformBlockBinding(program, blockIndex, binding); + + wtu.setupUnitQuad(gl); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Set up succeeded"); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: UniformBlock is not backed by a buffer"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArraysInstanced: UniformBlock is not backed by a buffer"); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, buffer); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: UniformBlock is backed by a buffer with no data store"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArraysInstanced: UniformBlock is backed by a buffer with no data store"); + + var arrayNotLargeEnough = new ArrayBuffer(blockSize - 1); + gl.bufferData(gl.UNIFORM_BUFFER, arrayNotLargeEnough, gl.DYNAMIC_DRAW); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: UniformBlock not populated with a large enough buffer"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArraysInstanced: UniformBlock not populated with a large enough buffer"); + + gl.bufferData(gl.UNIFORM_BUFFER, uboFloatView, gl.DYNAMIC_DRAW); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArrays: should be able to draw with sufficient data for UniformBlock"); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstanced: should be able to draw with sufficient data for UniformBlock"); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + + gl.bindBufferRange(gl.UNIFORM_BUFFER, binding, buffer, 0, blockSize -1); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArrays: bindBufferRange set size too small for UniformBlock"); + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawArraysInstanced: bindBufferRange set size too small for UniformBlock"); +} + +function runDrawElementsTests() { + debug(""); + debug("Testing drawElements, drawRangeElements and drawElementsInstanced"); + + var instanceCount = 4; + + var program = wtu.setupProgram(gl, ['vshader', 'fshader']); + if (!program) { + testFailed("Could not compile shader with uniform blocks without error"); + return; + } + + var blockIndex = gl.getUniformBlockIndex(program, "UBOData"); + var blockSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); + var uniformIndices = gl.getUniformIndices(program, ["UBORed", "UBOGreen", "UBOBlue"]); + var uniformOffsets = gl.getActiveUniforms(program, uniformIndices, gl.UNIFORM_OFFSET); + + if (uniformOffsets.length < 3) { + testFailed("Could not query uniform offsets"); + return; + } + + var uboArray = new ArrayBuffer(blockSize); + var uboFloatView = new Float32Array(uboArray); + uboFloatView[uniformOffsets[0] / Float32Array.BYTES_PER_ELEMENT] = 1.0; // UBORed + uboFloatView[uniformOffsets[1] / Float32Array.BYTES_PER_ELEMENT] = 0.0; // UBOGreen + uboFloatView[uniformOffsets[2] / Float32Array.BYTES_PER_ELEMENT] = 0.0; // UBOBlue + + var binding = 1; + gl.uniformBlockBinding(program, blockIndex, binding); + + wtu.setupIndexedQuad(gl, 1, 0); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Set up succeeded"); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.UNIFORM_BUFFER, buffer); + + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: UniformBlock is not backed by a buffer"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawRangeElements: UniformBlock is not backed by a buffer"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElementsInstanced: UniformBlock is not backed a buffer"); + + gl.bindBufferBase(gl.UNIFORM_BUFFER, binding, buffer); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: UniformBlock is populated with a buffer with no data store"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawRangeElements: UniformBlock is populated with a buffer with no data store"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElementsInstanced: UniformBlock is populated with a buffer with no data store"); + + var arrayNotLargeEnough = new ArrayBuffer(blockSize - 1); + gl.bufferData(gl.UNIFORM_BUFFER, arrayNotLargeEnough, gl.DYNAMIC_DRAW); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: UniformBlock not populated with a large enough buffer"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawRangeElements: UniformBlock not populated with a large enough buffer"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElementsInstanced: UniformBlock not populated with a large enough buffer"); + + gl.bufferData(gl.UNIFORM_BUFFER, uboFloatView, gl.DYNAMIC_DRAW); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElements: should be able to draw with sufficient data for UniformBlock"); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawRangeElements: should be able to draw with sufficient data for UniformBlock"); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced: should be able to draw with sufficient data for UniformBlock"); + wtu.checkCanvas(gl, [255, 0, 0, 255], "draw call should set canvas to red", 2); + + gl.bindBufferRange(gl.UNIFORM_BUFFER, binding, buffer, 0, blockSize -1); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElements: bindBufferRange set size too small for UniformBlock"); + gl.drawRangeElements(gl.TRIANGLES, 0, 5, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawRangeElements: bindBufferRange set size too small for UniformBlock"); + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "drawElementsInstanced: bindBufferRange set size too small for UniformBlock"); +} + +debug(""); +wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/vertex-id-large-count.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/vertex-id-large-count.html new file mode 100644 index 0000000000..a68f5d911c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/vertex-id-large-count.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL 2 gl_VertexID Large Count Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<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> +<!-- Shaders for testing instanced draws --> +<script id="vs" type="text/plain"> +#version 300 es + +uniform ivec2 resolution; +void main() { + ivec2 pixel = ivec2( + gl_VertexID % resolution.x, + gl_VertexID / resolution.x); + vec2 xy = (vec2(pixel) + 0.5) / vec2(resolution) * 2.0 - 1.0; + + gl_Position = vec4(xy, 0, 1); + gl_PointSize = 1.0; +} +</script> +<script id="fs" type="text/plain"> +#version 300 es +precision mediump float; +uniform vec4 color; +out vec4 fragColor; +void main() { + fragColor = color; +} +</script> + +<script> +"use strict"; +description("Test gl_VertexID"); + +debug(""); + +const wtu = WebGLTestUtils; +const canvas = document.createElement("canvas"); +const size = 2048; +canvas.width = size; +canvas.height = size; +const gl = wtu.create3DContext(canvas, null, 2); + +// document.body.appendChild(gl.canvas); +// gl.canvas.style.background = 'yellow'; +// gl.canvas.style.margin = '20px'; +// const ext = gl.getExtension('WEBGL_debug_renderer_info'); +// if (ext) { +// debug(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)); +// } + +(function() { + if (!gl) { + testFailed("WebGL context does not exist"); + return; + } + testPassed("WebGL context exists"); + + const vs = document.getElementById("vs").innerHTML.trim(); + const fs = document.getElementById("fs").innerHTML.trim(); + const prog = wtu.loadProgram(gl, vs, fs); + gl.useProgram(prog); + const resolutionLoc = gl.getUniformLocation(prog, 'resolution'); + const colorLoc = gl.getUniformLocation(prog, 'color'); + + // - + + debug(""); + debug("----------------"); + debug("drawArrays"); + + const u8Color = c => c.map(v => v * 255 | 0); + const transparentBlack = [0, 0, 0, 0]; + const red = [1, 0, 0, 1]; + const green = [0, 1, 0, 1]; + const blue = [0, 0, 1, 1]; + + const test = function(first, count, color) { + debug(""); + debug(`drawArrays(first: ${first}, count: ${count})`); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.uniform4fv(colorLoc, color); + gl.uniform2i(resolutionLoc, size, size); + gl.drawArrays(gl.POINTS, first, count); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + const y = first / size; + const height = count / size; + + // The shader draws 1 pixel points by looking at gl_VertexID + // as a 1D index into a 2D array. In other words + // row = gl_VertexID / width; + // col = gl_VertexID % width; + // Setting first to a value of rows * width (ie, y * size) + // lets us skip y rows when drawing so we then check that + // from y to height rows are the expected color and everything + // else is the cleared color. + wtu.checkCanvasRect(gl, 0, y, size, height, u8Color(color)); + wtu.checkCanvasRect(gl, 0, 0, size, size - height, transparentBlack); + }; + + test(0, size * size, red); + test(size * size / 2, size * size / 2, green); + test(size * size / 4, size * size / 4 * 3, blue); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "There should be no remaining errors"); +})(); + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> + +<!-- +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/conformance2/rendering/vertex-id.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/vertex-id.html new file mode 100644 index 0000000000..ec4bd05ad4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/vertex-id.html @@ -0,0 +1,219 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL 2 gl_VertexID Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/desktop-gl-constants.js"></script> +<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> +<!-- Shaders for testing instanced draws --> +<script id="vs" type="text/plain"> +#version 300 es +flat out highp int vVertexID; + +void main() { + vVertexID = gl_VertexID; + gl_PointSize = 1.0; + gl_Position = vec4(0,0,0,1); +} +</script> +<script id="fs" type="text/plain"> +#version 300 es +flat in highp int vVertexID; +out highp int oVertexID; +void main() { + oVertexID = vVertexID; +} +</script> + +<script> +"use strict"; +description("Test gl_VertexID"); + +debug(""); + +const wtu = WebGLTestUtils; +const canvas = document.createElement("canvas"); +canvas.width = 1; +canvas.height = 1; +const gl = wtu.create3DContext(canvas, null, 2); + +(function() { + if (!gl) { + testFailed("WebGL context does not exist"); + return; + } + testPassed("WebGL context exists"); + + const vs = document.getElementById("vs").innerHTML.trim(); + const fs = document.getElementById("fs").innerHTML.trim(); + const prog = wtu.loadProgram(gl, vs, fs); + gl.useProgram(prog); + + const tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R32I, 1, 1); + 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"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No errors after setup"); + + function shouldBeVal(prefix, expected, was) { + let text = prefix + "Should be " + expected; + let func = testPassed; + if (was != expected) { + text = text + ", was " + was; + func = testFailed; + } + func(text); + }; + + const readValData = new Int32Array(10000); + function ensureVal(prefix, expected) { + gl.readPixels(0, 0, 1, 1, gl.RGBA_INTEGER, gl.INT, readValData); + const was = readValData[0]; + shouldBeVal(prefix, expected, was); + }; + + gl.clearBufferiv(gl.COLOR, 0, new Int32Array([42, 0, 0, 0])); + ensureVal("After clear", 42); + + // - + + debug(""); + debug("----------------"); + debug("drawArrays"); + + let test = function(first, count) { + debug(""); + debug(`drawArrays(first: ${first}, count: ${count})`); + gl.drawArrays(gl.POINTS, first, count); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + ensureVal("", first+count-1); + }; + + test(0, 1); + test(1, 1); + test(10000, 1); + test(100000, 1); + test(1000000, 1); + + test(0, 2); + test(1, 2); + test(10000, 2); + test(100000, 2); + test(1000000, 2); + + const INT32_MAX = 0x7fffffff; + + test = function(first, count) { + debug(""); + debug(`drawArrays(first: ${first}, count: ${count})`); + gl.drawArrays(gl.POINTS, first, count); + if (!wtu.glErrorShouldBe(gl, [gl.NO_ERROR, gl.OUT_OF_MEMORY])) { + ensureVal("", first+count-1); + } + }; + + test(INT32_MAX-2, 1); + test(INT32_MAX-1, 1); + test(INT32_MAX, 1); + + // - + + debug(""); + debug("----------------"); + debug("drawElements"); + + const indexBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); + const indexData = new Uint16Array([1, 2, 5, 3, 10000]); + debug("indexData: " + indexData); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); + + test = function(first, count) { + debug(""); + debug(`drawElements(first: ${first}, count: ${count})`); + gl.drawElements(gl.POINTS, count, gl.UNSIGNED_SHORT, first*2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + ensureVal("", indexData[first+count-1]); + }; + + for (let f = 0; f < indexData.length; ++f) { + for (let c = 1; f + c <= indexData.length; ++c) { + test(f, c); + } + } + + // - + + debug(""); + debug("----------------"); + debug("Via transform feedback"); + + gl.transformFeedbackVaryings(prog, ["vVertexID"], gl.INTERLEAVED_ATTRIBS); + wtu.linkProgram(gl, prog); + gl.useProgram(prog); + + const tfBuffer = gl.createBuffer(); + gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer); + gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4*10000, gl.DYNAMIC_READ); + + test = function(offset, count) { + debug(""); + debug("drawArrays(" + offset + ", " + count + ")"); + gl.beginTransformFeedback(gl.POINTS); + gl.drawArrays(gl.POINTS, offset, count); + gl.endTransformFeedback(); + gl.getBufferSubData(gl.TRANSFORM_FEEDBACK_BUFFER, 0, readValData); + let ok = true; + for (let i = 0; i < readValData.length; i++) { + if (i >= count) + break; + const expected = offset + i; + const was = readValData[i]; + if (was != expected) { + testFailed("[" + i + "] expected " + expected + ", was " + was); + ok = false; + break; + } + } + if (ok) { + testPassed("ok"); + } + }; + + test(0, 1); + test(1, 1); + test(10000, 1); + + test(0, 2); + test(1, 2); + test(10000, 2); + + test(10000, 10000); + + // - + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "There should be no remaining errors"); +})(); + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> + +<!-- +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. +--> |