switching-objects.html (9317B)
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>Switching transform feedback objects</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 <canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> 19 <div id="console"></div> 20 <script id="vshader" type="x-shader/x-vertex">#version 300 es 21 in float in_value; 22 out float out_value1; 23 out float out_value2; 24 25 void main() { 26 out_value1 = in_value * 2.; 27 out_value2 = in_value * 4.; 28 } 29 </script> 30 <script id="fshader" type="x-shader/x-fragment">#version 300 es 31 precision mediump float; 32 out vec4 dummy; 33 void main() { 34 dummy = vec4(0.); 35 } 36 </script> 37 <script> 38 "use strict"; 39 description("Tests switching transform feedback objects."); 40 41 debug("<h3>Setup</h3>") 42 43 var wtu = WebGLTestUtils; 44 var canvas = document.getElementById("canvas"); 45 var gl = wtu.create3DContext(canvas, null, 2); 46 if (!gl) { 47 testFailed("WebGL context does not exist"); 48 } 49 50 // Setup 51 const prog_interleaved = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"], 52 ["out_value1", "out_value2"], gl.INTERLEAVED_ATTRIBS, 53 ["in_value"]); 54 const prog_no_varyings = wtu.setupTransformFeedbackProgram(gl, ["vshader", "fshader"], 55 [], gl.INTERLEAVED_ATTRIBS, 56 ["in_value"]); 57 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "shader compilation"); 58 const vertexBuffer = createBuffer(gl, new Float32Array([1, 2, 3, 4])); 59 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 60 gl.enableVertexAttribArray(0); 61 gl.vertexAttribPointer(0, 1, gl.FLOAT, false, 0, 0); 62 gl.useProgram(prog_interleaved); 63 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertex buffer and program setup"); 64 65 const tf1 = gl.createTransformFeedback(); 66 const tf2 = gl.createTransformFeedback(); 67 const tfBuffer1 = createBuffer(gl, new Float32Array([0, 0])); 68 const tfBuffer2 = createBuffer(gl, new Float32Array([0, 0])); 69 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1); 70 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer1); 71 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 72 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer2); 73 const expected_tf_output = [2, 4]; 74 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TF object setup"); 75 76 debug("<h3>Baseline transform feedback success case</h3>"); 77 78 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1); 79 gl.beginTransformFeedback(gl.POINTS); 80 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin TF"); 81 gl.drawArrays(gl.POINTS, 0, 1); 82 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "draw"); 83 gl.endTransformFeedback(); 84 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end TF"); 85 86 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1); 87 wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output); 88 89 debug("<h3>Generic binding is not changed when switching TF object</h3>"); 90 91 // According to the GL ES spec historically, TRANSFORM_FEEDBACK_BUFFER_BINDING is listed as part 92 // of the transform feedback object state. However, many drivers treat it as global context state 93 // and not part of the tranform feedback object, which means that it does not change when 94 // bindTransformFeedback is called. Khronos has resolved to change the spec to specify the latter 95 // behavior: https://gitlab.khronos.org/opengl/API/issues/66 (Khronos private link). This tests 96 // for the new behavior. 97 98 // Set each buffer to contain its buffer number. We use this to check which 99 // buffer is *really* bound at the driver level by reading the buffer contents. 100 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); 101 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1); 102 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([1]), gl.STREAM_READ); 103 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2); 104 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([2]), gl.STREAM_READ); 105 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferData"); 106 107 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1); 108 checkParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, tfBuffer2); 109 checkIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, tfBuffer1); 110 wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, [2]); 111 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readback"); 112 113 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, null); 114 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 115 checkParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, null); 116 checkIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, tfBuffer2); 117 118 debug("<h3>Error switching TF object while TF is enabled</h3>"); 119 120 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1); 121 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1); 122 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ); 123 gl.beginTransformFeedback(gl.POINTS); 124 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin"); 125 checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1); 126 127 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 128 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bind while unpaused"); 129 130 // Check that nothing actually changed and rendering still works 131 checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1); 132 gl.drawArrays(gl.POINTS, 0, 1); 133 gl.endTransformFeedback(); 134 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should complete successfully"); 135 wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output); 136 137 138 debug("<h3>Successfully switching TF object while TF is paused</h3>"); 139 140 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1); 141 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ); 142 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2); 143 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, new Float32Array([0, 0]), gl.STREAM_READ); 144 145 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 146 gl.beginTransformFeedback(gl.POINTS); 147 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin on tf2"); 148 checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf2); 149 150 gl.pauseTransformFeedback(); 151 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf1); 152 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bind while paused"); 153 gl.beginTransformFeedback(gl.POINTS); 154 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "begin on tf1"); 155 checkParameter(gl.TRANSFORM_FEEDBACK_BINDING, tf1); 156 gl.drawArrays(gl.POINTS, 0, 1); 157 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "draw should succeed"); 158 gl.endTransformFeedback(); 159 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end on tf1"); 160 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer1); 161 wtu.checkFloatBuffer(gl, gl.TRANSFORM_FEEDBACK_BUFFER, expected_tf_output); 162 163 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 164 gl.endTransformFeedback(); 165 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end on tf2"); 166 167 debug("<h3>Misc. invalid operations</h3>") 168 169 gl.endTransformFeedback(); 170 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "endTransformFeedback before begin"); 171 gl.pauseTransformFeedback(); 172 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "pauseTransformFeedback when not active"); 173 gl.resumeTransformFeedback(); 174 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "pauseTransformFeedback when not active"); 175 176 gl.beginTransformFeedback(gl.POINTS); 177 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "transform feedback should begin successfully"); 178 gl.drawArrays(gl.TRIANGLE_STRIP, 0, 1); 179 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "wrong primitive mode"); 180 gl.useProgram(prog_no_varyings); 181 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "switch program while active"); 182 gl.resumeTransformFeedback(); 183 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "resumeTransformFeedback when not paused"); 184 gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf2); 185 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bindTransformFeedback when active"); 186 gl.bindBuffer(gl.TRANSFORM_FEEDBACK_BUFFER, tfBuffer2); 187 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bindBuffer(TRANSFORM_FEEDBACK_BUFFER) when active"); 188 gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, tfBuffer2); 189 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bindBufferBase(TRANSFORM_FEEDBACK_BUFFER) when active"); 190 191 gl.pauseTransformFeedback(); 192 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "pause"); 193 gl.pauseTransformFeedback(); 194 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "already paused"); 195 gl.endTransformFeedback(); 196 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "end while paused"); 197 198 finishTest(); 199 200 // Helper functions 201 function createBuffer(gl, dataOrSize) { 202 const buf = gl.createBuffer(); 203 gl.bindBuffer(gl.ARRAY_BUFFER, buf); 204 gl.bufferData(gl.ARRAY_BUFFER, dataOrSize, gl.STATIC_DRAW); 205 gl.bindBuffer(gl.ARRAY_BUFFER, null); 206 return buf; 207 } 208 209 function checkParameter(param, expected) { 210 const value = gl.getParameter(param); 211 if (value != expected) { 212 testFailed(wtu.glEnumToString(gl, param) + " was " + value + ", but expected " + expected); 213 } else { 214 testPassed(wtu.glEnumToString(gl, param) + " was " + value + ", matching expected " + expected); 215 } 216 } 217 218 function checkIndexedParameter(param, index, expected) { 219 const value = gl.getIndexedParameter(param, index); 220 if (value != expected) { 221 testFailed(wtu.glEnumToString(gl, param) + "[" + index + "] was " + value + ", but expected " + expected); 222 } else { 223 testPassed(wtu.glEnumToString(gl, param) + "[" + index + "] was " + value + ", matching expected " + expected); 224 } 225 } 226 227 228 </script> 229 230 </body> 231 </html>