1Name 2 3 ANGLE_timer_query 4 5Name Strings 6 7 GL_ANGLE_timer_query 8 9Contributors 10 11 Contributors to ARB_occlusion_query 12 Contributors to EXT_timer_query 13 Contributors to ARB_timer_query 14 Ben Vanik, Google Inc. 15 Daniel Koch, TransGaming Inc. 16 17Contact 18 19 Ben Vanik, Google Inc. (benvanik 'at' google 'dot' com) 20 21Status 22 23 Draft 24 25Version 26 27 Last Modified Date: Apr 28, 2011 28 Author Revision: 1 29 30Number 31 32 OpenGL ES Extension #?? 33 34Dependencies 35 36 OpenGL ES 2.0 is required. 37 38 The extension is written against the OpenGL ES 2.0 specification. 39 40Overview 41 42 Applications can benefit from accurate timing information in a number of 43 different ways. During application development, timing information can 44 help identify application or driver bottlenecks. At run time, 45 applications can use timing information to dynamically adjust the amount 46 of detail in a scene to achieve constant frame rates. OpenGL 47 implementations have historically provided little to no useful timing 48 information. Applications can get some idea of timing by reading timers 49 on the CPU, but these timers are not synchronized with the graphics 50 rendering pipeline. Reading a CPU timer does not guarantee the completion 51 of a potentially large amount of graphics work accumulated before the 52 timer is read, and will thus produce wildly inaccurate results. 53 glFinish() can be used to determine when previous rendering commands have 54 been completed, but will idle the graphics pipeline and adversely affect 55 application performance. 56 57 This extension provides a query mechanism that can be used to determine 58 the amount of time it takes to fully complete a set of GL commands, and 59 without stalling the rendering pipeline. It uses the query object 60 mechanisms first introduced in the occlusion query extension, which allow 61 time intervals to be polled asynchronously by the application. 62 63IP Status 64 65 No known IP claims. 66 67New Procedures and Functions 68 69 void GenQueriesANGLE(sizei n, uint *ids); 70 void DeleteQueriesANGLE(sizei n, const uint *ids); 71 boolean IsQueryANGLE(uint id); 72 void BeginQueryANGLE(enum target, uint id); 73 void EndQueryANGLE(enum target); 74 void QueryCounterANGLE(uint id, enum target); 75 void GetQueryivANGLE(enum target, enum pname, int *params); 76 void GetQueryObjectivANGLE(uint id, enum pname, int *params); 77 void GetQueryObjectuivANGLE(uint id, enum pname, uint *params); 78 void GetQueryObjecti64vANGLE(uint id, enum pname, int64 *params); 79 void GetQueryObjectui64vANGLE(uint id, enum pname, uint64 *params); 80 81New Tokens 82 83 Accepted by the <pname> parameter of GetQueryivANGLE: 84 85 QUERY_COUNTER_BITS_ANGLE 0x8864 86 CURRENT_QUERY_ANGLE 0x8865 87 88 Accepted by the <pname> parameter of GetQueryObjectivANGLE, 89 GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, and 90 GetQueryObjectui64vANGLE: 91 92 QUERY_RESULT_ANGLE 0x8866 93 QUERY_RESULT_AVAILABLE_ANGLE 0x8867 94 95 Accepted by the <target> parameter of BeginQueryANGLE, EndQueryANGLE, and 96 GetQueryivANGLE: 97 98 TIME_ELAPSED_ANGLE 0x88BF 99 100 Accepted by the <target> parameter of GetQueryivANGLE and 101 QueryCounterANGLE: 102 103 TIMESTAMP_ANGLE 0x8E28 104 105Additions to Chapter 2 of the OpenGL ES 2.0 Specification (OpenGL ES Operation) 106 107 (Modify table 2.1, Correspondence of command suffix letters to GL argument) 108 Add two new types: 109 110 Letter Corresponding GL Type 111 ------ --------------------- 112 i64 int64ANGLE 113 ui64 uint64ANGLE 114 115 (Modify table 2.2, GL data types) Add two new types: 116 117 GL Type Minimum Bit Width Description 118 ------- ----------------- ----------------------------- 119 int64ANGLE 64 Signed 2's complement integer 120 uint64ANGLE 64 Unsigned binary integer 121 122Additions to Chapter 5 of the OpenGL ES 2.0 Specification (Special Functions) 123 124 Add a new section 5.3 "Timer Queries": 125 126 "5.3 Timer Queries 127 128 Timer queries use query objects to track the amount of time needed to 129 fully complete a set of GL commands, or to determine the current time 130 of the GL. 131 132 Timer queries are associated with query objects. The command 133 134 void GenQueriesANGLE(sizei n, uint *ids); 135 136 returns <n> previously unused query object names in <ids>. These 137 names are marked as used, but no object is associated with them until 138 the first time they are used by BeginQueryANGLE. Query objects contain 139 one piece of state, an integer result value. This result value is 140 initialized to zero when the object is created. Any positive integer 141 except for zero (which is reserved for the GL) is a valid query 142 object name. 143 144 Query objects are deleted by calling 145 146 void DeleteQueriesANGLE(sizei n, const uint *ids); 147 148 <ids> contains <n> names of query objects to be deleted. After a 149 query object is deleted, its name is again unused. Unused names in 150 <ids> are silently ignored. 151 If an active query object is deleted its name immediately becomes unused, 152 but the underlying object is not deleted until it is no longer active. 153 154 A timer query can be started and finished by calling 155 156 void BeginQueryANGLE(enum target, uint id); 157 void EndQueryANGLE(enum target); 158 159 where <target> is TIME_ELAPSED_ANGLE. If BeginQueryANGLE is called 160 with an unused <id>, that name is marked as used and associated with 161 a new query object. 162 163 If BeginQueryANGLE is called with an <id> of zero, if the active query 164 object name for <target> is non-zero, if <id> is the name of an existing 165 query object whose type does not match <target>, or if <id> is the active 166 query object name for any query type, the error INVALID_OPERATION is 167 generated. If EndQueryANGLE is called while no query with the same target 168 is in progress, an INVALID_OPERATION error is generated. 169 170 When BeginQueryANGLE and EndQueryANGLE are called with a <target> of 171 TIME_ELAPSED_ANGLE, the GL prepares to start and stop the timer used for 172 timer queries. The timer is started or stopped when the effects from all 173 previous commands on the GL client and server state and the framebuffer 174 have been fully realized. The BeginQueryANGLE and EndQueryANGLE commands 175 may return before the timer is actually started or stopped. When the timer 176 query timer is finally stopped, the elapsed time (in nanoseconds) is 177 written to the corresponding query object as the query result value, and 178 the query result for that object is marked as available. 179 180 If the elapsed time overflows the number of bits, <n>, available to hold 181 elapsed time, its value becomes undefined. It is recommended, but not 182 required, that implementations handle this overflow case by saturating at 183 2^n - 1. 184 185 The necessary state is a single bit indicating whether an timer 186 query is active, the identifier of the currently active timer 187 query, and a counter keeping track of the time that has passed. 188 189 When the command 190 191 void QueryCounterANGLE(uint id, enum target); 192 193 is called with <target> TIMESTAMP_ANGLE, the GL records the current time 194 into the corresponding query object. The time is recorded after all 195 previous commands on the GL client and server state and the framebuffer 196 have been fully realized. When the time is recorded, the query result for 197 that object is marked available. QueryCounterANGLE timer queries can be 198 used within a BeginQueryANGLE / EndQueryANGLE block where the <target> is 199 TIME_ELAPSED_ANGLE and it does not affect the result of that query object. 200 The error INVALID_OPERATION is generated if the <id> is already in use 201 within a BeginQueryANGLE/EndQueryANGLE block." 202 203Additions to Chapter 6 of the OpenGL ES 2.0 Specification (State and State 204Requests) 205 206 Add a new section 6.1.9 "Timer Queries": 207 208 "The command 209 210 boolean IsQueryANGLE(uint id); 211 212 returns TRUE if <id> is the name of a query object. If <id> is zero, 213 or if <id> is a non-zero value that is not the name of a query 214 object, IsQueryANGLE returns FALSE. 215 216 Information about a query target can be queried with the command 217 218 void GetQueryivANGLE(enum target, enum pname, int *params); 219 220 <target> identifies the query target and can be TIME_ELAPSED_ANGLE or 221 TIMESTAMP_ANGLE for timer queries. 222 223 If <pname> is CURRENT_QUERY_ANGLE, the name of the currently active query 224 for <target>, or zero if no query is active, will be placed in <params>. 225 226 If <pname> is QUERY_COUNTER_BITS_ANGLE, the implementation-dependent number 227 of bits used to hold the query result for <target> will be placed in 228 <params>. The number of query counter bits may be zero, in which case 229 the counter contains no useful information. 230 231 For timer queries (TIME_ELAPSED_ANGLE and TIMESTAMP_ANGLE), if the number 232 of bits is non-zero, the minimum number of bits allowed is 30 which 233 will allow at least 1 second of timing. 234 235 The state of a query object can be queried with the commands 236 237 void GetQueryObjectivANGLE(uint id, enum pname, int *params); 238 void GetQueryObjectuivANGLE(uint id, enum pname, uint *params); 239 void GetQueryObjecti64vANGLE(uint id, enum pname, int64 *params); 240 void GetQueryObjectui64vANGLE(uint id, enum pname, uint64 *params); 241 242 If <id> is not the name of a query object, or if the query object 243 named by <id> is currently active, then an INVALID_OPERATION error is 244 generated. 245 246 If <pname> is QUERY_RESULT_ANGLE, then the query object's result 247 value is returned as a single integer in <params>. If the value is so 248 large in magnitude that it cannot be represented with the requested type, 249 then the nearest value representable using the requested type is 250 returned. If the number of query counter bits for target is zero, then 251 the result is returned as a single integer with the value zero. 252 253 There may be an indeterminate delay before the above query returns. If 254 <pname> is QUERY_RESULT_AVAILABLE_ANGLE, FALSE is returned if such a delay 255 would be required; otherwise TRUE is returned. It must always be true 256 that if any query object returns a result available of TRUE, all queries 257 of the same type issued prior to that query must also return TRUE. 258 259 Querying the state for a given timer query forces that timer query to 260 complete within a finite amount of time. 261 262 If multiple queries are issued on the same target and id prior to 263 calling GetQueryObject[u]i[64]vANGLE, the result returned will always be 264 from the last query issued. The results from any queries before the 265 last one will be lost if the results are not retrieved before starting 266 a new query on the same <target> and <id>." 267 268Errors 269 270 The error INVALID_VALUE is generated if GenQueriesANGLE is called where 271 <n> is negative. 272 273 The error INVALID_VALUE is generated if DeleteQueriesANGLE is called 274 where <n> is negative. 275 276 The error INVALID_OPERATION is generated if BeginQueryANGLE is called 277 when a query of the given <target> is already active. 278 279 The error INVALID_OPERATION is generated if EndQueryANGLE is called 280 when a query of the given <target> is not active. 281 282 The error INVALID_OPERATION is generated if BeginQueryANGLE is called 283 where <id> is zero. 284 285 The error INVALID_OPERATION is generated if BeginQueryANGLE is called 286 where <id> is the name of a query currently in progress. 287 288 The error INVALID_OPERATION is generated if BeginQueryANGLE is called 289 where <id> is the name of an existing query object whose type does not 290 match <target>. 291 292 The error INVALID_ENUM is generated if BeginQueryANGLE or EndQueryANGLE 293 is called where <target> is not TIME_ELAPSED_ANGLE. 294 295 The error INVALID_ENUM is generated if GetQueryivANGLE is called where 296 <target> is not TIME_ELAPSED_ANGLE or TIMESTAMP_ANGLE. 297 298 The error INVALID_ENUM is generated if GetQueryivANGLE is called where 299 <pname> is not QUERY_COUNTER_BITS_ANGLE or CURRENT_QUERY_ANGLE. 300 301 The error INVALID_ENUM is generated if QueryCounterANGLE is called where 302 <target> is not TIMESTAMP_ANGLE. 303 304 The error INVALID_OPERATION is generated if QueryCounterANGLE is called 305 on a query object that is already in use inside a 306 BeginQueryANGLE/EndQueryANGLE. 307 308 The error INVALID_OPERATION is generated if GetQueryObjectivANGLE, 309 GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or 310 GetQueryObjectui64vANGLE is called where <id> is not the name of a query 311 object. 312 313 The error INVALID_OPERATION is generated if GetQueryObjectivANGLE, 314 GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or 315 GetQueryObjectui64vANGLE is called where <id> is the name of a currently 316 active query object. 317 318 The error INVALID_ENUM is generated if GetQueryObjectivANGLE, 319 GetQueryObjectuivANGLE, GetQueryObjecti64vANGLE, or 320 GetQueryObjectui64vANGLE is called where <pname> is not 321 QUERY_RESULT_ANGLE or QUERY_RESULT_AVAILABLE_ANGLE. 322 323New State 324 325 (Add a new table 6.xx, "Query Operations") 326 327 Get Value Type Get Command Initial Value Description Sec 328 --------- ---- ----------- ------------- ----------- ------ 329 - B - FALSE query active 5.3 330 CURRENT_QUERY_ANGLE Z+ GetQueryivANGLE 0 active query ID 5.3 331 QUERY_RESULT_ANGLE Z+ GetQueryObjectuivANGLE, 0 samples-passed count 5.3 332 GetQueryObjectui64vANGLE 333 QUERY_RESULT_AVAILABLE_ANGLE B GetQueryObjectivANGLE FALSE query result available 5.3 334 335New Implementation Dependent State 336 337 (Add the following entry to table 6.18): 338 339 Get Value Type Get Command Minimum Value Description Sec 340 -------------------------- ---- ----------- ------------- ---------------- ------ 341 QUERY_COUNTER_BITS_ANGLE Z+ GetQueryivANGLE see 6.1.9 Number of bits in 6.1.9 342 query counter 343 344Examples 345 346 (1) Here is some rough sample code that demonstrates the intended usage 347 of this extension. 348 349 GLint queries[N]; 350 GLint available = 0; 351 // timer queries can contain more than 32 bits of data, so always 352 // query them using the 64 bit types to avoid overflow 353 GLuint64ANGLE timeElapsed = 0; 354 355 // Create a query object. 356 glGenQueriesANGLE(N, queries); 357 358 // Start query 1 359 glBeginQueryANGLE(GL_TIME_ELAPSED_ANGLE, queries[0]); 360 361 // Draw object 1 362 .... 363 364 // End query 1 365 glEndQueryANGLE(GL_TIME_ELAPSED_ANGLE); 366 367 ... 368 369 // Start query N 370 glBeginQueryANGLE(GL_TIME_ELAPSED_ANGLE, queries[N-1]); 371 372 // Draw object N 373 .... 374 375 // End query N 376 glEndQueryANGLE(GL_TIME_ELAPSED_ANGLE); 377 378 // Wait for all results to become available 379 while (!available) { 380 glGetQueryObjectivANGLE(queries[N-1], GL_QUERY_RESULT_AVAILABLE_ANGLE, &available); 381 } 382 383 for (i = 0; i < N; i++) { 384 // See how much time the rendering of object i took in nanoseconds. 385 glGetQueryObjectui64vANGLE(queries[i], GL_QUERY_RESULT_ANGLE, &timeElapsed); 386 387 // Do something useful with the time. Note that care should be 388 // taken to use all significant bits of the result, not just the 389 // least significant 32 bits. 390 AdjustObjectLODBasedOnDrawTime(i, timeElapsed); 391 } 392 393 This example is sub-optimal in that it stalls at the end of every 394 frame to wait for query results. Ideally, the collection of results 395 would be delayed one frame to minimize the amount of time spent 396 waiting for the GPU to finish rendering. 397 398 (2) This example is basically the same as the example above but uses 399 QueryCounter instead. 400 401 GLint queries[N+1]; 402 GLint available = 0; 403 // timer queries can contain more than 32 bits of data, so always 404 // query them using the 64 bit types to avoid overflow 405 GLuint64ANGLE timeStart, timeEnd, timeElapsed = 0; 406 407 // Create a query object. 408 glGenQueriesANGLE(N+1, queries); 409 410 // Query current timestamp 1 411 glQueryCounterANGLE(queries[0], GL_TIMESTAMP_ANGLE); 412 413 // Draw object 1 414 .... 415 416 // Query current timestamp N 417 glQueryCounterANGLE(queries[N-1], GL_TIMESTAMP_ANGLE); 418 419 // Draw object N 420 .... 421 422 // Query current timestamp N+1 423 glQueryCounterANGLE(queries[N], GL_TIMESTAMP_ANGLE); 424 425 // Wait for all results to become available 426 while (!available) { 427 glGetQueryObjectivANGLE(queries[N], GL_QUERY_RESULT_AVAILABLE_ANGLE, &available); 428 } 429 430 for (i = 0; i < N; i++) { 431 // See how much time the rendering of object i took in nanoseconds. 432 glGetQueryObjectui64vANGLE(queries[i], GL_QUERY_RESULT_ANGLE, &timeStart); 433 glGetQueryObjectui64vANGLE(queries[i+1], GL_QUERY_RESULT_ANGLE, &timeEnd); 434 timeElapsed = timeEnd - timeStart; 435 436 // Do something useful with the time. Note that care should be 437 // taken to use all significant bits of the result, not just the 438 // least significant 32 bits. 439 AdjustObjectLODBasedOnDrawTime(i, timeElapsed); 440 } 441 442Issues from EXT_timer_query 443 444 (1) What time interval is being measured? 445 446 RESOLVED: The timer starts when all commands prior to BeginQuery() have 447 been fully executed. At that point, everything that should be drawn by 448 those commands has been written to the framebuffer. The timer stops 449 when all commands prior to EndQuery() have been fully executed. 450 451 (2) What unit of time will time intervals be returned in? 452 453 RESOLVED: Nanoseconds (10^-9 seconds). This unit of measurement allows 454 for reasonably accurate timing of even small blocks of rendering 455 commands. The granularity of the timer is implementation-dependent. A 456 32-bit query counter can express intervals of up to approximately 4 457 seconds. 458 459 (3) What should be the minimum number of counter bits for timer queries? 460 461 RESOLVED: 30 bits, which will allow timing sections that take up to 1 462 second to render. 463 464 (4) How are counter results of more than 32 bits returned? 465 466 RESOLVED: Via two new datatypes, int64ANGLE and uint64ANGLE, and their 467 corresponding GetQueryObject entry points. These types hold integer 468 values and have a minimum bit width of 64. 469 470 (5) Should the extension measure total time elapsed between the full 471 completion of the BeginQuery and EndQuery commands, or just time 472 spent in the graphics library? 473 474 RESOLVED: This extension will measure the total time elapsed between 475 the full completion of these commands. Future extensions may implement 476 a query to determine time elapsed at different stages of the graphics 477 pipeline. 478 479 (6) If multiple query types are supported, can multiple query types be 480 active simultaneously? 481 482 RESOLVED: Yes; an application may perform a timer query and another 483 type of query simultaneously. An application can not perform multiple 484 timer queries or multiple queries of other types simultaneously. An 485 application also can not use the same query object for another query 486 and a timer query simultaneously. 487 488 (7) Do query objects have a query type permanently associated with them? 489 490 RESOLVED: No. A single query object can be used to perform different 491 types of queries, but not at the same time. 492 493 Having a fixed type for each query object simplifies some aspects of the 494 implementation -- not having to deal with queries with different result 495 sizes, for example. It would also mean that BeginQuery() with a query 496 object of the "wrong" type would result in an INVALID_OPERATION error. 497 498 UPDATE: This resolution was relevant for EXT_timer_query and OpenGL 2.0. 499 Since EXT_transform_feedback has since been incorporated into the core, 500 the resolution is that BeginQuery will generate error INVALID_OPERATION 501 if <id> represents a query object of a different type. 502 503 (8) How predictable/repeatable are the results returned by the timer 504 query? 505 506 RESOLVED: In general, the amount of time needed to render the same 507 primitives should be fairly constant. But there may be many other 508 system issues (e.g., context switching on the CPU and GPU, virtual 509 memory page faults, memory cache behavior on the CPU and GPU) that can 510 cause times to vary wildly. 511 512 Note that modern GPUs are generally highly pipelined, and may be 513 processing different primitives in different pipeline stages 514 simultaneously. In this extension, the timers start and stop when the 515 BeginQuery/EndQuery commands reach the bottom of the rendering pipeline. 516 What that means is that by the time the timer starts, the GL driver on 517 the CPU may have started work on GL commands issued after BeginQuery, 518 and the higher pipeline stages (e.g., vertex transformation) may have 519 started as well. 520 521 (9) What should the new 64 bit integer type be called? 522 523 RESOLVED: The new types will be called GLint64ANGLE/GLuint64ANGLE. The new 524 command suffixes will be i64 and ui64. These names clearly convey the 525 minimum size of the types. These types are similar to the C99 standard 526 type int_least64_t, but we use names similar to the C99 optional type 527 int64_t for simplicity. 528 529Issues from ARB_timer_query 530 531 (10) What about tile-based implementations? The effects of a command are 532 not complete until the frame is completely rendered. Timing recorded 533 before the frame is complete may not be what developers expect. Also 534 the amount of time needed to render the same primitives is not 535 consistent, which conflicts with issue (8) above. The time depends on 536 how early or late in the scene it is placed. 537 538 RESOLVED: The current language supports tile-based rendering okay as it 539 is written. Developers are warned that using timers on tile-based 540 implementation may not produce results they expect since rendering is not 541 done in a linear order. Timing results are calculated when the frame is 542 completed and may depend on how early or late in the scene it is placed. 543 544 (11) Can the GL implementation use different clocks to implement the 545 TIME_ELAPSED and TIMESTAMP queries? 546 547 RESOLVED: Yes, the implemenation can use different internal clocks to 548 implement TIME_ELAPSED and TIMESTAMP. If different clocks are 549 used it is possible there is a slight discrepancy when comparing queries 550 made from TIME_ELAPSED and TIMESTAMP; they may have slight 551 differences when both are used to measure the same sequence. However, this 552 is unlikely to affect real applications since comparing the two queries is 553 not expected to be useful. 554 555Issues 556 557 (12) What should we call this extension? 558 559 RESOLVED: ANGLE_timer_query 560 561 (13) Why is this done as a separate extension instead of just supporting 562 ARB_timer_query? 563 564 ARB_timer_query is written against OpenGL 3.2, which includes a lot of 565 the required support for dealing with query objects. None of these 566 functions or tokens exist in OpenGL ES, and as such have to be added in 567 this specification. 568 569 (14) How does this extension differ from ARB_timer_query? 570 571 This extension contains most ARB_timer_query behavior unchanged as well 572 as a subset of the query support required to use it from the core 573 OpenGL 3.2 spec. It omits the glGetInteger(TIMESTAMP) functionality used to 574 query the current time on the GPU, but the behavior for all remaining 575 functionality taken from ARB_timer_query is the same. 576 577 (15) Are query objects shareable between multiple contexts? 578 579 RESOLVED: No. Query objects are lightweight and we normally share 580 large data across contexts. Also, being able to share query objects 581 across contexts is not particularly useful. In order to do the async 582 query across contexts, a query on one context would have to be finished 583 before the other context could query it. 584 585Revision History 586 587 Revision 1, 2011/04/28 588 - copied from revision 9 of ARB_timer_query and revision 7 of 589 ARB_occlusion_query 590 - removed language that was clearly not relevant to ES2 591 - rebased changes against the OpenGL ES 2.0 specification 592