Name ARB_occlusion_query Name Strings GL_ARB_occlusion_query Contributors Ross Cunniff Matt Craighead Daniel Ginsburg Kevin Lefebvre Bill Licea-Kane Nick Triantos Contact Matt Craighead, NVIDIA Corporation (mcraighead 'at' nvidia.com) Daniel Ginsburg, AMD (dan.ginsburg 'at' amd.com) Notice Copyright (c) 2003-2013 The Khronos Group Inc. Copyright terms at http://www.khronos.org/registry/speccopyright.html Specification Update Policy Khronos-approved extension specifications are updated in response to issues and bugs prioritized by the Khronos OpenGL Working Group. For extensions which have been promoted to a core Specification, fixes will first appear in the latest version of that core Specification, and will eventually be backported to the extension document. This policy is described in more detail at https://www.khronos.org/registry/OpenGL/docs/update_policy.php IP Status HP has claimed that they hold IP around use of this extension. HP has committed to releasing rights to this IP to the ARB if the functionality is included in OpenGL (April 10, 2003). Status Approved by the ARB (version 1.0), June 10, 2003, pending further minor revisions Version Date: April 21, 2007 Revision: 7 $Id$ Number ARB Extension #29 Dependencies Written based on the wording of the OpenGL 1.4 specification. HP_occlusion_test affects the definition of this extension. Overview This extension defines a mechanism whereby an application can query the number of pixels (or, more precisely, samples) drawn by a primitive or group of primitives. The primary purpose of such a query (hereafter referred to as an "occlusion query") is to determine the visibility of an object. Typically, the application will render the major occluders in the scene, then perform an occlusion query for the bounding box of each detail object in the scene. Only if said bounding box is visible, i.e., if at least one sample is drawn, should the corresponding object be drawn. The earlier HP_occlusion_test extension defined a similar mechanism, but it had two major shortcomings. - It returned the result as a simple GL_TRUE/GL_FALSE result, when in fact it is often useful to know exactly how many samples were drawn. - It provided only a simple "stop-and-wait" model for using multiple queries. The application begins an occlusion test and ends it; then, at some later point, it asks for the result, at which point the driver must stop and wait until the result from the previous test is back before the application can even begin the next one. This is a very simple model, but its performance is mediocre when an application wishes to perform many queries, and it eliminates most of the opportunities for parallelism between the CPU and GPU. This extension solves both of those problems. It returns as its result the number of samples that pass the depth and stencil tests, and it encapsulates occlusion queries in "query objects" that allow applications to issue many queries before asking for the result of any one. As a result, they can overlap the time it takes for the occlusion query results to be returned with other, more useful work, such as rendering other parts of the scene or performing other computations on the CPU. There are many situations where a pixel/sample count, rather than a boolean result, is useful. - Objects that are visible but cover only a very small number of pixels can be skipped at a minimal reduction of image quality. - Knowing exactly how many pixels an object might cover may help the application decide which level-of-detail model should be used. If only a few pixels are visible, a low-detail model may be acceptable. - "Depth peeling" techniques, such as order-independent transparency, need to know when to stop rendering more layers; it is difficult to determine a priori how many layers are needed. A boolean result allows applications to stop when more layers will not affect the image at all, but this will likely result in unacceptable performance. Instead, it makes more sense to stop rendering when the number of pixels in each layer falls below a given threshold. - Occlusion queries can replace glReadPixels of the depth buffer to determine whether (for example) a light source is visible for the purposes of a lens flare effect or a halo to simulate glare. Pixel counts allow you to compute the percentage of the light source that is visible, and the brightness of these effects can be modulated accordingly. Issues How is this extension different from NV_occlusion_query? The following changes have been made. - A "target" parameter has been added. Only one target exists at present, SAMPLES_PASSED_ARB, but future extensions could add additional types of queries. - Terminology changed slightly. "Pixel" was being used incorrectly, where "fragment" or "sample" would be more accurate. - Various NVIDIA-specific references have been removed. - Interactions with multisample have been changed slightly to allow implementations based on either a sample count or a fragment count. The result is returned in units of samples. - Clarified that using an id of zero is illegal. - Added missing spec language for IsQuery entry point. - General language, issues, etc. cleanup. - HP_occlusion_test is no longer required. - Modified the minimum required counter bits to be dependent on the implementation's maximum viewport or the value 0 - Clarified that active query state is per target server state. This implies that a loop of QUERY_RESULT_AVAILABLE_ARB will return TRUE in finite time. NV_occlusion_query asked that the application flush to prevent an infinite loop. - Clarified the behavior of the async QUERY_RESULT_AVAILABLE_ARB command. - When the count of samples that pass the occlusion query overflows, the value should saturate. Should we use an object-based interface? RESOLVED: Yes, this makes the interface much simpler, and it is friendly for indirect rendering. What is the difference between a "query object" and an "occlusion query"? "Occlusion query" is a synonym for "query object used with target SAMPLES_PASSED". Should we offer a way to poll whether an occlusion query has completed and its result is available? RESOLVED. Yes, this allows applications to use CPU time that might have been spent spinning more usefully. However, the polling method introduced in the NV_occlusion_query spec allowed for a potential infinite loop if the application does not do a flush. This version of the spec clarifies the behavior which now makes such a flush unnecessary. Is GetQueryObjectuivARB necessary? RESOLVED: Yes, it makes using a 32-bit count less painful. Should there be a limit on how many queries can be outstanding? RESOLVED: No. This would make the extension much more difficult to spec and use. Allowing this does not add any significant implementation burden; and even if drivers have some internal limit on the number of outstanding queries, it is not expected that applications will need to know this to achieve optimal or near-optimal performance. What happens if BeginQueryARB is called when a query is already outstanding for a different object on the same target? RESOLVED: This is an INVALID_OPERATION error. What happens if BeginQueryARB is called with an ID of a query that is already in progress? RESOLVED: This is also an INVALID_OPERATION error. What parameters should EndQueryARB have? RESOLVED: Just a target. It doesn't need to take an "id" argument, since this would be redundant -- only one query can be active for any given target at a given time. How many bits should we require the samples-passed count to be, at minimum? RESOLVED. The largest minimum that can be required of a GL implementation is 32, the minimum bit width of an int or uint. The minimum number of bits required for the samples-passed count will be dependent on the implementation's maximum viewport size. In order to allow for two overdraws in the case of only one sample buffer, the minimum counter precision (n) will be determined by: n = min (32 , ceil (log2 (maxViewportWidth x maxViewportHeight x 1 sample x 2 overdraws) ) ) An implementation can either set QUERY_COUNTER_BITS_ARB to the value 0, or to some number greater than or equal to n. If an implementation returns 0 for QUERY_COUNTER_BITS_ARB, then the occlusion queries will always return that zero samples passed the occlusion test, and so an application should not use occlusion queries on that implementation. Note that other targets may come along in the future that require more or fewer bits. What should we do about overflows? RESOLVED: Overflows are required to saturate, though it is expected that several current implementations will not conform to this requirement. The ideal behavior is to saturate. This ensures that you always get a "large" result when you render many samples. It also ensures that apps which want a boolean test can do this without worrying about the rare case where the result ends up exactly at zero after wrapping. Either way, it's unlikely that this matters much as long as a sufficient number of bits are required. What is the interaction with multisample? RESOLVED: We count samples, not pixels -- even if MULTISAMPLE is disabled but SAMPLE_BUFFERS is 1. A given fragment may have anywhere between zero and SAMPLES of its samples covered. Ideally, the samples-passed count would be incremented by the precise number of samples, but we permit implementations to instead increment the samples-passed count by SAMPLES if at least one sample in a given fragment is covered. Note that the depth/stencil test optimization whereby implementations may choose to depth test at only one of the samples when MULTISAMPLE is disabled does not cause this to become ill-specified, because we are counting the number of samples that are still alive _after_ the depth test stage. The particular mechanism used to decide whether to kill or keep those samples is not relevant. Exactly what stage in the pipeline are we counting samples at? RESOLVED: We are counting immediately after _both_ the depth and stencil tests, i.e., samples that pass both. Note that the depth test comes after the stencil test, so to say that it is the number that pass the depth test is sufficient; though it is often conceptually helpful to think of the depth and stencil tests as being combined, because the depth test's result impacts the stencil operation used. Is it guaranteed that occlusion queries return in order? RESOLVED: Yes. It makes sense to do this. If occlusion test X occurred before occlusion query Y, and the driver informs the app that occlusion query Y is done, the app can infer that occlusion query X is also done. For applications that do poll, this allows them to do so with less effort. Will polling a query without a Flush possibly cause an infinite loop? RESOLVED: No. An infinite loop was possible in the original NV_occlusion_query spec if an application did not perform a flush prior to polling. This behavior was removed in this version of the spec as it violated language in the core GL spec. Instead of allowing for an infinite loop, performing a QUERY_RESULT_AVAILABLE_ARB will perform a flush if the result is not ready yet on the first time it is queried. This ensures that the async query will return true in finite time. This behavior is not a significant performance loss over the original version of the spec. A flush would need to be performed at some point anyway and the flush performed when QUERY_RESULT_AVAILABLE_ARB is requested will only occur *if the result is not back yet*. What should be the interaction between this spec and HP_occlusion_test? RESOLVED: Whereas NV_occlusion_query required that you implement HP_occlusion_test, and even went so far as to specify the precise behavior of HP_occlusion_test (since the HP_occlusion_test spec did not contain those details), this spec does not. This spec explains the interaction with HP_occlusion_test, but does not attempt to double as a spec for that extension. What happens if HP_occlusion_test and ARB_occlusion_query usage is overlapped? RESOLVED: The two can be overlapped safely. Counting is enabled if either an occlusion query is active *or* OCCLUSION_TEST_HP is enabled. The alternative (producing an error) does not work -- it would require that PopAttrib be capable of producing an error, which would be rather problematic. Note that BeginQueryARB, not EndQueryARB, resets the sample count (and therefore the occlusion test result). This can avoid certain types of strange behavior where an occlusion query's samples-passed count does not always correspond to the samples rendered during the occlusion query. The spec would make sense the other way, but the behavior would be strange. Should there be a "target" parameter to BeginQueryARB? RESOLVED: Yes. Whereas NV_occlusion_query wasn't trying to solve a problem beyond simple occlusion queries, this extension creates a framework useful for future queries. Does GenQueriesARB need a "target" parameter? RESOLVED: No. A query can be reused any number of times with any targets. How can one ask for the currently active query? RESOLVED: A new entry point has been added to query information about a given query target. This makes it unnecessary to add two new enumerants (# of bits and current query ID) for each new target that is introduced. 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. What happens when an app begins a query on a target, ends it, begins a query on the same target with the same id, ends it, and then tries to retrieve data about the query using GetQueryObjecti[u]vARB? Which query does the GetQueryObjecti[u]vARB return results for? RESOLVED. In this case, the result retrieved from GetQueryObjecti[u]vARB will be from the last query on that target and id. The result returned from GetQueryObjecti[u]vARB will always be from the last BeginQueryARB/EndQueryARB pair on that target and id. Is this extension useful for saving geometry, fill rate, or both? The answer to this question is to some extent implementation- dependent, but it is expected that it is most useful for reducing geometry workload, and less so for fill rate. For the cost of rendering a bounding box, you can potentially save rendering a normal object. A bounding box consists of only 12 triangles, whereas the original object might have contained thousands or even millions of triangles. Using bounding box occlusion queries may either help or hurt in fill-limited situations, because rendering the pixels of a bounding box is not free. In most situations, a bounding box will probably have more pixels than the original object. Those pixels can probably be rendered more quickly, though, since they involve only Z reads (no Z writes or color traffic), and they need not be textured or otherwise shaded. In multipass rendering situations, however, occlusion queries can almost always save fill rate, because wrapping an object with an occlusion query is generally cheap. See "Usage Examples" for an illustration. What can be said about guaranteeing correctness when using occlusion queries, especially as it relates to invariance? Invariance is critical to guarantee the correctness of occlusion queries. If occlusion queries go through a different code path than standard rendering, the fragments rendered may be different. However, the invariance issues are difficult at best to solve. Because of the vagaries of floating-point precision, it is difficult to guarantee that rendering a bounding box will render at least as many pixels with equal or smaller Z values than the object itself would have rendered. Likewise, many other aspects of rendering state tend to be different when performing an occlusion query. Color and depth writes are typically disabled, as are texturing, vertex programs, and any fancy per-fragment math. So unless all these features have guarantees of invariance themselves (unlikely at best), requiring invariance for ARB_occlusion_query would be futile. In general, implementations are recommended to be fully invariant with respect to whether any given type of query is active, insofar as it is possible. That is, having an occlusion query active should not affect the operation of any other stage of the pipeline. Following this rule is essential to numerous occlusion query algorithms working correctly. However, to permit implementations where this feature is implemented in software, this rule is only a recommendation, not a requirement. Another unrelated problem that can threaten correctness is near and far clipping. The bounding box of an object may penetrate the near clip plane, even though the original object may not have. In such a circumstance, a bounding box occlusion query may produce an incorrect result. Whenever you design an algorithm using occlusion queries, it is best to be careful about the near and far clip planes. How can frame-to-frame coherency help applications using this extension get even higher performance? Usually, if an object is visible one frame, it will be visible the next frame, and if it is not visible, it will not be visible the next frame. Of course, for most applications, "usually" isn't good enough. It is undesirable, but acceptable, to render an object that *isn't* visible, because that only costs performance. It is generally unacceptable to *not* render an object that *is* visible. The simplest approach is that visible objects should be checked every N frames (where, say, N=5) to see if they have become occluded, while objects that were occluded last frame must be rechecked again in the current frame to guarantee that they are still occluded. This will reduce the number of wasteful occlusion queries by almost a factor of N. Other, more complicated techniques exist but are beyond the scope of this extension document. Do occlusion queries make other visibility algorithms obsolete? No. Occlusion queries are helpful, but they are not a cure-all. They should be only one of many items in your bag of tricks to decide whether objects are visible or invisible. They are not an excuse to skip frustum culling, or precomputing visibility using portals for static environments, or other standard visibility techniques. New Procedures and Functions void GenQueriesARB(sizei n, uint *ids); void DeleteQueriesARB(sizei n, const uint *ids); boolean IsQueryARB(uint id); void BeginQueryARB(enum target, uint id); void EndQueryARB(enum target); void GetQueryivARB(enum target, enum pname, int *params); void GetQueryObjectivARB(uint id, enum pname, int *params); void GetQueryObjectuivARB(uint id, enum pname, uint *params); New Tokens Accepted by the parameter of BeginQueryARB, EndQueryARB, and GetQueryivARB: SAMPLES_PASSED_ARB 0x8914 Accepted by the parameter of GetQueryivARB: QUERY_COUNTER_BITS_ARB 0x8864 CURRENT_QUERY_ARB 0x8865 Accepted by the parameter of GetQueryObjectivARB and GetQueryObjectuivARB: QUERY_RESULT_ARB 0x8866 QUERY_RESULT_AVAILABLE_ARB 0x8867 Additions to Chapter 2 of the OpenGL 1.4 Specification (OpenGL Operation) Modify Section 2.1, OpenGL Fundamentals (p. 4) (modify fourth paragraph, p. 4) It also means that queries and pixel read operations return state consistent with complete execution of all previously invoked GL commands, except where explicitly specified otherwise Additions to Chapter 3 of the OpenGL 1.4 Specification (Rasterization) None. Additions to Chapter 4 of the OpenGL 1.4 Specification (Per-Fragment Operations and the Frame Buffer) Add a new section "Occlusion Queries" between sections 4.1.6 and 4.1.7: "4.1.6A Occlusion Queries Occlusion queries can be used to track the number of fragments or samples that pass the depth test. Occlusion queries are associated with query objects. The command void GenQueriesARB(sizei n, uint *ids); returns previously unused query object names in . These names are marked as used, but no object is associated with them until the first time they are used by BeginQueryARB. 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 DeleteQueriesARB(sizei n, const uint *ids); contains names of query objects to be deleted. After a query object is deleted, its name is again unused. Unused names in are silently ignored. An occlusion query can be started and finished by calling void BeginQueryARB(enum target, uint id); void EndQueryARB(enum target); where is SAMPLES_PASSED_ARB. If BeginQueryARB is called with an unused , that name is marked as used and associated with a new query object. If BeginQueryARB is called while another query is already in progress with the same target, an INVALID_OPERATION error is generated. If EndQueryARB is called while no query with the same target is in progress, an INVALID_OPERATION error is generated. Calling either GenQueriesARB or DeleteQueriesARB while any query of any target is active causes an INVALID_OPERATION error to be generated. BeginQueryARB with a of SAMPLES_PASSED_ARB resets the current samples-passed count to zero and sets the query active state to TRUE and the active query id to . EndQueryARB with a target of SAMPLES_PASSED_ARB initializes a copy of the current samples-passed count into the active occlusion query object's results value, sets the active occlusion query object's result available to FALSE, sets the query active state to FALSE, and the active query id to 0. If BeginQueryARB is called with an of zero, or where is the name of a query currently in progress, an INVALID_OPERATION error is generated. When an occlusion query is active, the samples-passed count increases by a certain quantity for each fragment that passes the depth test. If the value of SAMPLE_BUFFERS is 0, then the samples-passed count increases by 1 for each fragment. If the value of SAMPLE_BUFFERS is 1, then the samples-passed count increases by the number of samples whose coverage bit is set. However, implementations, at their discretion, are allowed to instead increase the samples-passed count by the value of SAMPLES if any sample in the fragment is covered. If the samples-passed count overflows, i.e., exceeds the value 2^n-1 (where n is the number of bits in the samples-passed count), its value becomes undefined. It is recommended, but not required, that implementations handle this overflow case by saturating at 2^n-1 and incrementing no further. The necessary state is a single bit indicating whether an occlusion query is active, the identifier of the currently active occlusion query, and a counter keeping track of the number of samples that have passed." Additions to Chapter 5 of the OpenGL 1.4 Specification (Special Functions) Add to the end of Section 5.4 "Display Lists": "DeleteQueriesARB, GenQueriesARB, IsQueryARB, GetQueryivARB, GetQueryObjectivARB, and GetQueryObjectuivARB are not compiled into display lists but are executed immediately." Additions to Chapter 6 of the OpenGL 1.4 Specification (State and State Requests) Add a new section 6.1.13 "Occlusion Queries": "The command boolean IsQueryARB(uint id); returns TRUE if is the name of a query object. If is zero, or if is a non-zero value that is not the name of a query object, IsQueryARB returns FALSE. Information about a query target can be queried with the command void GetQueryivARB(enum target, enum pname, int *params); If is CURRENT_QUERY_ARB, the name of the currently active query for , or zero if no query is active, will be placed in . If is QUERY_COUNTER_BITS_ARB, the number of bits in the counter for will be placed in . The minimum number of query counter bits allowed is a function of the implementation's maximum viewport dimensions (MAX_VIEWPORT_DIMS). If the counter is non-zero, then the counter must be able to represent at least two overdraws for every pixel in the viewport using only one sample buffer. The formula to compute the allowable minimum value is below (where n is the minimum number of bits): n = (min (32, ceil (log2 (maxViewportWidth x maxViewportHeight x 2) ) ) ) or 0 If the value of n is 0, then the result from GetQueryiv(SAMPLES_PASSED_ARB) will always return 0, The state of a query object can be queried with the commands void GetQueryObjectivARB(uint id, enum pname, int *params); void GetQueryObjectuivARB(uint id, enum pname, uint *params); If is not the name of a query object, or if the query object named by is currently active, then an INVALID_OPERATION error is generated. If is QUERY_RESULT_ARB, then the query object's result value is placed in . Often, query object results will be returned asynchronously with respect to the host processor's operation. As a result, sometimes, if a result is queried, the host must wait until the result is back. If is QUERY_RESULT_AVAILABLE_ARB, the value placed in indicates whether or not such a wait would occur if the result of that query object were to be queried presently. A result of TRUE means no wait would be required; a result of FALSE means that some wait would occur. It must always be true that if the result for one query is available, the result for all previous queries must also be available at that point in time. Querying the state for a given occlusion query forces that occlusion 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]iVARB, 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." Dependencies on HP_occlusion_test When GetIntegerv is called with of OCCLUSION_TEST_RESULT_HP, the current samples-passed count is reset to zero. The occlusion test result is TRUE when the samples-passed count is nonzero, and FALSE when it is zero. Sample counting is active (i.e. the samples- passed count increases as fragments are drawn) whenever either an occlusion query is active *or* OCCLUSION_TEST_HP is enabled. GLX Protocol Seven new GL commands are added. The following two rendering commands are sent to the server as part of a glXRender request: BeginQueryARB 2 12 rendering command length 2 231 rendering command opcode 4 ENUM target 4 CARD32 id EndQueryARB 2 8 rendering command length 2 232 rendering command opcode 4 ENUM target The remaining fivecommands are non-rendering commands. These commands are sent separately (i.e., not as part of a glXRender or glXRenderLarge request), using glx single requests: DeleteQueriesARB 1 CARD8 opcode (X assigned) 1 161 GLX opcode 2 3+n request length 4 GLX_CONTEXT_TAG context tag 4 INT32 n n*4 LISTofCARD32 ids GenQueriesARB 1 CARD8 opcode (X assigned) 1 162 GLX opcode 2 3 request length 4 GLX_CONTEXT_TAG context tag 4 INT32 n => 1 1 reply 1 unused 2 CARD16 sequence number 4 n reply length 24 unused n*4 LISTofCARD32 queries IsQueryARB 1 CARD8 opcode (X assigned) 1 163 GLX opcode 2 3 request length 4 GLX_CONTEXT_TAG context tag 4 CARD32 id => 1 1 reply 1 unused 2 CARD16 sequence number 4 0 reply length 4 BOOL32 return value 20 unused GetQueryivARB 1 CARD8 opcode (X assigned) 1 164 GLX opcode 2 4 request length 4 GLX_CONTEXT_TAG context tag 4 ENUM target 4 ENUM pname => 1 1 reply 1 unused 2 CARD16 sequence number 4 m reply length, m=(n==1?0:n) 4 unused 4 CARD32 n if (n=1) this follows: 4 INT32 params 12 unused otherwise this follows: 16 unused n*4 LISTofINT32 params GetQueryObjectivARB 1 CARD8 opcode (X assigned) 1 165 GLX opcode 2 4 request length 4 GLX_CONTEXT_TAG context tag 4 CARD32 id 4 ENUM pname => 1 1 reply 1 unused 2 CARD16 sequence number 4 m reply length, m=(n==1?0:n) 4 unused 4 CARD32 n if (n=1) this follows: 4 INT32 params 12 unused otherwise this follows: 16 unused n*4 LISTofINT32 params GetQueryObjectuivARB 1 CARD8 opcode (X assigned) 1 166 GLX opcode 2 4 request length 4 GLX_CONTEXT_TAG context tag 4 CARD32 id 4 ENUM pname => 1 1 reply 1 unused 2 CARD16 sequence number 4 m reply length, m=(n==1?0:n) 4 unused 4 CARD32 n if (n=1) this follows: 4 CARD32 params 12 unused otherwise this follows: 16 unused n*4 LISTofCARD32 params Errors The error INVALID_VALUE is generated if GenQueriesARB is called where is negative. The error INVALID_VALUE is generated if DeleteQueriesARB is called where is negative. The error INVALID_OPERATION is generated if GenQueriesARB or DeleteQueriesARB is called when a query of any target is active. The error INVALID_ENUM is generated if BeginQueryARB, EndQueryARB, or GetQueryivARB is called where is not SAMPLES_PASSED_ARB. The error INVALID_OPERATION is generated if BeginQueryARB is called when a query of the given is already active. The error INVALID_OPERATION is generated if EndQueryARB is called when a query of the given is not active. The error INVALID_OPERATION is generated if BeginQueryARB is called where is zero. The error INVALID_OPERATION is generated if BeginQueryARB is called where is is the name of a query currently in progress. The error INVALID_ENUM is generated if GetQueryivARB is called where is not QUERY_COUNTER_BITS_ARB or CURRENT_QUERY_ARB. The error INVALID_OPERATION is generated if GetQueryObjectivARB or GetQueryObjectuivARB is called where is not the name of a query object. The error INVALID_OPERATION is generated if GetQueryObjectivARB or GetQueryObjectuivARB is called where is the name of a currently active query object. The error INVALID_ENUM is generated if GetQueryObjectivARB or GetQueryObjectuivARB is called where is not QUERY_RESULT_ARB or QUERY_RESULT_AVAILABLE_ARB. The error INVALID_OPERATION is generated if any of the commands defined in this extension is executed between the execution of Begin and the corresponding execution of End. New State (table 6.18, p. 233) Get Value Type Get Command Initial Value Description Sec Attribute --------- ---- ----------- ------------- ----------- ------ --------- - B - FALSE query active 4.1.6A - CURRENT_QUERY_ARB Z+ GetQueryiv 0 active query ID 4.1.6A - QUERY_RESULT_ARB Z+ GetQueryObjectuivARB 0 samples-passed count 4.1.6A - QUERY_RESULT_AVAILABLE_ARB B GetQueryObjectivARB FALSE query result available 4.1.6A - New Implementation Dependent State (table 6.29, p. 224) Add the following entry: Get Value Type Get Command Minimum Value Description Sec Attribute -------------------------- ---- ----------- ------------- ---------------- ------ -------------- QUERY_COUNTER_BITS_ARB Z+ GetQueryiv see 6.1.13 Number of bits in 6.1.13 - query counter Revision History Date: 4/21/2007 Revision: 7 (Jon Leech) - Added QUERY_RESULT_ARB and QUERY_RESULT_AVAILABLE to state table 6.18 (also fixed in OpenGL 2.1 spec). Date: 11/4/2006 Revision: 6 (Benj Lipchak, AMD) - Updated contact info after ATI/AMD merger. Date: 10/27/2004 Revision: 5? (James Jones, NVIDIA) - Added GLX protocol. Usage Examples Here is some rough sample code that illustrates how this extension can be used. GLuint queries[N]; GLuint sampleCount; GLint available; GLuint bitsSupported; // check to make sure functionality is supported glGetQueryiv(GL_QUERY_COUNTER_BITS_ARB, &bitsSupported); if (bitsSupported == 0) { // render scene without using occlusion queries } glGenQueriesARB(N, queries); ... // before this point, render major occluders glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_FALSE); // also disable texturing and any fancy shaders for (i = 0; i < N; i++) { glBeginQueryARB(GL_SAMPLES_PASSED_ARB, queries[i]); // render bounding box for object i glEndQueryARB(GL_SAMPLES_PASSED_ARB); } glFlush(); // Do other work until "most" of the queries are back, to avoid // wasting time spinning i = N*3/4; // instead of N-1, to prevent the GPU from going idle do { DoSomeStuff(); glGetQueryObjectivARB(queries[i], GL_QUERY_RESULT_AVAILABLE_ARB, &available); } while (!available); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glDepthMask(GL_TRUE); // reenable other state, such as texturing for (i = 0; i < N; i++) { glGetQueryObjectuivARB(queries[i], GL_QUERY_RESULT_ARB, &sampleCount); if (sampleCount > 0) { // render object i } } Here is some rough sample code for a simple multipass rendering application that does not use occlusion queries. for (i = 0; i < N; i++) { // First rendering pass glDisable(GL_BLEND); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); // configure shader 0 // render object i // Second rendering pass glEnable(GL_BLEND); glBlendFunc(...); glDepthFunc(GL_EQUAL); glDepthMask(GL_FALSE); // configure shader 1 // render object i } Here is the previous example, enhanced using occlusion queries. GLuint queries[N]; GLuint sampleCount; glGenQueriesARB(N, queries); ... // First rendering pass plus almost-free visibility checks glDisable(GL_BLEND); glDepthFunc(GL_LESS); glDepthMask(GL_TRUE); // configure shader 0 for (i = 0; i < N; i++) { glBeginQueryARB(GL_SAMPLES_PASSED_ARB, queries[i]); // render object i glEndQueryARB(GL_SAMPLES_PASSED_ARB); } // Second pass only on objects that were visible glEnable(GL_BLEND); glBlendFunc(...); glDepthFunc(GL_EQUAL); glDepthMask(GL_FALSE); // configure shader 1 for (i = 0; i < N; i++) { glGetQueryObjectuivARB(queries[i], GL_QUERY_RESULT_ARB, &sampleCount); if (sampleCount > 0) { // render object i } }