Query Object

From OpenGL Wiki
Jump to navigation Jump to search
Query Object
Core in version 4.6
Core since version 1.5
ARB extension ARB_occlusion_query

Query Objects are OpenGL Objects that are used for asynchronous queries of certain kinds of information.

Management[edit]

Query objects follow the general OpenGL Object paradigm. They have glGenQueries and glDeleteQueries. However, unlike most other OpenGL Objects, there is no general glBindQuery or similar function.

Query objects are not Container Objects, but they cannot be shared between OpenGL contexts.

Many kinds of query operations require a scope. The scope of the query is bound by a pair of begin/end functions, such that the query returns some information generated through the execution of the commands within that scope.

Like textures, query objects have different types, represented by the target​ parameter taken by the query object functions. Query objects represent a single integer value, generated by some OpenGL process, which can be queried asynchronously. The different types determine what the value being queried means and where it comes from:

  • GL_SAMPLES_PASSED​: The number of samples that pass the depth test for all drawing commands within the scope of the query.
  • GL_ANY_SAMPLES_PASSED​: GL_FALSE if none of the scoped drawing commands generate samples that pass the depth test; otherwise, the value is GL_TRUE. This requires OpenGL 3.3 or ARB_occlusion_query2.
  • GL_ANY_SAMPLES_PASSED_CONSERVATIVE​: As GL_ANY_SAMPLES_PASSED, except that the implementation may use a less accurate algorithm, which may be faster, but at the cost of more more false positives. This requires OpenGL 4.3 or ARB_ES3_compatibility.
  • GL_PRIMITIVES_GENERATED: Records the number of primitives sent to a particular Geometry Shader output stream (or by stream 0 if no GS is active) by the scoped drawing commands. This requires OpenGL 3.0; multiple streams requires OpenGL 4.0 or ARB_transform_feedback3.
  • GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: Records the number of primitives written by a Geometry Shader stream to a Transform Feedback object (or by stream 0 if no GS is active) by the scoped drawing commands. This requires OpenGL 3.0; multiple streams requires OpenGL 4.0 or ARB_transform_feedback3.
  • GL_TIME_ELAPSED​: Records the time that it takes for the GPU to execute all of the scoped commands. The timer starts when all commands before the scope have completed, and the timer ends when the last scoped command has completed. Requires OpenGL 3.3 or ARB_timer_query.
  • GL_TIMESTAMP: Records the current time of the GPU. Requires OpenGL 3.3 or ARB_timer_query.

Query scope[edit]

The result of query objects is usually based on data created from multiple drawing commands. These commands represent the scope of the query operation. The scope of a query operation is demarcated by a begin/end pair of functions:

glBeginQuery(GLenum target​, GLuint id​);
glEndQuery(GLenum target​);

The only query type target​ not accepted by these functions is GL_TIMESTAMP.

Some targets are indexed, meaning that the target has some number of slots for query operations. These kinds of targets can use glBeginQueryIndexed and glEndQueryIndexed, which take an index that the function will begin a query on. glBeginQuery is defined as calling glBeginQueryIndexed with an index​ of 0.

What the index represents depends on the target​ type. Targets that allow for a non-zero index are:

  • GL_PRIMITIVES_GENERATED​: The index represents a vertex stream output from the Geometry Shader. The maximum index is GL_MAX_VERTEX_STREAMS. Requires OpenGL 4.0 or ARB_transform_feedback3.
  • GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: The index represents a vertex stream output from the Geometry Shader. The maximum index is GL_MAX_VERTEX_STREAMS. Requires OpenGL 4.0 or ARB_transform_feedback3.

Query retrieval[edit]

Once a query object has ended its querying operation (via a call to glEndQueryIndexed or an equivalent function), the data stored in the query can be retrieved. To retrieve information from the query object, the following function is used:

glGetQueryObject[ui64v](GLuint id​, GLenum pname​, type * params​);

The id​ is the query object to retrieve information about. pname​ is the type of information to retrieve. The data queried will be written into params​.

Query objects provide the following different types of information:

