Name EXT_occlusion_query_boolean Name Strings GL_EXT_occlusion_query_boolean Contributors All those who have contributed to the definition of occlusion query functionality in the OpenGL ARB and OpenGL ES workgroups, upon which this extension spec is entirely dependent. Contact Benj Lipchak, Apple (lipchak 'at' apple.com) Status Complete Version Date: July 22, 2011 Revision: 2 Number OpenGL ES Extension #100 Dependencies Written based on the wording of the OpenGL ES 2.0.25 Full Specification (November 2, 2010). Overview This extension defines a mechanism whereby an application can query whether any pixels (or, more precisely, samples) are 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 each detail object in the scene. On subsequent frames, the previous results of the occlusion queries can be used to decide whether to draw an object or not. New Procedures and Functions void GenQueriesEXT(sizei n, uint *ids); void DeleteQueriesEXT(sizei n, const uint *ids); boolean IsQueryEXT(uint id); void BeginQueryEXT(enum target, uint id); void EndQueryEXT(enum target); void GetQueryivEXT(enum target, enum pname, int *params); void GetQueryObjectuivEXT(uint id, enum pname, uint *params); New Tokens Accepted by the parameter of BeginQueryEXT, EndQueryEXT, and GetQueryivEXT: ANY_SAMPLES_PASSED_EXT 0x8C2F ANY_SAMPLES_PASSED_CONSERVATIVE_EXT 0x8D6A Accepted by the parameter of GetQueryivEXT: CURRENT_QUERY_EXT 0x8865 Accepted by the parameter of GetQueryObjectivEXT and GetQueryObjectuivEXT: QUERY_RESULT_EXT 0x8866 QUERY_RESULT_AVAILABLE_EXT 0x8867 Additions to Chapter 2 of the OpenGL ES 2.0 Specification (OpenGL ES Operation) Add a new section "Asynchronous Queries" between sections 2.12 and 2.13 and renumber subsequent sections: "2.13 Asynchronous Queries Asynchronous queries provide a mechanism to return information about the processing of a sequence of GL commands. There is one query type supported by the GL. Occlusion queries (see section 4.1.6) set a boolean to true when any fragments or samples pass the depth test. The results of asynchronous queries are not returned by the GL immediately after the completion of the last command in the set; subsequent commands can be processed while the query results are not complete. When available, the query results are stored in an associated query object. The commands described in section 6.1.6 provide mechanisms to determine when query results are available and return the actual results of the query. The name space for query objects is the unsigned integers, with zero reserved by the GL. Each type of query supported by the GL has an active query object name. If the active query object name for a query type is non-zero, the GL is currently tracking the information corresponding to that query type and the query results will be written into the corresponding query object. If the active query object for a query type name is zero, no such information is being tracked. A query object is created and made active by calling void BeginQueryEXT(enum target, uint id); indicates the type of query to be performed; valid values of are defined in subsequent sections. If the query object name has not been created, the name is marked as used and associated with a new query object of the type specified by . Otherwise must be the name of an existing query object of that type. BeginQueryEXT fails and an INVALID_OPERATION error is generated if is not a name returned from a previous call to GenQueriesEXT, or if such a name has since been deleted with DeleteQueriesEXT. BeginQueryEXT sets the active query object name for the query type given by to . If BeginQueryEXT is called with an of zero, if the active query object name for is non-zero (for the targets ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the active query for either target is non-zero), if is the name of an existing query object whose type does not match , or if is the active query object name for any query type, the error INVALID_OPERATION is generated. The command void EndQueryEXT(enum target); marks the end of the sequence of commands to be tracked for the query type given by . The active query object for is updated to indicate that query results are not available, and the active query object name for is reset to zero. When the commands issued prior to EndQueryEXT have completed and a final query result is available, the query object active when EndQueryEXT is called is updated by the GL. The query object is updated to indicate that the query results are available and to contain the query result. If the active query object name for is zero when EndQueryEXT is called, the error INVALID_OPERATION is generated. The command void GenQueriesEXT(sizei n, uint *ids); returns previously unused query object names in . These names are marked as used, for the purposes of GenQueriesEXT only, but no object is associated with them until the first time they are used by BeginQueryEXT. Query objects are deleted by calling void DeleteQueriesEXT(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, as is the value zero. 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 (see section C.1). Query objects contain two pieces of state: a single bit indicating whether a query result is available, and an integer containing the query result value. The number of bits used to represent the query result is implementation-dependent and may vary by query object type. In the initial state of a query object, the result is available and its value is zero. The necessary state for each query type is an unsigned integer holding the active query object name (zero if no query object is active), and any state necessary to keep the current results of an asynchronous query in progress. Only a single type of occlusion query can be active at one time, so the required state for occlusion queries is shared." Additions to Chapter 3 of the OpenGL ES 2.0 Specification (Rasterization) None Additions to Chapter 4 of the OpenGL ES 2.0 Specification (Per-Fragment Operations and the Frame Buffer) Add a new section "Occlusion Queries" between sections 4.1.5 and 4.1.6 and renumber subsequent sections: "4.1.6 Occlusion Queries Occlusion queries use query objects to track the number of fragments or samples that pass the depth test. An occlusion query can be started and finished by calling BeginQueryEXT and EndQueryEXT, respectively, with a target of ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT. When an occlusion query is started with the target ANY_SAMPLES_PASSED_EXT, the samples-boolean state maintained by the GL is set to FALSE. While that occlusion query is active, the samples-boolean state is set to TRUE if any fragment or sample passes the depth test. When the occlusion query finishes, the samples-boolean state of FALSE or TRUE 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 target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, an implementation may choose to use a less precise version of the test which can additionally set the samples-boolean state to TRUE in some other implementation dependent cases." Additions to Chapter 5 of the OpenGL ES 2.0 Specification (Special Functions) None Additions to Chapter 6 of the OpenGL ES 2.0 Specification (State and State Requests) Add a new section "Asynchronous Queries" between sections 6.1.5 and 6.1.6 and renumber subsequent sections: "6.1.6 Asynchronous Queries The command boolean IsQueryEXT(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, IsQueryEXT returns FALSE. Information about a query target can be queried with the command void GetQueryivEXT(enum target, enum pname, int *params); identifies the query target, and must be one of ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT. must be CURRENT_QUERY_EXT. The name of the currently active query for , or zero if no query is active, will be placed in . The state of a query object can be queried with the command void GetQueryObjectuivEXT(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. must be QUERY_RESULT_EXT or QUERY_RESULT_AVAILABLE_EXT. If is QUERY_RESULT_EXT, then the query object's result value is returned as a single integer in . 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. There may be an indeterminate delay before the above query returns. If is QUERY_RESULT_AVAILABLE_EXT, 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 any given query object forces that occlusion query to complete within a finite amount of time. Repeatedly querying the QUERY_RESULT_AVAILABLE_EXT state for any given query object is guaranteed to return true eventually. Note that multiple queries to the same occlusion object may result in a significant performance loss. For better performance it is recommended to wait N frames before querying this state. N is implementation dependent but is generally between one and three. If multiple queries are issued using the same object name prior to calling GetQueryObjectuivEXT, the result and availability information returned will always be from the last query issued. The results from any queries before the last one will be lost if they are not retrieved before starting a new query on the same and ." Additions to Appendix C of the OpenGL ES 2.0 Specification (Shared Object and Multiple Contexts) Change the first sentence of the first paragraph of section C.1.2 to read: "When a buffer, texture, renderbuffer, or query is deleted, ..." Add the following sentence to the end of section C.1.2: "A query object is in use so long as it is the active query object for a query type and index, as described in section 2.13." Errors The error INVALID_OPERATION is generated if BeginQueryEXT is called where is not a name returned from a previous call to GenQueriesEXT, or if such a name has since been deleted with DeleteQueriesEXT. The error INVALID_OPERATION is generated if BeginQueryEXT is called where is zero. The error INVALID_OPERATION is generated if BeginQueryEXT is called where is the name of an existing query object whose type does not match . The error INVALID_OPERATION is generated if BeginQueryEXT is called where is the active query object name for any query type. The error INVALID_OPERATION is generated if BeginQueryEXT is called when the active query object name for either ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT is non-zero. The error INVALID_OPERATION is generated if EndQueryEXT is called when the active query object name for is zero. The error INVALID_OPERATION is generated if GetQueryObjectuivEXT is called where is not the name of a query object. The error INVALID_OPERATION is generated if GetQueryObjectuivEXT is called where is the name of a currently active query object. The error INVALID_VALUE is generated if GenQueriesEXT is called where is negative. The error INVALID_VALUE is generated if DeleteQueriesEXT is called where is negative. The error INVALID_ENUM is generated if BeginQueryEXT, EndQueryEXT, or GetQueryivEXT is called where is not ANY_SAMPLES_PASSED_EXT or ANY_SAMPLES_PASSED_CONSERVATIVE_EXT. The error INVALID_ENUM is generated if GetQueryivEXT is called where is not CURRENT_QUERY_EXT. The error INVALID_ENUM is generated if GetQueryObjectuivEXT is called where is not QUERY_RESULT_EXT or QUERY_RESULT_AVAILABLE_EXT. New State (table 6.18, p. 233) Int'l Get Value Type Get Command Value Description Sec -------------------------- ---- -------------------- ----- ---------------------- ----- - B - FALSE query active 4.1.6 CURRENT_QUERY_EXT Z+ GetQueryivEXT 0 active query ID 4.1.6 QUERY_RESULT_EXT B GetQueryObjectuivEXT FALSE samples-passed 4.1.6 QUERY_RESULT_AVAILABLE_EXT B GetQueryObjectuivEXT FALSE query result available 4.1.6 Issues (1) What should the enum be called? RESOLVED: The enum should be called ANY_SAMPLES_PASSED as in ARB_occlusion_query2 to retain compatibility between the two extensions. (2) Can application-provided names be used as query object names? ARB_occlusion_query allows application-provided names, but this was later removed in core OpenGL. RESOLVED: No, we will follow core OpenGL on this. (3) Should calling GenQueries or DeleteQueries when a query is active produce an error? This behavior is in ARB_occlusion_query but was removed in OpenGL 3.0. RESOLVED: Not an error. Calling DeleteQueries marks the name as no longer used, but the object is not deleted until it is no longer in use (i.e. no longer active). (4) What is the interaction with multisample? RESOLVED: The query result is set to true if at least one sample passes the depth test. (5) 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. (6) Is it guaranteed that occlusion queries return in order? RESOLVED: Yes. 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. (7) Will polling a query for QUERY_RESULT_AVAILABLE without a Flush possibly cause an infinite loop? RESOLVED: No. (8) Should there be a "target" parameter to BeginQuery? RESOLVED: Yes. This distinguishes the boolean queries defined by this extension (and ARB_occlusion_query2) from the counter queries defined by ARB_occlusion_query. (9) 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. (10) Should there be a limit on how many queries can be outstanding? RESOLVED: No. If an implementation has an internal limit, it can flush the pipeline when it runs out. (11) Can an implementation sometimes return a conservative result, i.e. return true even though no samples were drawn? RESOLVED: Yes, but only when explicitly enabled by the application. Allowing such results with no restrictions effectively makes the functionality of the extension optional, which decreases its value. Precise restrictions are presumably hard to specify. One situation where this restriction could be relevant is if an implementation performs a conservative early depth test at a lower precision and wants to base the occlusion query result on that whenever the early depth test can be used. (12) Should the restrictions in issue 11 be explicitly enabled by the application in order to be in effect? RESOLVED: Yes. The restrictions could be enabled by a hint call or by using a different enum in the BeginQuery call. This would enable the application to choose whether it wants a precise (but possibly slow) version or an approximate (but possibly faster) version. (13) Can the restrictions in issue 18 be applied nondeterministically? An implementation might benefit from taking the decision of whether to apply a particular restriction on a case by case basis. Some of these decisions could depend on nondeterministic effects such as memory bus timing. RESOLVED: No. This would violate the GL repeatability principle. (14) How does an application request that the result is allowed to be conservative (as defined in issue 11)? RESOLVED: It is specified as a separate query target, ANY_SAMPLES_PASSED_CONSERVATIVE. Revision History Date: 5/03/2011 Revision: 1 (Benj Lipchak) - Initial draft based on XXX_occlusion_query_boolean Date: 7/22/2011 Revision: 2 (Benj Lipchak) - Rename from APPLE to EXT