Name | |
ANGLE_timer_query | |
Name Strings | |
GL_ANGLE_timer_query | |
Contributors | |
Contributors to ARB_occlusion_query | |
Contributors to EXT_timer_query | |
Contributors to ARB_timer_query | |
Ben Vanik, Google Inc. | |
Daniel Koch, TransGaming Inc. | |
Contact | |
Ben Vanik, Google Inc. (benvanik 'at' google 'dot' com) | |
Status | |
Draft | |
Version | |
Last Modified Date: Apr 28, 2011 | |
Author Revision: 1 | |
Number | |
OpenGL ES Extension #?? | |
Dependencies | |
OpenGL ES 2.0 is required. | |
The extension is written against the OpenGL ES 2.0 specification. | |
Overview | |
Applications can benefit from accurate timing information in a number of | |
different ways. During application development, timing information can | |
help identify application or driver bottlenecks. At run time, | |
applications can use timing information to dynamically adjust the amount | |
of detail in a scene to achieve constant frame rates. OpenGL | |
implementations have historically provided little to no useful timing | |
information. Applications can get some idea of timing by reading timers | |
on the CPU, but these timers are not synchronized with the graphics | |
rendering pipeline. Reading a CPU timer does not guarantee the completion | |
of a potentially large amount of graphics work accumulated before the | |
timer is read, and will thus produce wildly inaccurate results. | |
glFinish() can be used to determine when previous rendering commands have | |
been completed, but will idle the graphics pipeline and adversely affect | |
application performance. | |
This extension provides a query mechanism that can be used to determine | |
the amount of time it takes to fully complete a set of GL commands, and | |
without stalling the rendering pipeline. It uses the query object | |
mechanisms first introduced in the occlusion query extension, which allow | |
time intervals to be polled asynchronously by the application. | |
IP Status | |
No known IP claims. | |
New Procedures and Functions | |
void GenQueriesANGLE(sizei n, uint *ids); | |
void DeleteQueriesANGLE(sizei n, const uint *ids); | |
boolean IsQueryANGLE(uint id); | |
void BeginQueryANGLE(enum target, uint id); | |
void EndQueryANGLE(enum target); | |
void QueryCounterANGLE(uint id, enum target); | |
void GetQueryivANGLE(enum target, enum pname, int *params); | |
void GetQueryObjectivANGLE(uint id, enum pname, int *params); | |
void GetQueryObjectuivANGLE(uint id, enum pname, uint *params); | |
void GetQueryObjecti64vANGLE(uint id, enum pname, int64 *params); | |
void GetQueryObjectui64vANGLE(uint id, enum pname, uint64 *params); | |
New Tokens | |
Accepted by the <pname> parameter of GetQueryivANGLE: | |
QUERY_COUNTER_BITS_ANGLE 0x8864 | |
CURRENT_QUERY_ANGLE 0x8865 | |
Accepted by the <pname> parameter of GetQueryObjectivANGLE, | |
GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, and | |
GetQueryObjectui64vANGLE: | |
QUERY_RESULT_ANGLE 0x8866 | |
QUERY_RESULT_AVAILABLE_ANGLE 0x8867 | |
Accepted by the <target> parameter of BeginQueryANGLE, EndQueryANGLE, and | |
GetQueryivANGLE: | |
TIME_ELAPSED_ANGLE 0x88BF | |
Accepted by the <target> parameter of GetQueryivANGLE and | |
QueryCounterANGLE: | |
TIMESTAMP_ANGLE 0x8E28 | |
Additions to Chapter 2 of the OpenGL ES 2.0 Specification (OpenGL ES Operation) | |
(Modify table 2.1, Correspondence of command suffix letters to GL argument) | |
Add two new types: | |
Letter Corresponding GL Type | |
------ --------------------- | |
i64 int64ANGLE | |
ui64 uint64ANGLE | |
(Modify table 2.2, GL data types) Add two new types: | |
GL Type Minimum Bit Width Description | |
------- ----------------- ----------------------------- | |
int64ANGLE 64 Signed 2's complement integer | |
uint64ANGLE 64 Unsigned binary integer | |
Additions to Chapter 5 of the OpenGL ES 2.0 Specification (Special Functions) | |
Add a new section 5.3 "Timer Queries": | |
"5.3 Timer Queries | |
Timer queries use query objects to track the amount of time needed to | |
fully complete a set of GL commands, or to determine the current time | |
of the GL. | |
Timer queries are associated with query objects. The command | |
void GenQueriesANGLE(sizei n, uint *ids); | |
returns <n> previously unused query object names in <ids>. These | |
names are marked as used, but no object is associated with them until | |
the first time they are used by BeginQueryANGLE. Query objects contain | |
one piece of state, an integer result value. This result value is | |
initialized to zero when the object is created. Any positive integer | |
except for zero (which is reserved for the GL) is a valid query | |
object name. | |
Query objects are deleted by calling | |
void DeleteQueriesANGLE(sizei n, const uint *ids); | |
<ids> contains <n> names of query objects to be deleted. After a | |
query object is deleted, its name is again unused. Unused names in | |
<ids> are silently ignored. | |
If an active query object is deleted its name immediately becomes unused, | |
but the underlying object is not deleted until it is no longer active. | |
A timer query can be started and finished by calling | |
void BeginQueryANGLE(enum target, uint id); | |
void EndQueryANGLE(enum target); | |
where <target> is TIME_ELAPSED_ANGLE. If BeginQueryANGLE is called | |
with an unused <id>, that name is marked as used and associated with | |
a new query object. | |
If BeginQueryANGLE is called with an <id> of zero, if the active query | |
object name for <target> is non-zero, if <id> is the name of an existing | |
query object whose type does not match <target>, or if <id> is the active | |
query object name for any query type, the error INVALID_OPERATION is | |
generated. If EndQueryANGLE is called while no query with the same target | |
is in progress, an INVALID_OPERATION error is generated. | |
When BeginQueryANGLE and EndQueryANGLE are called with a <target> of | |
TIME_ELAPSED_ANGLE, the GL prepares to start and stop the timer used for | |
timer queries. The timer is started or stopped when the effects from all | |
previous commands on the GL client and server state and the framebuffer | |
have been fully realized. The BeginQueryANGLE and EndQueryANGLE commands | |
may return before the timer is actually started or stopped. When the timer | |
query timer is finally stopped, the elapsed time (in nanoseconds) is | |
written to the corresponding query object as the query result value, and | |
the query result for that object is marked as available. | |
If the elapsed time overflows the number of bits, <n>, available to hold | |
elapsed time, its value becomes undefined. It is recommended, but not | |
required, that implementations handle this overflow case by saturating at | |
2^n - 1. | |
The necessary state is a single bit indicating whether an timer | |
query is active, the identifier of the currently active timer | |
query, and a counter keeping track of the time that has passed. | |
When the command | |
void QueryCounterANGLE(uint id, enum target); | |
is called with <target> TIMESTAMP_ANGLE, the GL records the current time | |
into the corresponding query object. The time is recorded after all | |
previous commands on the GL client and server state and the framebuffer | |
have been fully realized. When the time is recorded, the query result for | |
that object is marked available. QueryCounterANGLE timer queries can be | |
used within a BeginQueryANGLE / EndQueryANGLE block where the <target> is | |
TIME_ELAPSED_ANGLE and it does not affect the result of that query object. | |
The error INVALID_OPERATION is generated if the <id> is already in use | |
within a BeginQueryANGLE/EndQueryANGLE block." | |
Additions to Chapter 6 of the OpenGL ES 2.0 Specification (State and State | |
Requests) | |
Add a new section 6.1.9 "Timer Queries": | |
"The command | |
boolean IsQueryANGLE(uint id); | |
returns TRUE if <id> is the name of a query object. If <id> is zero, | |
or if <id> is a non-zero value that is not the name of a query | |
object, IsQueryANGLE returns FALSE. | |
Information about a query target can be queried with the command | |
void GetQueryivANGLE(enum target, enum pname, int *params); | |
<target> identifies the query target and can be TIME_ELAPSED_ANGLE or | |
TIMESTAMP_ANGLE for timer queries. | |
If <pname> is CURRENT_QUERY_ANGLE, the name of the currently active query | |
for <target>, or zero if no query is active, will be placed in <params>. | |
If <pname> is QUERY_COUNTER_BITS_ANGLE, the implementation-dependent number | |
of bits used to hold the query result for <target> will be placed in | |
<params>. The number of query counter bits may be zero, in which case | |
the counter contains no useful information. | |
For timer queries (TIME_ELAPSED_ANGLE and TIMESTAMP_ANGLE), if the number | |
of bits is non-zero, the minimum number of bits allowed is 30 which | |
will allow at least 1 second of timing. | |
The state of a query object can be queried with the commands | |
void GetQueryObjectivANGLE(uint id, enum pname, int *params); | |
void GetQueryObjectuivANGLE(uint id, enum pname, uint *params); | |
void GetQueryObjecti64vANGLE(uint id, enum pname, int64 *params); | |
void GetQueryObjectui64vANGLE(uint id, enum pname, uint64 *params); | |
If <id> is not the name of a query object, or if the query object | |
named by <id> is currently active, then an INVALID_OPERATION error is | |
generated. | |
If <pname> is QUERY_RESULT_ANGLE, then the query object's result | |
value is returned as a single integer in <params>. If the value is so | |
large in magnitude that it cannot be represented with the requested type, | |
then the nearest value representable using the requested type is | |
returned. If the number of query counter bits for target is zero, then | |
the result is returned as a single integer with the value zero. | |
There may be an indeterminate delay before the above query returns. If | |
<pname> is QUERY_RESULT_AVAILABLE_ANGLE, FALSE is returned if such a delay | |
would be required; otherwise TRUE is returned. It must always be true | |
that if any query object returns a result available of TRUE, all queries | |
of the same type issued prior to that query must also return TRUE. | |
Querying the state for a given timer query forces that timer query to | |
complete within a finite amount of time. | |
If multiple queries are issued on the same target and id prior to | |
calling GetQueryObject[u]i[64]vANGLE, the result returned will always be | |
from the last query issued. The results from any queries before the | |
last one will be lost if the results are not retrieved before starting | |
a new query on the same <target> and <id>." | |
Errors | |
The error INVALID_VALUE is generated if GenQueriesANGLE is called where | |
<n> is negative. | |
The error INVALID_VALUE is generated if DeleteQueriesANGLE is called | |
where <n> is negative. | |
The error INVALID_OPERATION is generated if BeginQueryANGLE is called | |
when a query of the given <target> is already active. | |
The error INVALID_OPERATION is generated if EndQueryANGLE is called | |
when a query of the given <target> is not active. | |
The error INVALID_OPERATION is generated if BeginQueryANGLE is called | |
where <id> is zero. | |
The error INVALID_OPERATION is generated if BeginQueryANGLE is called | |
where <id> is the name of a query currently in progress. | |
The error INVALID_OPERATION is generated if BeginQueryANGLE is called | |
where <id> is the name of an existing query object whose type does not | |
match <target>. | |
The error INVALID_ENUM is generated if BeginQueryANGLE or EndQueryANGLE | |
is called where <target> is not TIME_ELAPSED_ANGLE. | |
The error INVALID_ENUM is generated if GetQueryivANGLE is called where | |
<target> is not TIME_ELAPSED_ANGLE or TIMESTAMP_ANGLE. | |
The error INVALID_ENUM is generated if GetQueryivANGLE is called where | |
<pname> is not QUERY_COUNTER_BITS_ANGLE or CURRENT_QUERY_ANGLE. | |
The error INVALID_ENUM is generated if QueryCounterANGLE is called where | |
<target> is not TIMESTAMP_ANGLE. | |
The error INVALID_OPERATION is generated if QueryCounterANGLE is called | |
on a query object that is already in use inside a | |
BeginQueryANGLE/EndQueryANGLE. | |
The error INVALID_OPERATION is generated if GetQueryObjectivANGLE, | |
GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or | |
GetQueryObjectui64vANGLE is called where <id> is not the name of a query | |
object. | |
The error INVALID_OPERATION is generated if GetQueryObjectivANGLE, | |
GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or | |
GetQueryObjectui64vANGLE is called where <id> is the name of a currently | |
active query object. | |
The error INVALID_ENUM is generated if GetQueryObjectivANGLE, | |
GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or | |
GetQueryObjectui64vANGLE is called where <pname> is not | |
QUERY_RESULT_ANGLE or QUERY_RESULT_AVAILABLE_ANGLE. | |
New State | |
(Add a new table 6.xx, "Query Operations") | |
Get Value Type Get Command Initial Value Description Sec | |
--------- ---- ----------- ------------- ----------- ------ | |
- B - FALSE query active 5.3 | |
CURRENT_QUERY_ANGLE Z+ GetQueryivANGLE 0 active query ID 5.3 | |
QUERY_RESULT_ANGLE Z+ GetQueryObjectuivANGLE, 0 samples-passed count 5.3 | |
GetQueryObjectui64vANGLE | |
QUERY_RESULT_AVAILABLE_ANGLE B GetQueryObjectivANGLE FALSE query result available 5.3 | |
New Implementation Dependent State | |
(Add the following entry to table 6.18): | |
Get Value Type Get Command Minimum Value Description Sec | |
-------------------------- ---- ----------- ------------- ---------------- ------ | |
QUERY_COUNTER_BITS_ANGLE Z+ GetQueryivANGLE see 6.1.9 Number of bits in 6.1.9 | |
query counter | |
Examples | |
(1) Here is some rough sample code that demonstrates the intended usage | |
of this extension. | |
GLint queries[N]; | |
GLint available = 0; | |
// timer queries can contain more than 32 bits of data, so always | |
// query them using the 64 bit types to avoid overflow | |
GLuint64ANGLE timeElapsed = 0; | |
// Create a query object. | |
glGenQueriesANGLE(N, queries); | |
// Start query 1 | |
glBeginQueryANGLE(GL_TIME_ELAPSED_ANGLE, queries[0]); | |
// Draw object 1 | |
.... | |
// End query 1 | |
glEndQueryANGLE(GL_TIME_ELAPSED_ANGLE); | |
... | |
// Start query N | |
glBeginQueryANGLE(GL_TIME_ELAPSED_ANGLE, queries[N-1]); | |
// Draw object N | |
.... | |
// End query N | |
glEndQueryANGLE(GL_TIME_ELAPSED_ANGLE); | |
// Wait for all results to become available | |
while (!available) { | |
glGetQueryObjectivANGLE(queries[N-1], GL_QUERY_RESULT_AVAILABLE_ANGLE, &available); | |
} | |
for (i = 0; i < N; i++) { | |
// See how much time the rendering of object i took in nanoseconds. | |
glGetQueryObjectui64vANGLE(queries[i], GL_QUERY_RESULT_ANGLE, &timeElapsed); | |
// Do something useful with the time. Note that care should be | |
// taken to use all significant bits of the result, not just the | |
// least significant 32 bits. | |
AdjustObjectLODBasedOnDrawTime(i, timeElapsed); | |
} | |
This example is sub-optimal in that it stalls at the end of every | |
frame to wait for query results. Ideally, the collection of results | |
would be delayed one frame to minimize the amount of time spent | |
waiting for the GPU to finish rendering. | |
(2) This example is basically the same as the example above but uses | |
QueryCounter instead. | |
GLint queries[N+1]; | |
GLint available = 0; | |
// timer queries can contain more than 32 bits of data, so always | |
// query them using the 64 bit types to avoid overflow | |
GLuint64ANGLE timeStart, timeEnd, timeElapsed = 0; | |
// Create a query object. | |
glGenQueriesANGLE(N+1, queries); | |
// Query current timestamp 1 | |
glQueryCounterANGLE(queries[0], GL_TIMESTAMP_ANGLE); | |
// Draw object 1 | |
.... | |
// Query current timestamp N | |
glQueryCounterANGLE(queries[N-1], GL_TIMESTAMP_ANGLE); | |
// Draw object N | |
.... | |
// Query current timestamp N+1 | |
glQueryCounterANGLE(queries[N], GL_TIMESTAMP_ANGLE); | |
// Wait for all results to become available | |
while (!available) { | |
glGetQueryObjectivANGLE(queries[N], GL_QUERY_RESULT_AVAILABLE_ANGLE, &available); | |
} | |
for (i = 0; i < N; i++) { | |
// See how much time the rendering of object i took in nanoseconds. | |
glGetQueryObjectui64vANGLE(queries[i], GL_QUERY_RESULT_ANGLE, &timeStart); | |
glGetQueryObjectui64vANGLE(queries[i+1], GL_QUERY_RESULT_ANGLE, &timeEnd); | |
timeElapsed = timeEnd - timeStart; | |
// Do something useful with the time. Note that care should be | |
// taken to use all significant bits of the result, not just the | |
// least significant 32 bits. | |
AdjustObjectLODBasedOnDrawTime(i, timeElapsed); | |
} | |
Issues from EXT_timer_query | |
(1) What time interval is being measured? | |
RESOLVED: The timer starts when all commands prior to BeginQuery() have | |
been fully executed. At that point, everything that should be drawn by | |
those commands has been written to the framebuffer. The timer stops | |
when all commands prior to EndQuery() have been fully executed. | |
(2) What unit of time will time intervals be returned in? | |
RESOLVED: Nanoseconds (10^-9 seconds). This unit of measurement allows | |
for reasonably accurate timing of even small blocks of rendering | |
commands. The granularity of the timer is implementation-dependent. A | |
32-bit query counter can express intervals of up to approximately 4 | |
seconds. | |
(3) What should be the minimum number of counter bits for timer queries? | |
RESOLVED: 30 bits, which will allow timing sections that take up to 1 | |
second to render. | |
(4) How are counter results of more than 32 bits returned? | |
RESOLVED: Via two new datatypes, int64ANGLE and uint64ANGLE, and their | |
corresponding GetQueryObject entry points. These types hold integer | |
values and have a minimum bit width of 64. | |
(5) Should the extension measure total time elapsed between the full | |
completion of the BeginQuery and EndQuery commands, or just time | |
spent in the graphics library? | |
RESOLVED: This extension will measure the total time elapsed between | |
the full completion of these commands. Future extensions may implement | |
a query to determine time elapsed at different stages of the graphics | |
pipeline. | |
(6) If multiple query types are supported, can multiple query types be | |
active simultaneously? | |
RESOLVED: Yes; an application may perform a timer query and another | |
type of query simultaneously. An application can not perform multiple | |
timer queries or multiple queries of other types simultaneously. An | |
application also can not use the same query object for another query | |
and a timer query simultaneously. | |
(7) Do query objects have a query type permanently associated with them? | |
RESOLVED: No. A single query object can be used to perform different | |
types of queries, but not at the same time. | |
Having a fixed type for each query object simplifies some aspects of the | |
implementation -- not having to deal with queries with different result | |
sizes, for example. It would also mean that BeginQuery() with a query | |
object of the "wrong" type would result in an INVALID_OPERATION error. | |
UPDATE: This resolution was relevant for EXT_timer_query and OpenGL 2.0. | |
Since EXT_transform_feedback has since been incorporated into the core, | |
the resolution is that BeginQuery will generate error INVALID_OPERATION | |
if <id> represents a query object of a different type. | |
(8) How predictable/repeatable are the results returned by the timer | |
query? | |
RESOLVED: In general, the amount of time needed to render the same | |
primitives should be fairly constant. But there may be many other | |
system issues (e.g., context switching on the CPU and GPU, virtual | |
memory page faults, memory cache behavior on the CPU and GPU) that can | |
cause times to vary wildly. | |
Note that modern GPUs are generally highly pipelined, and may be | |
processing different primitives in different pipeline stages | |
simultaneously. In this extension, the timers start and stop when the | |
BeginQuery/EndQuery commands reach the bottom of the rendering pipeline. | |
What that means is that by the time the timer starts, the GL driver on | |
the CPU may have started work on GL commands issued after BeginQuery, | |
and the higher pipeline stages (e.g., vertex transformation) may have | |
started as well. | |
(9) What should the new 64 bit integer type be called? | |
RESOLVED: The new types will be called GLint64ANGLE/GLuint64ANGLE. The new | |
command suffixes will be i64 and ui64. These names clearly convey the | |
minimum size of the types. These types are similar to the C99 standard | |
type int_least64_t, but we use names similar to the C99 optional type | |
int64_t for simplicity. | |
Issues from ARB_timer_query | |
(10) What about tile-based implementations? The effects of a command are | |
not complete until the frame is completely rendered. Timing recorded | |
before the frame is complete may not be what developers expect. Also | |
the amount of time needed to render the same primitives is not | |
consistent, which conflicts with issue (8) above. The time depends on | |
how early or late in the scene it is placed. | |
RESOLVED: The current language supports tile-based rendering okay as it | |
is written. Developers are warned that using timers on tile-based | |
implementation may not produce results they expect since rendering is not | |
done in a linear order. Timing results are calculated when the frame is | |
completed and may depend on how early or late in the scene it is placed. | |
(11) Can the GL implementation use different clocks to implement the | |
TIME_ELAPSED and TIMESTAMP queries? | |
RESOLVED: Yes, the implemenation can use different internal clocks to | |
implement TIME_ELAPSED and TIMESTAMP. If different clocks are | |
used it is possible there is a slight discrepancy when comparing queries | |
made from TIME_ELAPSED and TIMESTAMP; they may have slight | |
differences when both are used to measure the same sequence. However, this | |
is unlikely to affect real applications since comparing the two queries is | |
not expected to be useful. | |
Issues | |
(12) What should we call this extension? | |
RESOLVED: ANGLE_timer_query | |
(13) Why is this done as a separate extension instead of just supporting | |
ARB_timer_query? | |
ARB_timer_query is written against OpenGL 3.2, which includes a lot of | |
the required support for dealing with query objects. None of these | |
functions or tokens exist in OpenGL ES, and as such have to be added in | |
this specification. | |
(14) How does this extension differ from ARB_timer_query? | |
This extension contains most ARB_timer_query behavior unchanged as well | |
as a subset of the query support required to use it from the core | |
OpenGL 3.2 spec. It omits the glGetInteger(TIMESTAMP) functionality used to | |
query the current time on the GPU, but the behavior for all remaining | |
functionality taken from ARB_timer_query is the same. | |
(15) Are query objects shareable between multiple contexts? | |
RESOLVED: No. Query objects are lightweight and we normally share | |
large data across contexts. Also, being able to share query objects | |
across contexts is not particularly useful. In order to do the async | |
query across contexts, a query on one context would have to be finished | |
before the other context could query it. | |
Revision History | |
Revision 1, 2011/04/28 | |
- copied from revision 9 of ARB_timer_query and revision 7 of | |
ARB_occlusion_query | |
- removed language that was clearly not relevant to ES2 | |
- rebased changes against the OpenGL ES 2.0 specification |