ext-disjoint-timer-query.html (15445B)
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 EXT_disjoint_timer_query Conformance Tests</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 21 <script> 22 "use strict"; 23 description("This test verifies the functionality of the EXT_disjoint_timer_query extension, if it is available."); 24 25 var wtu = WebGLTestUtils; 26 var canvas = document.getElementById("canvas"); 27 var gl = wtu.create3DContext(canvas); 28 var gl2 = null; 29 var ext = null; 30 var ext2 = null; 31 var query = null; 32 var query2 = null; 33 var elapsed_query = null; 34 var timestamp_query1 = null; 35 var timestamp_query2 = null; 36 var availability_retry = 500; 37 var timestamp_counter_bits = 0; 38 39 if (!gl) { 40 testFailed("WebGL context does not exist"); 41 finishTest(); 42 } else { 43 testPassed("WebGL context exists"); 44 45 // Query the extension and store globally so shouldBe can access it 46 ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_disjoint_timer_query"); 47 if (!ext) { 48 testPassed("No EXT_disjoint_timer_query support -- this is legal"); 49 finishTest(); 50 } else { 51 if (wtu.getDefault3DContextVersion() > 1) { 52 testFailed("EXT_disjoint_timer_query must not be advertised on WebGL 2.0 contexts"); 53 finishTest(); 54 } else { 55 runSanityTests(); 56 57 // Clear disjoint value. 58 gl.getParameter(ext.GPU_DISJOINT_EXT); 59 60 runElapsedTimeTest(); 61 timestamp_counter_bits = ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT); 62 if (timestamp_counter_bits > 0) { 63 runTimeStampTest(); 64 } 65 verifyQueryResultsNotAvailable(); 66 verifyDeleteQueryBehavior(); 67 verifyDeleteQueryErrorBehavior(); 68 69 window.requestAnimationFrame(checkQueryResults); 70 } 71 } 72 } 73 74 function runSanityTests() { 75 debug(""); 76 debug("Testing timer query expectations"); 77 78 shouldBe("ext.QUERY_COUNTER_BITS_EXT", "0x8864"); 79 shouldBe("ext.CURRENT_QUERY_EXT", "0x8865"); 80 shouldBe("ext.QUERY_RESULT_EXT", "0x8866"); 81 shouldBe("ext.QUERY_RESULT_AVAILABLE_EXT", "0x8867"); 82 shouldBe("ext.TIME_ELAPSED_EXT", "0x88BF"); 83 shouldBe("ext.TIMESTAMP_EXT", "0x8E28"); 84 shouldBe("ext.GPU_DISJOINT_EXT", "0x8FBB"); 85 86 shouldBe("ext.isQueryEXT(null)", "false"); 87 88 shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT) === null"); 89 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 90 shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30"); 91 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 92 93 shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT) === null"); 94 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 95 96 // Certain drivers set timestamp counter bits to 0 as they don't support timestamps 97 shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30 || " + 98 "ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) === 0"); 99 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 100 101 debug(""); 102 debug("Testing time elapsed query lifecycle"); 103 query = ext.createQueryEXT(); 104 shouldBe("ext.isQueryEXT(query)", "false"); 105 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query creation must succeed."); 106 shouldThrow("ext.beginQueryEXT(ext.TIMESTAMP_EXT, null)"); 107 ext.beginQueryEXT(ext.TIMESTAMP_EXT, query); 108 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Beginning a timestamp query should fail."); 109 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); 110 shouldBe("ext.isQueryEXT(query)", "true"); 111 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Beginning an inactive time elapsed query should succeed."); 112 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); 113 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to begin an active query should fail."); 114 ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); 115 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability of an active query should fail."); 116 ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); 117 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result of an active query should fail."); 118 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); 119 ext.endQueryEXT(ext.TIME_ELAPSED_EXT); 120 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Ending an active time elapsed query should succeed."); 121 shouldThrow("ext.getQueryObjectEXT(null, ext.QUERY_RESULT_AVAILABLE_EXT)"); 122 ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); 123 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Fetching query result availability after query end should succeed."); 124 ext.endQueryEXT(ext.TIME_ELAPSED_EXT); 125 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to end an inactive query should fail."); 126 ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); 127 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should not be able to use time elapsed query to store a timestamp."); 128 ext.deleteQueryEXT(query); 129 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query deletion must succeed."); 130 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); 131 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning a deleted query must fail."); 132 ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); 133 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability after query deletion should fail."); 134 shouldBe("ext.isQueryEXT(query)", "false"); 135 136 debug(""); 137 debug("Testing timestamp counter"); 138 query = ext.createQueryEXT(); 139 shouldThrow("ext.queryCounterEXT(null, ext.TIMESTAMP_EXT)"); 140 ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); 141 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp counter queries should work."); 142 ext.deleteQueryEXT(query); 143 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 144 145 debug(""); 146 debug("Performing parameter sanity checks"); 147 gl.getParameter(ext.TIMESTAMP_EXT); 148 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter timestamp calls should work."); 149 gl.getParameter(ext.GPU_DISJOINT_EXT); 150 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter disjoint calls should work."); 151 152 debug(""); 153 debug("Testing current query conditions"); 154 query = ext.createQueryEXT(); 155 query2 = ext.createQueryEXT(); 156 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); 157 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); 158 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); 159 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 160 161 debug(""); 162 debug("Testing failed begin query should not change the current query."); 163 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); 164 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning an elapsed query without ending should fail."); 165 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); 166 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 167 168 debug(""); 169 debug("Testing beginning a timestamp query is invalid and should not change the elapsed query."); 170 ext.beginQueryEXT(ext.TIMESTAMP_EXT, query2) 171 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM); 172 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); 173 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 174 175 debug(""); 176 debug("Testing timestamp queries end immediately so are never current."); 177 ext.queryCounterEXT(query2, ext.TIMESTAMP_EXT); 178 shouldBe("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT)", "null"); 179 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 180 181 debug(""); 182 debug("Testing ending the query should clear the current query."); 183 ext.endQueryEXT(ext.TIME_ELAPSED_EXT); 184 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); 185 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 186 187 debug(""); 188 debug("Testing beginning a elapsed query using a timestamp query should fail and not affect current query.") 189 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); 190 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Switching query targets should fail."); 191 shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); 192 wtu.glErrorShouldBe(gl, gl.NO_ERROR); 193 194 ext.deleteQueryEXT(query); 195 ext.deleteQueryEXT(query2); 196 197 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at end of sanity tests"); 198 } 199 200 function runElapsedTimeTest() { 201 debug(""); 202 debug("Testing elapsed time query"); 203 204 elapsed_query = ext.createQueryEXT(); 205 ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, elapsed_query); 206 gl.clearColor(0, 0, 1, 1); 207 gl.clear(gl.COLOR_BUFFER_BIT); 208 ext.endQueryEXT(ext.TIME_ELAPSED_EXT); 209 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Time elapsed query should have no errors"); 210 } 211 212 function runTimeStampTest() { 213 debug(""); 214 debug("Testing timestamp query"); 215 216 timestamp_query1 = ext.createQueryEXT(); 217 timestamp_query2 = ext.createQueryEXT(); 218 ext.queryCounterEXT(timestamp_query1, ext.TIMESTAMP_EXT); 219 gl.clearColor(1, 0, 0, 1); 220 gl.clear(gl.COLOR_BUFFER_BIT); 221 ext.queryCounterEXT(timestamp_query2, ext.TIMESTAMP_EXT); 222 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp queries should have no errors"); 223 } 224 225 function verifyQueryResultsNotAvailable() { 226 debug(""); 227 debug("Verifying queries' results don't become available too early"); 228 229 // Verify as best as possible that the implementation doesn't 230 // allow a query's result to become available the same frame, by 231 // spin-looping for some time and ensuring that none of the 232 // queries' results become available. 233 var startTime = Date.now(); 234 while (Date.now() - startTime < 2000) { 235 gl.finish(); 236 if (ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)) { 237 testFailed("One of the queries' results became available too early"); 238 return; 239 } 240 if (timestamp_counter_bits > 0) { 241 if (ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT) || 242 ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)) { 243 testFailed("One of the queries' results became available too early"); 244 return; 245 } 246 } 247 } 248 249 testPassed("Queries' results didn't become available in a spin loop"); 250 } 251 252 function verifyDeleteQueryBehavior() { 253 debug(""); 254 debug("Testing deleting an active query should end it."); 255 256 // Use a new context for this test 257 gl2 = wtu.create3DContext(null, null, 1); 258 if (!gl2) return; 259 ext2 = gl2.getExtension("EXT_disjoint_timer_query"); 260 if (!ext2) return; 261 262 query = ext2.createQueryEXT(); 263 ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query); 264 wtu.glErrorShouldBe(gl2, gl2.NONE, "The query began successfully"); 265 ext2.deleteQueryEXT(query); 266 wtu.glErrorShouldBe(gl2, gl2.NONE, "Deletion of the active query succeeds"); 267 shouldBeNull("ext2.getQueryEXT(ext2.TIME_ELAPSED_EXT, ext2.CURRENT_QUERY_EXT)"); 268 shouldBeFalse("ext2.isQueryEXT(query)"); 269 query = ext2.createQueryEXT(); 270 ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query); 271 wtu.glErrorShouldBe(gl2, gl2.NONE, "Beginning a new query succeeds"); 272 ext2.endQueryEXT(ext2.TIME_ELAPSED_EXT); 273 ext2.deleteQueryEXT(query); 274 wtu.glErrorShouldBe(gl2, gl2.NONE); 275 query = null; 276 ext2 = null; 277 gl2 = null; 278 } 279 280 function verifyDeleteQueryErrorBehavior() { 281 debug(""); 282 debug("Testing deleting a query created by another context."); 283 284 // Use new contexts for this test 285 gl2 = wtu.create3DContext(null, null, 1); 286 var gl3 = wtu.create3DContext(null, null, 1); 287 if (!gl2 || !gl3) return; 288 ext2 = gl2.getExtension("EXT_disjoint_timer_query"); 289 var ext3 = gl3.getExtension("EXT_disjoint_timer_query"); 290 if (!ext2 || !ext3) return; 291 292 query = ext2.createQueryEXT(); 293 ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query); 294 ext3.deleteQueryEXT(query); 295 wtu.glErrorShouldBe(gl3, gl3.INVALID_OPERATION); 296 shouldBeTrue("ext2.isQueryEXT(query)"); 297 shouldBe("ext2.getQueryEXT(ext2.TIME_ELAPSED_EXT, ext2.CURRENT_QUERY_EXT)", "query"); 298 ext2.endQueryEXT(ext2.TIME_ELAPSED_EXT); 299 ext2.deleteQueryEXT(query); 300 wtu.glErrorShouldBe(gl2, gl2.NONE); 301 query = null; 302 ext2 = null; 303 gl2 = null; 304 gl3 = null; 305 } 306 307 function checkQueryResults() { 308 if (availability_retry > 0) { 309 // Make a reasonable attempt to wait for the queries' results to become available. 310 if (!ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT) || 311 (timestamp_counter_bits > 0 && !ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT))) { 312 var error = gl.getError(); 313 if (error != gl.NO_ERROR) { 314 testFailed("getQueryObjectEXT should have no errors: " + wtu.glEnumToString(gl, error)); 315 debug(""); 316 finishTest(); 317 return; 318 } 319 availability_retry--; 320 window.requestAnimationFrame(checkQueryResults); 321 return; 322 } 323 } 324 325 debug(""); 326 debug("Testing query results"); 327 328 // Make sure queries are available. 329 shouldBe("ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); 330 if (timestamp_counter_bits > 0) { 331 shouldBe("ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); 332 shouldBe("ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); 333 } 334 335 var disjoint_value = gl.getParameter(ext.GPU_DISJOINT_EXT); 336 if (disjoint_value) { 337 // Cannot validate results make sense, but this is okay. 338 testPassed("Disjoint triggered."); 339 } else { 340 var elapsed_result = ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_EXT); 341 if (timestamp_counter_bits > 0) { 342 var timestamp_result1 = ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_EXT); 343 var timestamp_result2 = ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_EXT); 344 } 345 // Do some basic validity checking of the elapsed time query. There's no way it should 346 // take more than about half a second for a no-op query. 347 var halfSecondInNanos = 0.5 * 1000 * 1000 * 1000; 348 if (elapsed_result < 0 || elapsed_result > halfSecondInNanos) { 349 testFailed("Time elapsed query returned invalid data: " + elapsed_result); 350 } else { 351 testPassed("Time elapsed query results were valid."); 352 } 353 354 if (timestamp_counter_bits > 0) { 355 if (timestamp_result1 <= 0 || 356 timestamp_result2 <= 0 || 357 timestamp_result2 <= timestamp_result1) { 358 testFailed("Timestamp queries returned invalid data: timestamp_result1 = " + 359 timestamp_result1 + ", timestamp_result2 = " + timestamp_result2); 360 } else { 361 testPassed("Timestamp query results were valid."); 362 } 363 } 364 } 365 366 debug(""); 367 finishTest(); 368 } 369 </script> 370 </body> 371 </html>