webgl-depth-texture.html (15077B)
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 <!DOCTYPE html> 7 <html> 8 <head> 9 <meta charset="utf-8"> 10 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 11 <script src="../../js/js-test-pre.js"></script> 12 <script src="../../js/webgl-test-utils.js"></script> 13 <title>WebGL WEBGL_depth_texture Conformance Tests</title> 14 </head> 15 <body> 16 <script id="vshader" type="x-shader/x-vertex"> 17 attribute vec4 a_position; 18 void main() 19 { 20 gl_Position = a_position; 21 } 22 </script> 23 24 <script id="fshader" type="x-shader/x-fragment"> 25 precision mediump float; 26 uniform sampler2D u_texture; 27 uniform vec2 u_resolution; 28 void main() 29 { 30 vec2 texcoord = (gl_FragCoord.xy - vec2(0.5)) / (u_resolution - vec2(1.0)); 31 gl_FragColor = texture2D(u_texture, texcoord); 32 } 33 </script> 34 <div id="description"></div> 35 <div id="console"></div> 36 <canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> 37 <script> 38 "use strict"; 39 description("This test verifies the functionality of the WEBGL_depth_texture extension, if it is available."); 40 41 debug(""); 42 43 var wtu = WebGLTestUtils; 44 var canvas = document.getElementById("canvas"); 45 var gl = wtu.create3DContext(canvas, {antialias: false}); 46 var program = wtu.setupTexturedQuad(gl); 47 var ext = null; 48 var vao = null; 49 var tex; 50 var name; 51 var supportedFormats; 52 var canvas2; 53 54 if (!gl) { 55 testFailed("WebGL context does not exist"); 56 } else { 57 testPassed("WebGL context exists"); 58 59 // Run tests with extension disabled 60 runTestDisabled(); 61 62 // Query the extension and store globally so shouldBe can access it 63 ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_depth_texture"); 64 if (!ext) { 65 testPassed("No WEBGL_depth_texture support -- this is legal"); 66 runSupportedTest(false); 67 } else { 68 testPassed("Successfully enabled WEBGL_depth_texture extension"); 69 70 runSupportedTest(true); 71 runTestExtension(true); 72 runTestExtension(false); 73 } 74 } 75 76 function runSupportedTest(extensionEnabled) { 77 var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_depth_texture"); 78 if (name !== undefined) { 79 if (extensionEnabled) { 80 testPassed("WEBGL_depth_texture listed as supported and getExtension succeeded"); 81 } else { 82 testFailed("WEBGL_depth_texture listed as supported but getExtension failed"); 83 } 84 } else { 85 if (extensionEnabled) { 86 testFailed("WEBGL_depth_texture not listed as supported but getExtension succeeded"); 87 } else { 88 testPassed("WEBGL_depth_texture not listed as supported and getExtension failed -- this is legal"); 89 } 90 } 91 } 92 93 94 function runTestDisabled() { 95 debug("Testing binding enum with extension disabled"); 96 97 var tex = gl.createTexture(); 98 gl.bindTexture(gl.TEXTURE_2D, tex); 99 wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_VALUE], 100 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, 1, 1, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null)'); 101 wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_VALUE], 102 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, 1, 1, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null)'); 103 } 104 105 106 function dumpIt(gl, res, msg) { 107 return; // comment out to debug 108 debug(msg); 109 var actualPixels = new Uint8Array(res * res * 4); 110 gl.readPixels(0, 0, res, res, gl.RGBA, gl.UNSIGNED_BYTE, actualPixels); 111 112 for (var yy = 0; yy < res; ++yy) { 113 var strs = []; 114 for (var xx = 0; xx < res; ++xx) { 115 var actual = (yy * res + xx) * 4; 116 strs.push("(" + actualPixels[actual] + "," + actualPixels[actual+1] + "," + actualPixels[actual + 2] + "," + actualPixels[actual + 3] + ")"); 117 } 118 debug(strs.join(" ")); 119 } 120 } 121 function runTestExtension(unpackFlipY) { 122 debug("Testing WEBGL_depth_texture. UNPACK_FLIP_Y_WEBGL: " + unpackFlipY); 123 124 const res = 2; 125 const destRes = 4; 126 127 // make canvas for testing. 128 canvas2 = document.createElement("canvas"); 129 canvas2.width = res; 130 canvas2.height = res; 131 var ctx = canvas2.getContext("2d"); 132 ctx.fillStyle = "blue"; 133 ctx.fillRect(0, 0, canvas2.width, canvas2.height); 134 135 var program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['a_position']); 136 gl.useProgram(program); 137 gl.uniform2f(gl.getUniformLocation(program, "u_resolution"), destRes, destRes); 138 139 var buffer = gl.createBuffer(); 140 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 141 gl.bufferData( 142 gl.ARRAY_BUFFER, 143 new Float32Array( 144 [ 1, 1, 1, 145 -1, 1, 0, 146 -1, -1, -1, 147 1, 1, 1, 148 -1, -1, -1, 149 1, -1, 0, 150 ]), 151 gl.STATIC_DRAW); 152 gl.enableVertexAttribArray(0); 153 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); 154 155 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, unpackFlipY); 156 157 var types = [ 158 {obj: 'gl', attachment: 'DEPTH_ATTACHMENT', format: 'DEPTH_COMPONENT', type: 'UNSIGNED_SHORT', data: 'new Uint16Array(1)', depthBits: "16"}, 159 {obj: 'gl', attachment: 'DEPTH_ATTACHMENT', format: 'DEPTH_COMPONENT', type: 'UNSIGNED_INT', data: 'new Uint32Array(1)', depthBits: "16"}, 160 {obj: 'ext', attachment: 'DEPTH_STENCIL_ATTACHMENT', format: 'DEPTH_STENCIL', type: 'UNSIGNED_INT_24_8_WEBGL', data: 'new Uint32Array(1)', depthBits: "24", stencilBits: "8"} 161 ]; 162 163 for (var ii = 0; ii < types.length; ++ii) { 164 var typeInfo = types[ii]; 165 var type = typeInfo.type; 166 var typeStr = typeInfo.obj + '.' + type; 167 168 debug(""); 169 debug("testing: " + type); 170 171 // check that cubemaps are not allowed. 172 var cubeTex = gl.createTexture(); 173 gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTex); 174 var targets = [ 175 'TEXTURE_CUBE_MAP_POSITIVE_X', 176 'TEXTURE_CUBE_MAP_NEGATIVE_X', 177 'TEXTURE_CUBE_MAP_POSITIVE_Y', 178 'TEXTURE_CUBE_MAP_NEGATIVE_Y', 179 'TEXTURE_CUBE_MAP_POSITIVE_Z', 180 'TEXTURE_CUBE_MAP_NEGATIVE_Z' 181 ]; 182 for (var tt = 0; tt < targets.length; ++tt) { 183 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.' + targets[ii] + ', 0, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); 184 } 185 186 // The WebGL_depth_texture extension supports both NEAREST and 187 // LINEAR filtering for depth textures, even though LINEAR 188 // doesn't have much meaning, and isn't supported in WebGL 189 // 2.0. Still, test both. 190 var filterModes = [ 191 'LINEAR', 192 'NEAREST' 193 ]; 194 195 for (var jj = 0; jj < filterModes.length; ++jj) { 196 debug(''); 197 debug('testing ' + filterModes[jj] + ' filtering'); 198 var filterMode = gl[filterModes[jj]]; 199 200 // check 2d textures. 201 tex = gl.createTexture(); 202 gl.bindTexture(gl.TEXTURE_2D, tex); 203 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 204 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 205 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode); 206 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode); 207 208 // test level > 0 209 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.TEXTURE_2D, 1, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); 210 211 // test with data 212 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', ' + typeInfo.data + ')'); 213 214 // test with canvas 215 wtu.shouldGenerateGLError(gl, [gl.INVALID_VALUE, gl.INVALID_ENUM, gl.INVALID_OPERATION], 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', gl.' + typeInfo.format + ', ' + typeStr + ', canvas2)'); 216 217 // test copyTexImage2D 218 wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], 'gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', 0, 0, 1, 1, 0)'); 219 220 // test real thing 221 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', ' + res + ', ' + res + ', 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); 222 223 // test texSubImage2D 224 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.' + typeInfo.format + ', ' + typeStr + ', ' + typeInfo.data + ')'); 225 226 // test copyTexSubImage2D 227 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1)'); 228 229 // test generateMipmap 230 wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.generateMipmap(gl.TEXTURE_2D)'); 231 232 var fbo = gl.createFramebuffer(); 233 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 234 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl[typeInfo.attachment], gl.TEXTURE_2D, tex, 0); 235 236 // Ensure DEPTH_BITS returns >= 16 bits for UNSIGNED_SHORT and UNSIGNED_INT, >= 24 UNSIGNED_INT_24_8_WEBGL. 237 // If there is stencil, ensure STENCIL_BITS reports >= 8 for UNSIGNED_INT_24_8_WEBGL. 238 shouldBeGreaterThanOrEqual('gl.getParameter(gl.DEPTH_BITS)', typeInfo.depthBits); 239 if (typeInfo.stencilBits === undefined) { 240 shouldBe('gl.getParameter(gl.STENCIL_BITS)', '0'); 241 } else { 242 shouldBeGreaterThanOrEqual('gl.getParameter(gl.STENCIL_BITS)', typeInfo.stencilBits); 243 } 244 245 // TODO: remove this check if the spec is updated to require these combinations to work. 246 if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) 247 { 248 // try adding a color buffer. 249 var colorTex = gl.createTexture(); 250 gl.bindTexture(gl.TEXTURE_2D, colorTex); 251 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 252 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 253 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 254 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 255 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, res, res, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 256 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0); 257 } 258 259 shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); 260 261 // use the default texture to render with while we return to the depth texture. 262 gl.bindTexture(gl.TEXTURE_2D, null); 263 264 /* Setup 2x2 depth texture: 265 * 1 0.6 0.8 266 * | 267 * 0 0.2 0.4 268 * 0---1 269 */ 270 const d00 = 0.2; 271 const d01 = 0.4; 272 const d10 = 0.6; 273 const d11 = 0.8; 274 275 gl.enable(gl.SCISSOR_TEST); 276 277 gl.scissor(0, 0, 1, 1); 278 gl.clearDepth(d00); 279 gl.clear(gl.DEPTH_BUFFER_BIT); 280 281 gl.scissor(1, 0, 1, 1); 282 gl.clearDepth(d10); 283 gl.clear(gl.DEPTH_BUFFER_BIT); 284 285 gl.scissor(0, 1, 1, 1); 286 gl.clearDepth(d01); 287 gl.clear(gl.DEPTH_BUFFER_BIT); 288 289 gl.scissor(1, 1, 1, 1); 290 gl.clearDepth(d11); 291 gl.clear(gl.DEPTH_BUFFER_BIT); 292 293 gl.disable(gl.SCISSOR_TEST); 294 295 // render the depth texture. 296 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 297 gl.canvas.width = destRes; 298 gl.canvas.height = destRes; 299 gl.viewport(0, 0, destRes, destRes); 300 gl.bindTexture(gl.TEXTURE_2D, tex); 301 302 gl.disable(gl.DITHER); 303 gl.enable(gl.DEPTH_TEST); 304 gl.clearColor(1, 0, 0, 1); 305 gl.clearDepth(1.0); 306 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 307 gl.drawArrays(gl.TRIANGLES, 0, 6); 308 309 dumpIt(gl, res, "--depth--"); 310 311 var actualPixels = new Uint8Array(destRes * destRes * 4); 312 gl.readPixels(0, 0, destRes, destRes, gl.RGBA, gl.UNSIGNED_BYTE, actualPixels); 313 314 const eps = 0.002; 315 316 let expectedMin; 317 let expectedMax; 318 if (filterMode == gl.NEAREST) { 319 expectedMin = [ 320 d00, d00, d10, d10, 321 d00, d00, d10, d10, 322 d01, d01, d11, d11, 323 d01, d01, d11, d11 324 ]; 325 expectedMax = expectedMin.slice(); 326 327 expectedMin = expectedMin.map(x => x - eps); 328 expectedMax = expectedMax.map(x => x + eps); 329 } else { 330 expectedMin = [ 331 d00-eps, d00, d00, d10-eps, 332 d00, d00, d00, d10, 333 d00, d00, d00, d10, 334 d01-eps, d01, d01, d11-eps, 335 ]; 336 expectedMax = [ 337 d00+eps, d10, d10, d10+eps, 338 d01, d11, d11, d11, 339 d01, d11, d11, d11, 340 d01+eps, d11, d11, d11+eps, 341 ]; 342 } 343 344 for (var yy = 0; yy < destRes; ++yy) { 345 for (var xx = 0; xx < destRes; ++xx) { 346 const t = xx + destRes*yy; 347 const was = actualPixels[4*t] / 255.0; // 4bpp 348 const eMin = expectedMin[t]; 349 const eMax = expectedMax[t]; 350 let func = testPassed; 351 const text = `At ${xx},${yy}, expected within [${eMin},${eMax}], was ${was.toFixed(3)}` 352 if (was <= eMin || was >= eMax) { 353 func = testFailed; 354 } 355 func(text); 356 } 357 } 358 359 // check limitations 360 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 361 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl[typeInfo.attachment], gl.TEXTURE_2D, null, 0); 362 var badAttachment = typeInfo.attachment == 'DEPTH_ATTACHMENT' ? 'DEPTH_STENCIL_ATTACHMENT' : 'DEPTH_ATTACHMENT'; 363 wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.' + badAttachment + ', gl.TEXTURE_2D, tex, 0)'); 364 shouldNotBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); 365 wtu.shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, 'gl.clear(gl.DEPTH_BUFFER_BIT)'); 366 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 367 shouldBe('gl.getError()', 'gl.NO_ERROR'); 368 } 369 } 370 } 371 372 debug(""); 373 var successfullyParsed = true; 374 </script> 375 <script src="../../js/js-test-post.js"></script> 376 </body> 377 </html>