xrWebGLLayer_opaque_framebuffer_stencil.https.html (7686B)
1 <!DOCTYPE html> 2 <script src="/resources/testharness.js"></script> 3 <script src="/resources/testharnessreport.js"></script> 4 <script src="resources/webxr_util.js"></script> 5 <script src="resources/webxr_test_constants.js"></script> 6 7 <script> 8 let immersiveTestName = "Ensure that the framebuffer given by the WebGL layer" + 9 " works with stencil for immersive"; 10 let nonImmersiveTestName = "Ensure that the framebuffer given by the WebGL layer" + 11 " works with stencil for non-immersive"; 12 13 let fakeDeviceInitParams = TRACKED_IMMERSIVE_DEVICE; 14 15 function createShader(gl, type, source) { 16 var shader = gl.createShader(type); 17 gl.shaderSource(shader, source); 18 gl.compileShader(shader); 19 var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); 20 if (success) { 21 return shader; 22 } 23 24 gl.deleteShader(shader); 25 } 26 27 function createProgram(gl, vertexShader, fragmentShader) { 28 var program = gl.createProgram(); 29 gl.attachShader(program, vertexShader); 30 gl.attachShader(program, fragmentShader); 31 gl.linkProgram(program); 32 var success = gl.getProgramParameter(program, gl.LINK_STATUS); 33 if (success) { 34 return program; 35 } 36 37 gl.deleteProgram(program); 38 } 39 40 let testFunction = 41 (session, fakeDeviceController, t, sessionObjects) => new Promise((resolve, reject) => { 42 const gl = sessionObjects.gl; 43 const webglLayer = sessionObjects.glLayer; 44 const xrFramebuffer = webglLayer.framebuffer; 45 const webglCanvas = sessionObjects.gl.canvas; 46 47 session.requestAnimationFrame((time, xrFrame) => { 48 t.step(() => { 49 // Make sure we're starting with a clean error slate. 50 gl.getError(); 51 assert_equals(gl.getError(), gl.NO_ERROR, "Should not initially have any errors"); 52 53 if (session.mode === 'inline') { 54 // Creating a layer with an inline session should return a framebuffer of 55 // null, and as such most of these tests won't apply. 56 assert_equals(xrFramebuffer, null, 'inline, fbo = null'); 57 // We need to set canvas size here for inline session testing, since 58 // xrFramebuffer is null. 59 webglCanvas.width = webglCanvas.height = 300; 60 gl.bindFramebuffer(gl.FRAMEBUFFER, xrFramebuffer); 61 assert_equals(gl.getError(), gl.NO_ERROR, "Binding default framebuffer for inline session"); 62 } else { 63 assert_not_equals(xrFramebuffer, null, 'immersive, fbo != null'); 64 gl.bindFramebuffer(gl.FRAMEBUFFER, xrFramebuffer); 65 assert_equals(gl.getError(), gl.NO_ERROR, "Binding WebGLLayer framebuffer"); 66 } 67 68 // Framebuffer status must be complete inside of a XR frame callback. 69 assert_equals(gl.checkFramebufferStatus(gl.FRAMEBUFFER), gl.FRAMEBUFFER_COMPLETE, "FBO complete"); 70 }); 71 gl.clearColor(1, 1, 1, 1); 72 73 const vs = ` 74 attribute vec4 position; 75 uniform mat4 matrix; 76 void main() { 77 gl_Position = matrix * position; 78 } 79 `; 80 81 const fs = ` 82 precision mediump float; 83 uniform vec4 color; 84 void main() { 85 gl_FragColor = color; 86 } 87 `; 88 const vertexShader = createShader(gl, gl.VERTEX_SHADER, vs); 89 const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fs); 90 const program = createProgram(gl, vertexShader, fragmentShader); 91 92 const posLoc = gl.getAttribLocation(program, 'position'); 93 const matLoc = gl.getUniformLocation(program, 'matrix'); 94 const colorLoc = gl.getUniformLocation(program, 'color'); 95 96 const buf = gl.createBuffer(); 97 gl.bindBuffer(gl.ARRAY_BUFFER, buf); 98 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 99 0, -1, 100 1, 1, 101 -1, 1, 102 ]), gl.STATIC_DRAW); 103 104 gl.enableVertexAttribArray(posLoc); 105 gl.vertexAttribPointer( 106 posLoc, // attribute location 107 2, // 2 value per vertex 108 gl.FLOAT, // 32bit floating point values 109 false, // don't normalize 110 0, // stride (0 = base on type and size) 111 0, // offset into buffer 112 ); 113 114 let xrViewport; 115 if (session.mode == 'inline') { 116 xrViewport = { x: 0, y: 0, width: webglCanvas.width, height: webglCanvas.height }; 117 } else { 118 xrViewport = { x: 0, y: 0, width: webglLayer.framebufferWidth, height: webglLayer.framebufferHeight }; 119 } 120 121 gl.viewport(xrViewport.x, xrViewport.y, xrViewport.width, xrViewport.height); 122 gl.scissor(xrViewport.x, xrViewport.y, xrViewport.width, xrViewport.height); 123 124 // clear the stencil to 0 (the default) 125 gl.stencilMask(0xFF); 126 gl.clearStencil(0x0); 127 gl.disable( gl.SCISSOR_TEST ); 128 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); 129 130 gl.useProgram(program); 131 let m4 = [1, 0, 0, 0, 132 0, 1, 0, 0, 133 0, 0, 1, 0, 134 0, 0, 0, 1]; 135 136 // turn on the stencil 137 gl.enable(gl.STENCIL_TEST); 138 139 // Drawing into a stencil, always passes, ref val 1, mask 0xFF 140 gl.stencilFunc( 141 gl.ALWAYS, 142 1, 143 0xFF, 144 ); 145 // Set it to replace with the ref val (which is 1) 146 gl.stencilOp( 147 gl.KEEP, // stencil test fails 148 gl.KEEP, // depth test fails 149 gl.REPLACE, // both tests pass 150 ); 151 152 m4[0] = 0.2; m4[5] = 0.2; // scale x and y 153 // draw a white triangle 154 gl.uniform4fv(colorLoc, [1, 1, 1, 1]); // white 155 gl.uniformMatrix4fv(matLoc, false, m4); 156 gl.colorMask(false, false, false, false); 157 gl.drawArrays(gl.TRIANGLES, 0, 3); 158 159 gl.colorMask(true, true, true, true); 160 161 // Stencil must be 0 162 gl.stencilFunc( 163 gl.EQUAL, 164 0, 165 0xFF, 166 ); 167 // keep stencil unmodified during the draw pass 168 gl.stencilOp( 169 gl.KEEP, // stencil test fails 170 gl.KEEP, // depth test fails 171 gl.KEEP, // both tests pass 172 ); 173 174 m4[0] = 0.9; m4[5] = -0.9; // scale x and y 175 // draw a large green triangle 176 gl.uniform4fv(colorLoc, [0, 1, 0, 1]); // green 177 gl.uniformMatrix4fv(matLoc, false, m4); 178 gl.drawArrays(gl.TRIANGLES, 0, 3); 179 180 gl.flush(); 181 gl.finish(); 182 let pixels = new Uint8Array(4); 183 184 // check that the main color is used correctly (green) 185 pixels[0] = pixels[1] = pixels[2] = pixels[3] = 30; 186 gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/4, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 187 if (pixels[0] == 0x0 && pixels[1] == 0xFF && pixels[2] == 0x0) { // green? 188 // PASSED. 189 } else if (pixels[0] == 0xFF && pixels[1] == 0xFF && pixels[2] == 0xFF) { // white? 190 reject("Failed, white detected, must be green"); 191 } else { 192 reject("Failed, readPixels (1) didn't work, gl error = " + gl.getError() + ", pixel = " +pixels[0] + " " +pixels[1] + " " +pixels[2]); 193 } 194 195 // check if stencil worked, i.e. white pixels in the center 196 pixels[0] = pixels[1] = pixels[2] = pixels[3] = 20; 197 gl.readPixels(xrViewport.x + xrViewport.width / 2, xrViewport.y + xrViewport.height/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 198 if (pixels[0] == 0xFF && pixels[1] == 0xFF && pixels[2] == 0xFF) { // white? 199 // PASSED. 200 } else if (pixels[0] == 0x0 && pixels[1] == 0xFF && pixels[2] == 0x0) { // green? 201 reject("Failed, green detected, must be white"); 202 } else { 203 reject("Failed, readPixels (2) didn't work, gl error = " + gl.getError() + ", pixel = " +pixels[0] + " " +pixels[1] + " " +pixels[2]); 204 } 205 206 // Finished. 207 resolve(); 208 }); 209 }); 210 211 const gl_props = { antialias: false, alpha: false, stencil: true, depth: true }; 212 213 xr_session_promise_test( 214 nonImmersiveTestName, testFunction, fakeDeviceInitParams, 'inline', {}, {}, gl_props, gl_props); 215 216 xr_session_promise_test( 217 immersiveTestName, testFunction, fakeDeviceInitParams, 'immersive-vr', {}, {}, gl_props, gl_props); 218 219 </script>