| <!-- |
| /* |
| ** Copyright (c) 2015 The Khronos Group Inc. |
| ** |
| ** Permission is hereby granted, free of charge, to any person obtaining a |
| ** copy of this software and/or associated documentation files (the |
| ** "Materials"), to deal in the Materials without restriction, including |
| ** without limitation the rights to use, copy, modify, merge, publish, |
| ** distribute, sublicense, and/or sell copies of the Materials, and to |
| ** permit persons to whom the Materials are furnished to do so, subject to |
| ** the following conditions: |
| ** |
| ** The above copyright notice and this permission notice shall be included |
| ** in all copies or substantial portions of the Materials. |
| ** |
| ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. |
| */ |
| --> |
| |
| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>WebGL EXT_disjoint_timer_query Conformance Tests</title> |
| <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
| <script src="../../js/js-test-pre.js"></script> |
| <script src="../../js/webgl-test-utils.js"></script> |
| </head> |
| <body> |
| <div id="description"></div> |
| <canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> |
| <div id="console"></div> |
| |
| <script> |
| "use strict"; |
| description("This test verifies the functionality of the EXT_disjoint_timer_query extension, if it is available."); |
| |
| var wtu = WebGLTestUtils; |
| var canvas = document.getElementById("canvas"); |
| var gl = wtu.create3DContext(canvas); |
| var ext = null; |
| var query = null; |
| var query2 = null; |
| var elapsed_query = null; |
| var timestamp_query1 = null; |
| var timestamp_query2 = null; |
| var availability_retry = 500; |
| var timestamp_counter_bits = 0; |
| |
| if (!gl) { |
| testFailed("WebGL context does not exist"); |
| finishTest(); |
| } else { |
| testPassed("WebGL context exists"); |
| |
| // Query the extension and store globally so shouldBe can access it |
| ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_disjoint_timer_query"); |
| if (!ext) { |
| testPassed("No EXT_disjoint_timer_query support -- this is legal"); |
| finishTest(); |
| } else { |
| if (wtu.getDefault3DContextVersion() > 1) { |
| testFailed("EXT_disjoint_timer_query must not be advertised on WebGL 2.0 contexts"); |
| finishTest(); |
| } else { |
| runSanityTests(); |
| |
| // Clear disjoint value. |
| gl.getParameter(ext.GPU_DISJOINT_EXT); |
| |
| runElapsedTimeTest(); |
| timestamp_counter_bits = ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT); |
| if (timestamp_counter_bits > 0) { |
| runTimeStampTest(); |
| } |
| verifyQueryResultsNotAvailable(); |
| |
| window.requestAnimationFrame(checkQueryResults); |
| } |
| } |
| } |
| |
| function runSanityTests() { |
| debug(""); |
| debug("Testing timer query expectations"); |
| |
| shouldBe("ext.QUERY_COUNTER_BITS_EXT", "0x8864"); |
| shouldBe("ext.CURRENT_QUERY_EXT", "0x8865"); |
| shouldBe("ext.QUERY_RESULT_EXT", "0x8866"); |
| shouldBe("ext.QUERY_RESULT_AVAILABLE_EXT", "0x8867"); |
| shouldBe("ext.TIME_ELAPSED_EXT", "0x88BF"); |
| shouldBe("ext.TIMESTAMP_EXT", "0x8E28"); |
| shouldBe("ext.GPU_DISJOINT_EXT", "0x8FBB"); |
| |
| shouldBe("ext.isQueryEXT(null)", "false"); |
| |
| shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT) === null"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT) === null"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| // Certain drivers set timestamp counter bits to 0 as they don't support timestamps |
| shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30 || " + |
| "ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) === 0"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing time elapsed query lifecycle"); |
| query = ext.createQueryEXT(); |
| shouldBe("ext.isQueryEXT(query)", "false"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query creation must succeed."); |
| shouldThrow("ext.beginQueryEXT(ext.TIMESTAMP_EXT, null)"); |
| ext.beginQueryEXT(ext.TIMESTAMP_EXT, query); |
| wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Beginning a timestamp query should fail."); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); |
| shouldBe("ext.isQueryEXT(query)", "true"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Beginning an inactive time elapsed query should succeed."); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to begin an active query should fail."); |
| ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability of an active query should fail."); |
| ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result of an active query should fail."); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); |
| ext.endQueryEXT(ext.TIME_ELAPSED_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Ending an active time elapsed query should succeed."); |
| shouldThrow("ext.getQueryObjectEXT(null, ext.QUERY_RESULT_AVAILABLE_EXT)"); |
| ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Fetching query result availability after query end should succeed."); |
| ext.endQueryEXT(ext.TIME_ELAPSED_EXT); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to end an inactive query should fail."); |
| ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should not be able to use time elapsed query to store a timestamp."); |
| ext.deleteQueryEXT(query); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query deletion must succeed."); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning a deleted query must fail."); |
| ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability after query deletion should fail."); |
| shouldBe("ext.isQueryEXT(query)", "false"); |
| |
| debug(""); |
| debug("Testing timestamp counter"); |
| query = ext.createQueryEXT(); |
| shouldThrow("ext.queryCounterEXT(null, ext.TIMESTAMP_EXT)"); |
| ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp counter queries should work."); |
| ext.deleteQueryEXT(query); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Performing parameter sanity checks"); |
| gl.getParameter(ext.TIMESTAMP_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter timestamp calls should work."); |
| gl.getParameter(ext.GPU_DISJOINT_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter disjoint calls should work."); |
| |
| debug(""); |
| debug("Testing current query conditions"); |
| query = ext.createQueryEXT(); |
| query2 = ext.createQueryEXT(); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing failed begin query should not change the current query."); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning an elapsed query without ending should fail."); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing beginning a timestamp query is invalid and should not change the elapsed query."); |
| ext.beginQueryEXT(ext.TIMESTAMP_EXT, query2) |
| wtu.glErrorShouldBe(gl, gl.INVALID_ENUM); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing timestamp queries end immediately so are never current."); |
| ext.queryCounterEXT(query2, ext.TIMESTAMP_EXT); |
| shouldBe("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT)", "null"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing ending the query should clear the current query."); |
| ext.endQueryEXT(ext.TIME_ELAPSED_EXT); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| debug(""); |
| debug("Testing beginning a elapsed query using a timestamp query should fail and not affect current query.") |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); |
| wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Switching query targets should fail."); |
| shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR); |
| |
| ext.deleteQueryEXT(query); |
| ext.deleteQueryEXT(query2); |
| |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at end of sanity tests"); |
| } |
| |
| function runElapsedTimeTest() { |
| debug(""); |
| debug("Testing elapsed time query"); |
| |
| elapsed_query = ext.createQueryEXT(); |
| ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, elapsed_query); |
| gl.clearColor(0, 0, 1, 1); |
| gl.clear(gl.COLOR_BUFFER_BIT); |
| ext.endQueryEXT(ext.TIME_ELAPSED_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Time elapsed query should have no errors"); |
| } |
| |
| function runTimeStampTest() { |
| debug(""); |
| debug("Testing timestamp query"); |
| |
| timestamp_query1 = ext.createQueryEXT(); |
| timestamp_query2 = ext.createQueryEXT(); |
| ext.queryCounterEXT(timestamp_query1, ext.TIMESTAMP_EXT); |
| gl.clearColor(1, 0, 0, 1); |
| gl.clear(gl.COLOR_BUFFER_BIT); |
| ext.queryCounterEXT(timestamp_query2, ext.TIMESTAMP_EXT); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp queries should have no errors"); |
| } |
| |
| function verifyQueryResultsNotAvailable() { |
| debug(""); |
| debug("Verifying queries' results don't become available too early"); |
| |
| // Verify as best as possible that the implementation doesn't |
| // allow a query's result to become available the same frame, by |
| // spin-looping for some time and ensuring that none of the |
| // queries' results become available. |
| var startTime = Date.now(); |
| while (Date.now() - startTime < 2000) { |
| gl.finish(); |
| if (ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)) { |
| testFailed("One of the queries' results became available too early"); |
| return; |
| } |
| if (timestamp_counter_bits > 0) { |
| if (ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT) || |
| ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)) { |
| testFailed("One of the queries' results became available too early"); |
| return; |
| } |
| } |
| } |
| |
| testPassed("Queries' results didn't become available in a spin loop"); |
| } |
| |
| function checkQueryResults() { |
| if (availability_retry > 0) { |
| // Make a reasonable attempt to wait for the queries' results to become available. |
| if (!ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT) || |
| (timestamp_counter_bits > 0 && !ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT))) { |
| var error = gl.getError(); |
| if (error != gl.NO_ERROR) { |
| testFailed("getQueryObjectEXT should have no errors: " + wtu.glEnumToString(gl, error)); |
| debug(""); |
| finishTest(); |
| return; |
| } |
| availability_retry--; |
| window.requestAnimationFrame(checkQueryResults); |
| return; |
| } |
| } |
| |
| debug(""); |
| debug("Testing query results"); |
| |
| // Make sure queries are available. |
| shouldBe("ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); |
| if (timestamp_counter_bits > 0) { |
| shouldBe("ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); |
| shouldBe("ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); |
| } |
| |
| var disjoint_value = gl.getParameter(ext.GPU_DISJOINT_EXT); |
| if (disjoint_value) { |
| // Cannot validate results make sense, but this is okay. |
| testPassed("Disjoint triggered."); |
| } else { |
| var elapsed_result = ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_EXT); |
| if (timestamp_counter_bits > 0) { |
| var timestamp_result1 = ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_EXT); |
| var timestamp_result2 = ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_EXT); |
| } |
| // Do some basic validity checking of the elapsed time query. There's no way it should |
| // take more than about half a second for a no-op query. |
| var halfSecondInNanos = 0.5 * 1000 * 1000 * 1000; |
| if (elapsed_result < 0 || elapsed_result > halfSecondInNanos) { |
| testFailed("Time elapsed query returned invalid data: " + elapsed_result); |
| } else { |
| testPassed("Time elapsed query results were valid."); |
| } |
| |
| if (timestamp_counter_bits > 0) { |
| if (timestamp_result1 <= 0 || |
| timestamp_result2 <= 0 || |
| timestamp_result2 <= timestamp_result1) { |
| testFailed("Timestamp queries returned invalid data: timestamp_result1 = " + |
| timestamp_result1 + ", timestamp_result2 = " + timestamp_result2); |
| } else { |
| testPassed("Timestamp query results were valid."); |
| } |
| } |
| } |
| |
| debug(""); |
| finishTest(); |
| } |
| </script> |
| </body> |
| </html> |