webgl-specific-stencil-settings.html (11177B)
1 <!-- 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <title>WebGL stencil mask/func front-state-back-state equality test</title> 12 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 13 <script src="../../js/js-test-pre.js"></script> 14 <script src="../../js/webgl-test-utils.js"></script> 15 </head> 16 <body> 17 <div id="description"></div> 18 <div id="console"></div> 19 20 <script> 21 "use strict"; 22 var wtu = WebGLTestUtils; 23 description("Tests that stencil mask/func are validated correctly when the front state and back state differ."); 24 25 var gl; 26 27 function checkDrawError(errIfMismatch) { 28 wtu.shouldGenerateGLError(gl, errIfMismatch, "wtu.dummySetProgramAndDrawNothing(gl)"); 29 } 30 31 function setStencilMask(mask) { 32 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.FRONT, " + mask[0] + ")"); 33 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMaskSeparate(gl.BACK, " + mask[1] + ")"); 34 } 35 36 function testStencilMaskCase(mask, error) { 37 setStencilMask(mask); 38 // If an error is generated, it should be at draw time. 39 checkDrawError(error); 40 } 41 42 function testStencilMask(errIfMismatch) { 43 testStencilMaskCase([0, 256], gl.NO_ERROR); 44 testStencilMaskCase([1, 256], errIfMismatch); 45 testStencilMaskCase([1, 257], gl.NO_ERROR); 46 testStencilMaskCase([1, 258], errIfMismatch); 47 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilMask(1023)", "resetting stencilMask"); 48 } 49 50 function setStencilFunc(ref, mask) { 51 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFuncSeparate(gl.FRONT, gl.ALWAYS, " + ref[0] + ", " + mask[0] + ")"); 52 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFuncSeparate(gl.BACK, gl.ALWAYS, " + ref[1] + ", " + mask[1] + ")"); 53 } 54 55 function testStencilFuncCase(ref, mask, error) { 56 setStencilFunc(ref, mask); 57 // If an error is generated, it should be at draw time. 58 checkDrawError(error); 59 } 60 61 function testStencilFunc(errIfMismatch) { 62 testStencilFuncCase([ 256, 257], [1023, 1023], gl.NO_ERROR); 63 testStencilFuncCase([ 256, 254], [1023, 1023], errIfMismatch); 64 65 testStencilFuncCase([ -1, 0], [1023, 1023], gl.NO_ERROR); 66 testStencilFuncCase([ -1, 254], [1023, 1023], errIfMismatch); 67 68 testStencilFuncCase([ 0, 0], [ 1, 257], gl.NO_ERROR); 69 testStencilFuncCase([ 0, 0], [ 1, 258], errIfMismatch); 70 71 testStencilFuncCase([ 1, 1], [1024, 2048], gl.NO_ERROR); 72 testStencilFuncCase([ 1, 1], [2048, 1024], gl.NO_ERROR); 73 74 testStencilFuncCase([ -1, -1], [1023, 1023], gl.NO_ERROR); 75 testStencilFuncCase([ -1, 0], [1023, 1023], gl.NO_ERROR); 76 testStencilFuncCase([ 0, -1], [1023, 1023], gl.NO_ERROR); 77 testStencilFuncCase([ 0, 0], [1023, 1023], gl.NO_ERROR); 78 79 testStencilFuncCase([ -1, 255], [1023, 1023], errIfMismatch); 80 testStencilFuncCase([ 0, 256], [1023, 1023], errIfMismatch); 81 testStencilFuncCase([ 0, 1024], [1023, 1023], errIfMismatch); 82 testStencilFuncCase([ 1, 257], [1023, 1023], errIfMismatch); 83 testStencilFuncCase([ 255, -1], [1023, 1023], errIfMismatch); 84 testStencilFuncCase([ 256, 0], [1023, 1023], errIfMismatch); 85 testStencilFuncCase([1024, 0], [1023, 1023], errIfMismatch); 86 testStencilFuncCase([ 257, 1], [1023, 1023], errIfMismatch); 87 88 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.stencilFunc(gl.ALWAYS, 0, 1023)", "resetting stencilFunc"); 89 } 90 91 // 92 // Tests of the default framebuffer 93 // 94 95 debug(""); 96 97 debug("Testing default framebuffer with { stencil: true }"); 98 gl = wtu.create3DContext(undefined, { stencil: true }); 99 { 100 gl.enable(gl.STENCIL_TEST); 101 testStencilMaskCase([1, 256], gl.INVALID_OPERATION); 102 testStencilFuncCase([256, 0], [1023, 1023], gl.INVALID_OPERATION); 103 } 104 105 debug("Testing default framebuffer with { stencil: false }"); 106 gl = wtu.create3DContext(undefined, { stencil: false }); 107 { 108 // with { stencil: false } 109 gl.enable(gl.STENCIL_TEST); 110 testStencilMaskCase([1, 256], gl.NO_ERROR); 111 testStencilFuncCase([256, 0], [1023, 1023], gl.NO_ERROR); 112 } 113 // (continue using this GL context for the other tests) 114 115 // 116 // Tests with a framebuffer object 117 // 118 119 const fb = gl.createFramebuffer(); 120 gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 121 const colorRB = gl.createRenderbuffer(); 122 gl.bindRenderbuffer(gl.RENDERBUFFER, colorRB); 123 gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 1, 1); 124 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRB); 125 126 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "initial framebuffer setup") 127 128 function runWithStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, fn) { 129 let rbo = null; 130 let attachment; 131 if (haveDepthBuffer || haveStencilBuffer) { 132 rbo = gl.createRenderbuffer(); 133 gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); 134 135 let internalformat; 136 if (haveDepthBuffer && haveStencilBuffer) { 137 internalformat = gl.DEPTH_STENCIL; 138 attachment = gl.DEPTH_STENCIL_ATTACHMENT; 139 } else if (haveDepthBuffer) { 140 internalformat = gl.DEPTH_COMPONENT16; 141 attachment = gl.DEPTH_ATTACHMENT; 142 } else if (haveStencilBuffer) { 143 internalformat = gl.STENCIL_INDEX8; 144 attachment = gl.STENCIL_ATTACHMENT; 145 } 146 gl.renderbufferStorage(gl.RENDERBUFFER, internalformat, 1, 1); 147 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, rbo); 148 } 149 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); 150 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "depth/stencil renderbuffer setup") 151 152 if (enableStencilTest) { 153 gl.enable(gl.STENCIL_TEST); 154 } else { 155 gl.disable(gl.STENCIL_TEST); 156 } 157 158 fn(); 159 160 if (rbo) { 161 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, null); 162 gl.bindRenderbuffer(gl.RENDERBUFFER, null); 163 gl.deleteRenderbuffer(rbo); 164 } 165 166 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "depth/stencil renderbuffer cleanup") 167 } 168 169 function testStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, errIfMismatch) { 170 debug(""); 171 debug("With depthbuffer=" + haveDepthBuffer + 172 ", stencilbuffer=" + haveStencilBuffer + 173 ", stencilTest=" + enableStencilTest + 174 ", expecting error=" + wtu.glEnumToString(gl, errIfMismatch) + 175 " for mismatching mask or func settings."); 176 177 runWithStencilSettings(haveDepthBuffer, haveStencilBuffer, enableStencilTest, () => { 178 // Errors should be the same for both mask and func, because stencil test 179 // and stencil write are always enabled/disabled in tandem. 180 testStencilMask(errIfMismatch); 181 testStencilFunc(errIfMismatch); 182 }); 183 } 184 185 debug(""); 186 debug("Base case checks:"); 187 testStencilMaskCase([0, 0], gl.NO_ERROR); 188 testStencilFuncCase([0, 0], [1023, 1023], gl.NO_ERROR); 189 190 // haveDepthBuffer 191 // | haveStencilBuffer 192 // | | enableStencilTest 193 // | | | errIfMismatch 194 testStencilSettings(false, false, false, gl.NO_ERROR); 195 testStencilSettings( true, false, false, gl.NO_ERROR); 196 testStencilSettings(false, true, false, gl.NO_ERROR); 197 testStencilSettings( true, true, false, gl.NO_ERROR); 198 199 testStencilSettings(false, false, true, gl.NO_ERROR); 200 testStencilSettings( true, false, true, gl.NO_ERROR); 201 testStencilSettings(false, true, true, gl.INVALID_OPERATION); 202 testStencilSettings( true, true, true, gl.INVALID_OPERATION); 203 204 // 205 // Tests to make sure the stencil validation check, if cached, is invalidated correctly. 206 // 207 208 debug(""); 209 210 debug("Setup for stencil validation cache invalidation tests"); 211 setStencilMask([1, 258]); 212 setStencilFunc([0, 256], [1023, 1023]); 213 214 debug("Test with enabling/disabling stencil test"); 215 runWithStencilSettings(false, true, false, () => { 216 checkDrawError(gl.NO_ERROR); 217 gl.enable(gl.STENCIL_TEST); 218 checkDrawError(gl.INVALID_OPERATION); 219 gl.disable(gl.STENCIL_TEST); 220 checkDrawError(gl.NO_ERROR); 221 }); 222 223 debug("Test with swapping in a new FBO"); 224 runWithStencilSettings(false, false, true, () => { 225 // no error with no stencil buffer 226 checkDrawError(gl.NO_ERROR); 227 228 // swap in a new FBO with a stencil buffer 229 const fb2 = gl.createFramebuffer(); 230 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); 231 gl.bindRenderbuffer(gl.RENDERBUFFER, colorRB); 232 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRB); 233 const rbo = gl.createRenderbuffer(); 234 gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); 235 gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, 1, 1); 236 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo); 237 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); 238 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 239 240 // this draw sholud detect the new fbo state 241 checkDrawError(gl.INVALID_OPERATION); 242 243 gl.deleteFramebuffer(fb2); 244 gl.deleteRenderbuffer(rbo) 245 gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 246 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 247 }); 248 249 debug("Test with adding a stencil attachment"); 250 runWithStencilSettings(false, false, true, () => { 251 // no error with no stencil buffer 252 checkDrawError(gl.NO_ERROR); 253 254 // add a stencil attachment 255 const rbo = gl.createRenderbuffer(); 256 gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); 257 gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, 1, 1); 258 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo); 259 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); 260 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 261 262 // this draw sholud detect the new fbo state 263 checkDrawError(gl.INVALID_OPERATION); 264 265 gl.deleteRenderbuffer(rbo) 266 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 267 }); 268 269 debug("Test with reallocating the DEPTH_STENCIL attachment from depth to depth+stencil"); 270 runWithStencilSettings(false, false, true, () => { 271 // attach a depth buffer to the DEPTH_STENCIL attachment 272 const rbo = gl.createRenderbuffer(); 273 gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); 274 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); 275 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rbo); 276 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); 277 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 278 279 // this draw is invalid, but it still might trigger caching of the stencil validation 280 checkDrawError(gl.INVALID_FRAMEBUFFER_OPERATION); 281 282 gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, 1, 1); 283 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 284 // this draw sholud detect the new fbo state 285 checkDrawError(gl.INVALID_OPERATION); 286 287 gl.deleteRenderbuffer(rbo) 288 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 289 }); 290 291 gl.deleteFramebuffer(fb); 292 gl.deleteRenderbuffer(colorRB); 293 294 var successfullyParsed = true; 295 </script> 296 297 <script src="../../js/js-test-post.js"></script> 298 </body> 299 </html>