diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html new file mode 100644 index 0000000000..a33844f6b4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html @@ -0,0 +1,812 @@ +<!-- +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 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="fshader" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +uniform vec4 u_colors[$(numDrawingBuffers)]; +void main() { + for (int i = 0; i < $(numDrawingBuffers); ++i) { + gl_FragData[i] = u_colors[i]; + } +} +</script> +<script id="fshaderNoWrite" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +void main() { +} +</script> +<script id="fshaderRed" type="x-shader/x-fragment"> +precision mediump float; +void main() { + gl_FragColor = vec4(1,0,0,1); +} +</script> +<script id="fshaderRedWithExtension" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() { + gl_FragColor = vec4(1,0,0,1); +} +</script> +<script id="fshaderMacroDisabled" type="x-shader/x-fragment"> +#ifdef GL_EXT_draw_buffers + bad code here +#endif +precision mediump float; +void main() { + gl_FragColor = vec4(0,0,0,0); +} +</script> +<script id="fshaderMacroEnabled" type="x-shader/x-fragment"> +#ifdef GL_EXT_draw_buffers + #if GL_EXT_draw_buffers == 1 + #define CODE + #else + #define CODE this_code_is_bad_it_should_have_compiled + #endif +#else + #define CODE this_code_is_bad_it_should_have_compiled +#endif +CODE +precision mediump float; +void main() { + gl_FragColor = vec4(0,0,0,0); +} +</script> +<script id="fshaderBuiltInConstEnabled" type="x-shader/x-fragment"> +precision mediump float; +void main() { + gl_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 the WEBGL_draw_buffers extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var programWithMaxDrawBuffersEqualOne = null; +var drawBuffersUtils; +let fb; + +var extensionConstants = [ + { name: "MAX_COLOR_ATTACHMENTS_WEBGL", enum: 0x8CDF, expectedFn: function(v) { return v >= 4; }, passMsg: " should be >= 4"}, + { name: "MAX_DRAW_BUFFERS_WEBGL", enum: 0x8824, expectedFn: function(v) { return v > 0; }, passMsg: " should be > 0"}, + + { name: "COLOR_ATTACHMENT0_WEBGL", enum: 0x8CE0, }, + { name: "COLOR_ATTACHMENT1_WEBGL", enum: 0x8CE1, }, + { name: "COLOR_ATTACHMENT2_WEBGL", enum: 0x8CE2, }, + { name: "COLOR_ATTACHMENT3_WEBGL", enum: 0x8CE3, }, + { name: "COLOR_ATTACHMENT4_WEBGL", enum: 0x8CE4, }, + { name: "COLOR_ATTACHMENT5_WEBGL", enum: 0x8CE5, }, + { name: "COLOR_ATTACHMENT6_WEBGL", enum: 0x8CE6, }, + { name: "COLOR_ATTACHMENT7_WEBGL", enum: 0x8CE7, }, + { name: "COLOR_ATTACHMENT8_WEBGL", enum: 0x8CE8, }, + { name: "COLOR_ATTACHMENT9_WEBGL", enum: 0x8CE9, }, + { name: "COLOR_ATTACHMENT10_WEBGL", enum: 0x8CEA, }, + { name: "COLOR_ATTACHMENT11_WEBGL", enum: 0x8CEB, }, + { name: "COLOR_ATTACHMENT12_WEBGL", enum: 0x8CEC, }, + { name: "COLOR_ATTACHMENT13_WEBGL", enum: 0x8CED, }, + { name: "COLOR_ATTACHMENT14_WEBGL", enum: 0x8CEE, }, + { name: "COLOR_ATTACHMENT15_WEBGL", enum: 0x8CEF, }, + + { name: "DRAW_BUFFER0_WEBGL", enum: 0x8825, }, + { name: "DRAW_BUFFER1_WEBGL", enum: 0x8826, }, + { name: "DRAW_BUFFER2_WEBGL", enum: 0x8827, }, + { name: "DRAW_BUFFER3_WEBGL", enum: 0x8828, }, + { name: "DRAW_BUFFER4_WEBGL", enum: 0x8829, }, + { name: "DRAW_BUFFER5_WEBGL", enum: 0x882A, }, + { name: "DRAW_BUFFER6_WEBGL", enum: 0x882B, }, + { name: "DRAW_BUFFER7_WEBGL", enum: 0x882C, }, + { name: "DRAW_BUFFER8_WEBGL", enum: 0x882D, }, + { name: "DRAW_BUFFER9_WEBGL", enum: 0x882E, }, + { name: "DRAW_BUFFER10_WEBGL", enum: 0x882F, }, + { name: "DRAW_BUFFER11_WEBGL", enum: 0x8830, }, + { name: "DRAW_BUFFER12_WEBGL", enum: 0x8831, }, + { name: "DRAW_BUFFER13_WEBGL", enum: 0x8832, }, + { name: "DRAW_BUFFER14_WEBGL", enum: 0x8833, }, + { name: "DRAW_BUFFER15_WEBGL", enum: 0x8834, }, +]; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runEnumTestDisabled(); + runShadersTestDisabled(); + runAttachmentTestDisabled(); + + debug(""); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("WEBGL_draw_buffers"); + if (!ext) { + testPassed("No WEBGL_draw_buffers support -- this is legal"); + + runSupportedTest(false); + finishTest(); + } else { + testPassed("Successfully enabled WEBGL_draw_buffers extension"); + + drawBuffersUtils = WebGLDrawBuffersUtils(gl, ext); + runSupportedTest(true); + runEnumTestEnabled(); + runShadersTestEnabled(); + runAttachmentTestEnabled(); + runDrawTests(); + runPreserveTests(); + } +} + +function createExtDrawBuffersProgram(scriptId, sub) { + var fsource = wtu.getScript(scriptId); + fsource = wtu.replaceParams(fsource, sub); + return wtu.setupProgram(gl, [wtu.simpleVertexShader, fsource], ["vPosition"], undefined, true); +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("WEBGL_draw_buffers") >= 0) { + if (extensionEnabled) { + testPassed("WEBGL_draw_buffers listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_draw_buffers listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_draw_buffers not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_draw_buffers not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runEnumTestDisabled() { + debug(""); + debug("Testing binding enum with extension disabled"); + + // Use the constant directly as we don't have the extension + extensionConstants.forEach(function(c) { + if (c.expectedFn) { + shouldBeNull(`gl.getParameter(${c.enum})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, c.name + " should not be queryable if extension is disabled"); + } + }); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runEnumTestEnabled() { + debug(""); + debug("Testing enums with extension enabled"); + + extensionConstants.forEach(function(c) { + shouldBe("ext." + c.name, "0x" + c.enum.toString(16)); + if (c.expectedFn) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before getParameter"); + debug(c.name + ": 0x" + ext[c.name].toString(16)); + expectTrue(c.expectedFn(gl.getParameter(ext[c.name])), "gl.getParameter(ext." + c.name + ")" + c.passMsg); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, c.name + " query should succeed if extension is enabled"); + } + }); + + shouldBeTrue("gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL) >= gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL)"); + + debug("Testing drawBuffersWEBGL with default drawing buffer"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([])"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([gl.NONE, gl.NONE])"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.BACK])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + 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(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.NONE"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE])"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.COLOR_ATTACHMENT0,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteFramebuffer(fb)"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); +} + +function testShaders(tests, sub) { + tests.forEach(function(test) { + var shaders = [wtu.simpleVertexShader, wtu.replaceParams(wtu.getScript(test.fragmentShaderTemplate), sub)]; + var program = wtu.setupProgram(gl, shaders, ["vPosition"], undefined, true); + var programLinkedSuccessfully = (program != null); + var expectedProgramToLinkSuccessfully = (test.expectFailure == true); + expectTrue(programLinkedSuccessfully != expectedProgramToLinkSuccessfully, test.msg); + gl.deleteProgram(program); + }); +} + +function runShadersTestDisabled() { + debug(""); + debug("test shaders disabled"); + + var sub = {numDrawingBuffers: 1}; + testShaders([ + { fragmentShaderTemplate: "fshaderMacroDisabled", + msg: "GL_EXT_draw_buffers should not be defined in GLSL", + }, + { fragmentShaderTemplate: "fshader", + msg: "#extension GL_EXT_draw_buffers should not be allowed in GLSL", + expectFailure: true, + }, + ], sub); + + programWithMaxDrawBuffersEqualOne = createExtDrawBuffersProgram("fshaderBuiltInConstEnabled", sub); + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runShadersTestEnabled() { + debug(""); + debug("test shaders enabled"); + + var sub = {numDrawingBuffers: gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL)}; + testShaders([ + { fragmentShaderTemplate: "fshaderMacroEnabled", + msg: "GL_EXT_draw_buffers should be defined as 1 in GLSL", + }, + { fragmentShaderTemplate: "fshader", + msg: "fragment shader containing the #extension directive should compile", + }, + ], sub); + + var program = createExtDrawBuffersProgram("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"); + + debug(""); + debug("test that gl_MaxDrawBuffers is frozen at link time and enabling the extension won't change it."); + gl.useProgram(programWithMaxDrawBuffersEqualOne); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + gl.deleteProgram(programWithMaxDrawBuffersEqualOne); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runAttachmentTestDisabled() { + debug(""); + debug("test attachment disabled"); + var tex = gl.createTexture(); + var fb = gl.createFramebuffer(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + 1, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "should not be able to attach to gl.COLOR_ATTACHMENT1"); + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + 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 runAttachmentTestEnabled() { + debug(""); + debug("test attachment enabled"); + + var maxDrawingBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + var maxColorAttachments = gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL); + + var tex = gl.createTexture(); + var 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)); + ext.drawBuffersWEBGL(makeArray(maxDrawingBuffers, gl.NONE)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with array NONE of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxDrawingBuffers); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with array attachments of size " + maxColorAttachments); + bufs[0] = gl.NONE; + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with mixed array attachments of size " + maxColorAttachments); + if (maxDrawingBuffers > 1) { + bufs[0] = ext.COLOR_ATTACHMENT1_WEBGL; + bufs[1] = ext.COLOR_ATTACHMENT0_WEBGL; + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be able to call drawBuffersWEBGL with out of order attachments of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(Math.floor(maxDrawingBuffers / 2)); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL 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"); +} + +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(ext.MAX_DRAW_BUFFERS_WEBGL); + 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, fb); + ext.drawBuffersWEBGL(bufs); + }); + + var checkProgram = wtu.setupTexturedQuad(gl); + var redProgram = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderRed"], ["vPosition"]); + var redProgramWithExtension = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderRedWithExtension"], ["vPosition"]); + var drawProgram = createExtDrawBuffersProgram("fshader", {numDrawingBuffers: maxDrawingBuffers}); + 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(ext.DRAW_BUFFER0_WEBGL + 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); + ext.drawBuffersWEBGL(bufs); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(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 a fragment shader writing to neither gl_FragColor nor gl_FragData does not touch attachments"); + var noWriteProgram = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderNoWrite"], ["vPosition"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no GL error setting up the program"); + if (!noWriteProgram) { + testFailed("Setup a program where fragment shader writes nothing failed"); + } else { + gl.useProgram(noWriteProgram); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Active draw buffers with missing frag outputs."); + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + gl.deleteProgram(noWriteProgram); + } + + debug("test that NONE draws nothing"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(nones); + gl.useProgram(redProgram); + wtu.clearAndDrawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + + debug("test that gl_FragColor does not broadcast unless extension is enabled in fragment shader"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(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.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); + + debug("test that gl_FragColor broadcasts if extension is enabled in fragment shader"); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.useProgram(redProgramWithExtension); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + if (maxUsable > 1) { + // First half of color buffers disable. + var bufs1 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Second half of color buffers disable. + var bufs2 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Color buffers with even indices disabled. + var bufs3 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Color buffers with odd indices disabled. + var bufs4 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + for (var ii = 0; ii < maxUsable; ++ii) { + if (ii < half) { + bufs1[ii] = gl.NONE; + } else { + bufs2[ii] = gl.NONE; + } + if (ii % 2) { + bufs3[ii] = gl.NONE; + } else { + bufs4[ii] = gl.NONE; + } + } + + debug("test setting first half to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + // We should clear all buffers rather than depending on the previous + // gl_FragColor broadcasts test to succeed and setting the colors. + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + ext.drawBuffersWEBGL(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); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + ext.drawBuffersWEBGL(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]; + }); + + debug("test setting buffers with even indices to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.drawBuffersWEBGL(bufs3); + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index % 2) ? [255, 0, 0, 255] : [255, 0, 255, 255]; + }); + + debug("test setting buffers with odd indices to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(drawProgram); + ext.drawBuffersWEBGL(bufs4); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index % 2 == 0) ? [0, 0, 0, 255] : attachment.color; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB1); + ext.drawBuffersWEBGL(bufs); + drawAndCheckAttachments( + halfFB1, "framebuffer that only has first half of attachments", + function(attachment, index) { + return index < half; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB2); + ext.drawBuffersWEBGL(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); + ext.drawBuffersWEBGL(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); + ext.drawBuffersWEBGL(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 does not affect any color attachment contents"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + ext.drawBuffersWEBGL(nones); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + // fb2 still has the NONE draw buffers from before, so this draw should be a no-op. + 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 queries"); + debug("check framebuffer with all attachments on"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + for (var ii = 0; ii < maxUsable; ++ii) { + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL + " + 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(ext.DRAW_BUFFER0_WEBGL + " + ii + ")", "gl.NONE"); + } + + 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_COMPLETE"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + + 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(redProgramWithExtension); + gl.deleteProgram(drawProgram); +} + +function runPreserveTests() { + debug(""); + debug("--------- preserve tests -----------"); + + debug("Testing that frame buffer is cleared after compositing"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.clearColor(1, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [255, 255, 0, 255], "should be yellow"); + + // set the draw buffer to NONE + ext.drawBuffersWEBGL([gl.NONE]); + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + // make sure the canvas is still clear + wtu.checkCanvas(gl, [255, 255, 0, 255], "should be yellow"); + + wtu.waitForComposite(function() { + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "should be clear"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + runEndTests(); + }); +} + +function runEndTests() { + // Create new context and verify shader tests with no extension still succeeds. + debug(""); + debug("Testing new context with no extension"); + gl = wtu.create3DContext(); + if (!gl) { + testFailed("New WebGL context does not exist"); + } else { + testPassed("New WebGL context exists"); + runEnumTestDisabled(); + runShadersTestDisabled(); + runAttachmentTestDisabled(); + } + + finishTest(); +} +</script> +</body> +</html> |