| <!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> |