| <!doctype html> | |
| <meta charset="utf-8"> | |
| <title>WebGL state reaches a framebuffer created by the page</title> | |
| <link rel="author" title="mxmgorin" href="mailto:102797145+mxmgorin@users.noreply.github.com"> | |
| <link rel="help" href="https://registry.khronos.org/webgl/specs/latest/1.0/#5.14.3"> | |
| <link rel="help" href="https://registry.khronos.org/OpenGL-Refpages/es2.0/xhtml/glStencilMask.xml"> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| <script> | |
| function createContext() { | |
| // No stencil on the default framebuffer, so the only stencil is the FBO's. | |
| var gl = document.createElement("canvas").getContext("webgl", { stencil: false }); | |
| var framebuffer = gl.createFramebuffer(); | |
| gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); | |
| var texture = gl.createTexture(); | |
| gl.bindTexture(gl.TEXTURE_2D, texture); | |
| gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); | |
| gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); | |
| var renderbuffer = gl.createRenderbuffer(); | |
| gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); | |
| gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, 4, 4); | |
| gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, | |
| gl.RENDERBUFFER, renderbuffer); | |
| assert_equals(gl.checkFramebufferStatus(gl.FRAMEBUFFER), gl.FRAMEBUFFER_COMPLETE); | |
| var vertexShader = gl.createShader(gl.VERTEX_SHADER); | |
| gl.shaderSource(vertexShader, "attribute vec2 p; void main() { gl_Position = vec4(p, 0, 1); }"); | |
| gl.compileShader(vertexShader); | |
| var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); | |
| gl.shaderSource(fragmentShader, "void main() { gl_FragColor = vec4(1, 0, 0, 1); }"); | |
| gl.compileShader(fragmentShader); | |
| var program = gl.createProgram(); | |
| gl.attachShader(program, vertexShader); | |
| gl.attachShader(program, fragmentShader); | |
| gl.linkProgram(program); | |
| gl.useProgram(program); | |
| // One triangle covering the whole viewport. | |
| var buffer = gl.createBuffer(); | |
| gl.bindBuffer(gl.ARRAY_BUFFER, buffer); | |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW); | |
| gl.enableVertexAttribArray(0); | |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); | |
| return gl; | |
| } | |
| function readPixel(gl) { | |
| var pixel = new Uint8Array(4); | |
| gl.readPixels(1, 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel); | |
| return Array.from(pixel); | |
| } | |
| test(function() { | |
| var gl = document.createElement("canvas").getContext("webgl"); | |
| // Masked to the low byte: the stencil buffers WebGL gets are 8 bits deep. | |
| assert_equals(gl.getParameter(gl.STENCIL_WRITEMASK) & 0xff, 0xff); | |
| assert_equals(gl.getParameter(gl.STENCIL_BACK_WRITEMASK) & 0xff, 0xff); | |
| }, "the initial stencil writemask is all ones"); | |
| test(function() { | |
| var gl = createContext(); | |
| gl.enable(gl.STENCIL_TEST); | |
| // Write 5 into the stencil buffer without ever calling stencilMask. | |
| gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE); | |
| gl.stencilFunc(gl.ALWAYS, 5, 0xff); | |
| gl.drawArrays(gl.TRIANGLES, 0, 3); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT); | |
| gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP); | |
| gl.stencilFunc(gl.EQUAL, 5, 0xff); | |
| gl.drawArrays(gl.TRIANGLES, 0, 3); | |
| assert_array_equals(readPixel(gl), [255, 0, 0, 255]); | |
| assert_equals(gl.getError(), gl.NO_ERROR); | |
| }, "stencil writes reach a framebuffer's stencil attachment by default"); | |
| test(function() { | |
| var gl = createContext(); | |
| // Set before the framebuffer's first clear, which lazily initializes it. | |
| gl.clearStencil(1); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); | |
| gl.enable(gl.STENCIL_TEST); | |
| gl.stencilFunc(gl.EQUAL, 1, 0xff); | |
| gl.drawArrays(gl.TRIANGLES, 0, 3); | |
| assert_array_equals(readPixel(gl), [255, 0, 0, 255]); | |
| assert_equals(gl.getError(), gl.NO_ERROR); | |
| }, "the stencil clear value survives a framebuffer's first clear"); | |
| test(function() { | |
| var gl = createContext(); | |
| gl.clearDepth(0); | |
| gl.clearColor(0, 0, 0, 1); | |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
| // The triangle sits at depth 0.5: GREATER passes against 0, not against 1. | |
| gl.enable(gl.DEPTH_TEST); | |
| gl.depthFunc(gl.GREATER); | |
| gl.drawArrays(gl.TRIANGLES, 0, 3); | |
| assert_array_equals(readPixel(gl), [255, 0, 0, 255]); | |
| assert_equals(gl.getError(), gl.NO_ERROR); | |
| }, "the depth clear value survives a framebuffer's first clear"); | |
| </script> |