Source code
Revision control
Copy as Markdown
Other Tools
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL 2 EXT_disjoint_timer_query_webgl2 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_webgl2 extension, if it is available.");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, null, 2);
var gl2 = null;
var ext = null;
var ext2 = 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_webgl2");
if (!ext) {
testPassed("No EXT_disjoint_timer_query_webgl2 support -- this is legal");
finishTest();
} else {
runSanityTests();
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
// Clear disjoint value.
gl.getParameter(ext.GPU_DISJOINT_EXT);
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
runElapsedTimeTest();
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
timestamp_counter_bits = gl.getQuery(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT);
if (timestamp_counter_bits > 0) {
runTimeStampTest();
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
}
verifyQueryResultsNotAvailable();
verifyDeleteQueryBehavior();
verifyDeleteQueryErrorBehavior();
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
window.requestAnimationFrame(checkQueryResults);
}
}
function runSanityTests() {
debug("");
debug("Testing other query types");
query = gl.createQuery();
gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
shouldBeTrue("gl.getQuery(gl.ANY_SAMPLES_PASSED, gl.CURRENT_QUERY) !== null");
gl.endQuery(gl.ANY_SAMPLES_PASSED);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "enabling EXT_disjoint_timer_query_webgl2 should not break other queries");
debug("");
debug("Testing timer query expectations");
shouldBe("ext.QUERY_COUNTER_BITS_EXT", "0x8864");
shouldBe("ext.TIME_ELAPSED_EXT", "0x88BF");
shouldBe("ext.TIMESTAMP_EXT", "0x8E28");
shouldBe("ext.GPU_DISJOINT_EXT", "0x8FBB");
shouldBe("gl.isQuery(null)", "false");
shouldBeTrue("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY) === null");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
shouldBeTrue("gl.getQuery(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
shouldBeTrue("gl.getQuery(ext.TIMESTAMP_EXT, gl.CURRENT_QUERY) === null");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
// Certain drivers set timestamp counter bits to 0 as they don't support timestamps
shouldBeTrue("gl.getQuery(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30 || " +
"gl.getQuery(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) === 0");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
debug("");
debug("Testing time elapsed query lifecycle");
query = gl.createQuery();
shouldBe("gl.isQuery(query)", "false");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query creation must succeed.");
gl.beginQuery(ext.TIMESTAMP_EXT, query);
wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Beginning a timestamp query should fail.");
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
shouldBe("gl.isQuery(query)", "true");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Beginning an inactive time elapsed query should succeed.");
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to begin an active query should fail.");
gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability of an active query should fail.");
gl.getQueryParameter(query, gl.QUERY_RESULT);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result of an active query should fail.");
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "query");
gl.endQuery(ext.TIME_ELAPSED_EXT);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Ending an active time elapsed query should succeed.");
gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Fetching query result availability after query end should succeed.");
gl.endQuery(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.");
gl.deleteQuery(query);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query deletion must succeed.");
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning a deleted query must fail.");
gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability after query deletion should fail.");
shouldBe("gl.isQuery(query)", "false");
debug("");
debug("Testing timestamp counter");
query = gl.createQuery();
shouldThrow("ext.queryCounterEXT(null, ext.TIMESTAMP_EXT)");
ext.queryCounterEXT(query, ext.TIMESTAMP_EXT);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp counter queries should work.");
gl.deleteQuery(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 = gl.createQuery();
query2 = gl.createQuery();
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "null");
gl.beginQuery(ext.TIME_ELAPSED_EXT, query);
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "query");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
debug("");
debug("Testing failed begin query should not change the current query.");
gl.beginQuery(ext.TIME_ELAPSED_EXT, query2);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning an elapsed query without ending should fail.");
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "query");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
debug("");
debug("Testing beginning a timestamp query is invalid and should not change the elapsed query.");
gl.beginQuery(ext.TIMESTAMP_EXT, query2)
wtu.glErrorShouldBe(gl, gl.INVALID_ENUM);
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "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("gl.getQuery(ext.TIMESTAMP_EXT, gl.CURRENT_QUERY)", "null");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
debug("");
debug("Testing ending the query should clear the current query.");
gl.endQuery(ext.TIME_ELAPSED_EXT);
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "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.")
gl.beginQuery(ext.TIME_ELAPSED_EXT, query2);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Switching query targets should fail.");
shouldBe("gl.getQuery(ext.TIME_ELAPSED_EXT, gl.CURRENT_QUERY)", "null");
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
gl.deleteQuery(query);
gl.deleteQuery(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 = gl.createQuery();
gl.beginQuery(ext.TIME_ELAPSED_EXT, elapsed_query);
gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.endQuery(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 = gl.createQuery();
timestamp_query2 = gl.createQuery();
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 (gl.getQueryParameter(elapsed_query, gl.QUERY_RESULT_AVAILABLE)) {
testFailed("One of the queries' results became available too early");
return;
}
if (timestamp_counter_bits > 0) {
if (gl.getQueryParameter(timestamp_query1, gl.QUERY_RESULT_AVAILABLE) ||
gl.getQueryParameter(timestamp_query2, gl.QUERY_RESULT_AVAILABLE)) {
testFailed("One of the queries' results became available too early");
return;
}
}
}
testPassed("Queries' results didn't become available in a spin loop");
}
function verifyDeleteQueryBehavior() {
debug("");
debug("Testing deleting an active query should end it.");
// Use a new context for this test
gl2 = wtu.create3DContext(null, null, 2);
if (!gl2) return;
ext2 = gl2.getExtension("EXT_disjoint_timer_query_webgl2");
if (!ext2) return;
query = gl2.createQuery();
gl2.beginQuery(ext.TIME_ELAPSED_EXT, query);
wtu.glErrorShouldBe(gl2, gl2.NONE, "The query began successfully");
gl2.deleteQuery(query);
wtu.glErrorShouldBe(gl2, gl2.NONE, "Deletion of the active query succeeds");
shouldBeNull("gl2.getQuery(ext2.TIME_ELAPSED_EXT, gl2.CURRENT_QUERY)");
shouldBeFalse("gl2.isQuery(query)");
query = gl2.createQuery();
gl2.beginQuery(ext2.TIME_ELAPSED_EXT, query);
wtu.glErrorShouldBe(gl, gl2.NONE, "Beginning a new query succeeds");
gl2.endQuery(gl2.TIME_ELAPSED_EXT);
gl2.deleteQuery(query);
wtu.glErrorShouldBe(gl, gl.NONE);
query = null;
ext2 = null;
gl2 = null;
}
function verifyDeleteQueryErrorBehavior() {
debug("");
debug("Testing deleting a query created by another context.");
// Use new contexts for this test
gl2 = wtu.create3DContext(null, null, 2);
var gl3 = wtu.create3DContext(null, null, 2);
if (!gl2 || !gl3) return;
ext2 = gl2.getExtension("EXT_disjoint_timer_query_webgl2");
if (!ext2) return;
query = gl2.createQuery();
gl2.beginQuery(ext2.TIME_ELAPSED_EXT, query);
gl3.deleteQuery(query);
wtu.glErrorShouldBe(gl3, gl3.INVALID_OPERATION);
shouldBeTrue("gl2.isQuery(query)");
shouldBe("gl2.getQuery(ext2.TIME_ELAPSED_EXT, gl2.CURRENT_QUERY)", "query");
gl2.endQuery(ext2.TIME_ELAPSED_EXT);
gl2.deleteQuery(query);
wtu.glErrorShouldBe(gl2, gl2.NONE);
query = null;
ext2 = null;
gl2 = null;
gl3 = null;
}
function checkQueryResults() {
if (availability_retry > 0) {
// Make a reasonable attempt to wait for the queries' results to become available.
if (!gl.getQueryParameter(elapsed_query, gl.QUERY_RESULT_AVAILABLE) ||
(timestamp_counter_bits > 0 && !gl.getQueryParameter(timestamp_query2, gl.QUERY_RESULT_AVAILABLE))) {
var error = gl.getError();
if (error != gl.NO_ERROR) {
testFailed("getQueryParameter 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("gl.getQueryParameter(elapsed_query, gl.QUERY_RESULT_AVAILABLE)", "true");
if (timestamp_counter_bits > 0) {
shouldBe("gl.getQueryParameter(timestamp_query1, gl.QUERY_RESULT_AVAILABLE)", "true");
shouldBe("gl.getQueryParameter(timestamp_query2, gl.QUERY_RESULT_AVAILABLE)", "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 = gl.getQueryParameter(elapsed_query, gl.QUERY_RESULT);
if (timestamp_counter_bits > 0) {
var timestamp_result1 = gl.getQueryParameter(timestamp_query1, gl.QUERY_RESULT);
var timestamp_result2 = gl.getQueryParameter(timestamp_query2, gl.QUERY_RESULT);
}
// 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>