GL_QUERY_RESULT_AVAILABLE
The purpose of query objects is to represent asynchronous queries of values. Thus, even after a query's scope has ended, the queried value may not be immediately available. This query is used to ask the query object if the result is actually available. This will write GL_FALSE to params​ if the query operation has not been completed, and GL_TRUE otherwise.
GL_QUERY_RESULT
This returns the actual result of the query. If the query's internal integer value is too large for the bitdepth the function call asked for (ie: the query stores the integer with 48-bits, but the user uses the the 32-bit integer version of the function), then the value returned will be the value that is closest to the actual value of the query.
Note that if you query the result before GL_QUERY_RESULT_AVAILABLE returns GL_TRUE, then you will be effectively turning an asynchronous query into a synchronous one. That is, the function will stall the CPU until the result is available.
GL_QUERY_RESULT_NO_WAIT (requires OpenGL 4.4 or ARB_query_buffer_object)
This is a combination of the above two values. If the query result is available, this returns the query result as though with GL_QUERY_RESULT. If the query result is not available, then nothing is written, and whatever value was in params​ will be unchanged.

Query buffer object[edit]

Query Object
Core in version 4.6
Core since version 4.4
Core ARB extension ARB_query_buffer_object
Vendor extension AMD_query_buffer_object

The results queried from a query object can be fed into a Buffer Object. This allows users to help feed query results back into internal OpenGL operations. This is useful for querying the number of primitives generated by the Geometry Shader into an indirect rendering call. It can also be used in tandem with Image Load Store and other buffer writing mechanisms.

To do this, a buffer object must first be bound to GL_QUERY_BUFFER with glBindBuffer. When a non-zero buffer object is bound to this binding point, the behavior of glGetQueryObject changes. The params​ field is interpreted, not as a pointer to memory but as a numerical byte offset into the buffer bound to GL_QUERY_BUFFER.

The size of the value written is determined by the type used with the glGetQueryObject function call. So the 64-bit integer versions of that function will write 64 bits of data, and the params​ offset must be 8-byte aligned. Otherwise it will write 32 bits of data, and the offset must be 4-byte aligned.

The write into the buffer works using the standard, in-order Memory Model that the rest of OpenGL uses. There is a barrier that is needed to order writes between incoherent accesses to the buffer and this API.

Query precision[edit]

All query objects represent a single integer value. However, each type of query object has a different precision for this value. While you can query the integer value at 32 and 64-bit precision, the OpenGL implementation only provides so much precision for these values.

The precision for a query can be acquired by calling glGetQuery with GL_QUERY_COUNTER_BITS as the second parameter. OpenGL defines the minimum precision for each query type as follows:

Query type Minimum precision
GL_SAMPLES_PASSED​ 32*
GL_ANY_SAMPLES_PASSED​ 1
GL_ANY_SAMPLES_PASSED_CONSERVATIVE​ 1
GL_PRIMITIVES_GENERATED 32
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 32
GL_TIME_ELAPSED​ 30
GL_TIMESTAMP 30

* In OpenGL 3.3, the minimum precision required is a function of the maximum viewport width and height. Specifically, the precision is required to be at least enough to hold a counter that is twice the maximum width and height, thus supporting 2x multisampling at the largest buffer size. GL 4.0 and above simply require at least 32 bits of precision.

Note that implementations are allowed to return 0 for any of these values. This means that the query type contains no actual information, and the implementation does not really support it..

Occlusion queries[edit]

Query Object
Core in version 4.6
Core since version 1.5
Core ARB extension ARB_occlusion_query2

Occlusion queries are the collective name for the following query object types: GL_SAMPLES_PASSED​​, GL_ANY_SAMPLES_PASSED​​, and GL_ANY_SAMPLES_PASSED_CONSERVATIVE​​. These represent ways to detect whether an object is visible.

Specifically, these queries detect whether any fragments continue being processed after reaching the depth test stage in the Per-Sample Processing part of the rendering pipeline. These operations proceed in the order defined on that page, so if a fragment passes the depth test, then it must also have passed all of the operations before it. So fragments which pass the depth test must also have passed the stencil test, scissor test, etc.

This also has implications for what happens when explicitly forces the implementation to perform some of those tests before the Fragment Shader executes (normally, they come afterwards). In this case, a fragment will be considered to have passed the depth test before the FS executes. So that sample will be counted by the occlusion query even if you discard the fragment in the shader. The discarded fragment will not be written to color buffers, but it will still count for query purposes.

When using Multisampling, the implementation is allowed to decide how to count the individual samples within a pixel area. The implementation may count only the samples within a triangle's area (if the triangle only covers part of a pixel), or it may count all of the samples if the triangle covers any sample in a pixel.

GL_SAMPLES_PASSED gives a count of the number of samples that passed, while GL_ANY_SAMPLES_PASSED​​ is a boolean query: did any samples pass. The GL_ANY_SAMPLES_PASSED_CONSERVATIVE​​ does essentially the same thing, but with an accuracy tradeoff for possibly increased performance. If it is inaccurate, then it will only give false positives rather than false negatives.

The result of an occlusion query can be used as the condition for Conditional Rendering.

In general, this feature is used to prevent rendering objects that are complex by rendering a much simpler volume (with a simple shader) in the same place. Write Masks are used to prevent drawing anything or modifying the depth buffer. If samples of that object are visible, then the actual complex object is drawn.

Timer queries[edit]

Timer Query
Core in version 4.6
Core since version 3.3
Core ARB extension ARB_timer_query

Timer queries are the collective name for the query types GL_TIMESTAMP and GL_TIME_ELAPSED. These are used for measuring the GPU timings of various operations. All times returned are measured in nanoseconds.

The GL_TIME_ELAPSED query is used with a scope to measure how long it takes for the scoped commands to execute on the GPU. The timer starts when the first command starts executing, and the timer ends when the final command is completed.

The GL_TIMESTAMP query type is not used with a scope; it is used to record a specific time. Exactly what the time is relative to is implementation-dependent, but the timestamp is always increasing. The timestamp can be fetched in one of two ways. It can be fetched into a query object with this function:

glQueryCounter(GLuint id​, GLenum target​);

This will store into the query object the time when the GPU will have completed all previously issued commands.

It can also be fetched directly. Calling glGetInteger64v with GL_TIMESTAMP will return the GPU timestamp when all previously given commands have issued, but not necessarily completed. Unlike the actual timer query commands, this command will stall the CPU until the timestamp is returned.

When using the synchronous timestamp, coupled with an asynchronous GL_TIMESTAMP query into a query object, one can determine the latency between when commands are issued and when they complete.

Primitive queries[edit]

Primitive Query
Core in version 4.6
Core since version 3.0

Primitive queries are the collective name for the GL_PRIMITIVES_GENERATED​​ and GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN​ query object types. These are used for detecting how many primitives were generated during one or more rendering operations. This is primarily used for Transform Feedback, particularly in cases where the number of primitives to be generated by a Geometry Shader and/or Tessellation depends on the data provided.

The primitive counts in these cases are the number of basic primitives. So a triangle strip 5 vertices long is 3 primitives, not one primitive.

GL_PRIMITIVES_GENERATED​​ provides the number of primitives that are output by the final Vertex Processing step. If there's no Geometry Shader or Tessellation activated, then this will be the number of primitives from the drawing command.

GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN is slightly different. It is the number of primitives that are written to transform feedback buffers. For this count to be incremented, the scoped commands must issue drawing commands while within the boundaries of glBeginTransformFeedback/glEndTransformFeedback. The transform feedback scope and the query scope can overlap, and one can end before the other. Similarly, transform feedback can be paused with glPauseTransformFeedback, and the count will not include primitives generated during the pause.

Note that glDrawTransformFeedback is perfectly capable of rendering from a transform feedback object without having to query the number of vertices. Though this is only core in GL 4.0, it is widely available on 3.x-class hardware.

Multiple streams[edit]

Stream Output
Core in version 4.6
Core since version 4.0
Core ARB extension ARB_transform_feedback3

Transform feedback operations can have multiple output streams. As such, primitive queries can be indexed, with each index representing a potential transform feedback output stream.

The definition of the primitive query types does not change. Primitives can still be sent to a stream where no transform feedback buffers are written. In that case, any GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN queries active on that stream will not have their count incremented. However, active GL_PRIMITIVES_GENERATED queries will.

Reference[edit]