Name

    ARB_occlusion_query2

Name Strings

    GL_ARB_occlusion_query2

Contributors

    Aske Simon Christensen
    Bill Licea-Kane
    Cass Everitt
    Jeff Bolz
    Maurice Ribble

Contact

    Bill Licea-Kane (bill 'at' amd.com)

Notice

    Copyright (c) 2010-2013 The Khronos Group Inc. Copyright terms at
        http://www.khronos.org/registry/speccopyright.html

Status

    Complete. Approved by the ARB at the 2010/01/22 F2F meeting.
    Approved by the Khronos Board of Promoters on March 10, 2010.

Version

    Date: March 21, 2010
    Revision: 11
    $Id$

Number

    ARB Extension #80

Dependencies

    Written based on the wording of OpenGL Specification
    Version 3.2 (Core Profile) - July 24, 2009

    Version 3.2 (Compatibility Profile) interacts with this extension.

    OpenGL 1.x is required.

    ARB_occlusion_query interacts with this extension.

    Conditional rendering interacts with this extension.

Overview

    This extension trivially adds a boolean occlusion query
    to ARB_occlusion_query.

    While the counter-based occlusion query provided by
    ARB_occlusion_query is flexible, there is still value
    to a simple boolean, which is often sufficient for applications.

IP Status

    There are no known claims.

    HP claimed IP to a related extension, ARB_occlusion_query.
    HP committed to releasing rights to this IP to the ARB if the
    functionality is included in OpenGL (April 10, 2003).
    ARB_occlusion_query was promoted to core OpenGL 1.5 (July 29, 2003).

New Procedures and Functions

    None

New Tokens

    Accepted by the <target> parameter of BeginQuery, EndQuery,
    and GetQueryiv:

        ANY_SAMPLES_PASSED                              0x8C2F

Additions to Chapter 2 of the OpenGL 3.2 Specification
(OpenGL Operation)

2.14
Asynchronous Queries

p. 89-90, replace last sentence of first paragraph

   Occlusion queries (see section 4.1.6) count the number of fragments
   or samples that pass the depth test,
|  or set a boolean to true when fragments or samples pass the depth
|  test.

p. 90, modify the last paragraph before "The command void EndQuery..."

   BeginQuery sets the active query object name for the query type given by
   target to id. If BeginQuery is called with an id of zero, if the active query object
   name for target is non-zero
|  (for the targets SAMPLES_PASSED or ANY_SAMPLES PASSED, if the active query object
|  for either target is non-zero),
   if id is the name of an existing query object whose
   type does not match target, if id is the active query object name for any query type,
   or if id is the active query object for condtional rendering (see section 2.15), the
   error INVALID_OPERATION is generated.

2.15
Conditional Rendering

p. 91

Add clause prior to "all rendering commands between"

   "or if the result (ANY_SAMPLES_PASSED) is false,"

Add clause prior to "such commands are not discarded."

   "or if the result is true,"

p. 92

Change last sentence of Section 2.15

   The error
   INVALID_OPERATION is generated if id is the name of a query object with a target
   other than
|  SAMPLES_PASSED
|  or ANY_SAMPLES_PASSED,
   or id is the name of a query currently in progress.

Additions to Chapter 4 of the OpenGL 3.2 Specification
(Per-Fragment Operations and the Frame Buffer)

p. 192, Replace 4.1.6
Occlusion Queries

   Occlusion queries use query objects to track fragments or samples
   that pass the depth test. An occlusion query can be started and
   finished by calling BeginQuery and EndQuery, respectively, with a target
   of
|  SAMPLES_PASSED or ANY_SAMPLES_PASSED.

   When an occlusion query is started
|  with the target SAMPLES_PASSED,
   the samples-passed count maintained by the GL is set to zero. While that
   occlusion query is active, the samples-passed count is incremented for
   each fragment that passes the depth test. If the value of SAMPLE_BUFFERS
   is 0, then the samples-passed count is incremented by 1 for each fragment.
   If the value of SAMPLE_BUFFERS is 1, then the samples-passed count is
   incremented by the number of samples whose coverage bit is set. However,
   implementations, at their discretion, may instead increase the samples-passed
   count by the value of SAMPLES if any sample in the fragment is covered.
   When an occlusion query finishes and all fragments generated by commands
   issued prior to EndQuery have been generated, the samples-passed count 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 samples-passed count overflows (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.

|  When an occlusion query is started with the target ANY_SAMPLES_PASSED,
|  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.


Additions to Chapter 6 of the OpenGL 3.2 Specification
(State and State Requests)

6.1.6
Asynchronous Queries
p. 255

Modify the Sentence beginning with "Information about the query target can be..."

   ...target idendifies the query target, and must be one of
   | SAMPLES_PASSED or ANY_SAMPLES_PASSED
   for occlusion queries...

Modify the paragraph beginning with "For occlusion queries (SAMPLES_PASSED)..."

   For occlusion queries
|  (SAMPLES_PASSED and ANY_SAMPLES_PASSED), the number of bits
|  depends on the target.  For a target of ANY_SAMPLES_PASSED, if
|  the number of bits is non-zero,  the minimum number of bits is 1.  For a target
|  of SAMPLES_PASSED,
   if the number of bits is non-zero, ...


Dependencies

Version 3.2 (Compatibility Profile) interacts with this extension.

   Other than adjusting the page numbers referenced in the edits, there are no
   further changes required for the Compatibility Profile specification.

ARB_occlusion_query

   If ARB_occlusion_query is not supported, then delete the enum
   SAMPLES_PASSED and delete sections of text refering to
   SAMPLES_PASSED, and incorporate all other enums, entry
   points and state introduced by ARB_occlusion_query into
   this extension.

Conditional Rendering

   If Conditional Rendering is not supported, delete edits to
   section on Conditional Rendering.

New State

(table 6.33, p. 298)

    Get Value         Type    Get Command          Initial Value   Description              Sec     Attribute
    ---------         ----    -----------          -------------   -----------              ------  ---------
    QUERY_RESULT      Z+      GetQueryObjectuiv    0               samples-passed count     6.1.6   -
|                                                  FALSE           or any-samples-passed
|                                                                  boolean value


Usage Examples

    Here is some rough sample code that illustrates how this extension
    can be used.

        GLuint    queries[N];
        GLint     sampleBoolean;
        GLint     available;


        glGenQueries(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++) {
            glBeginQuery(GL_ANY_SAMPLES_PASSED, queries[i]);
            // render bounding box for object i
            glEndQuery(GL_ANY_SAMPLES_PASSED);
        }

        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();
            glGetQueryObjectiv(queries[i],
                                  GL_QUERY_RESULT_AVAILABLE,
                                  &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++) {
            glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT,
                                   &sampleBoolean);
            if (sampleBoolean != 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 sampleBoolean;

        glGenQueries(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++) {
            glBeginQuery(GL_ANY_SAMPLES_PASSED, queries[i]);
            // render object i
            glEndQuery(GL_ANY_SAMPLES_PASSED);
        }

        // 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++) {
            glGetQueryObjectuiv(queries[i], GL_QUERY_RESULT,
                                   &sampleBoolean);
            if (sampleBoolean != 0) {
                // render object i
            }
        }


Issues

    1) Can an occlusion query with a target of SAMPLES_PASSED
       be active at the same time as an occlusion query with a
       target of ANY_SAMPLES_PASSED?

       Resolved.  No!  Specifically, it is an error to BeginQuery for
       the targets SAMPLES_PASSED or ANY_SAMPLES_PASSED if the active
       query is non-zero for EITHER target.

    2) How many query types are there?

       Unresolved.

       The spec says "There are two query types" (p. 89), and
       list them as Transform Feedback Queries and Occlusion Queries.

       The spec says "Each type of query supported by the GL has an active
       query object name."  (p. 90)

       Table 6.47 Miscellaneous (p. 312) says there are
       CURRENT_QUERY 3 x Z+.

       I believe there are three:

       Primitive Queries (target PRIMITIVES_GENERATED)
       Transform Feedback Queries (target TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)
       and
       Occlusion Queries (target SAMPLES_PASSED or ANY_SAMPLES_PASSED)

       It is probable that the spec should say "There are three query types"
       and list them as Primitive Queries (section 2.17), Transform Feedback
       Queries (section 2.16) and Occlusion Queries (section 4.1.6).

       A separate bug will be posted for the core specification to clarify
       Primitive Queries and Transform Feedback.

       This extension currently assumes that the bug will be resolved and only
       edits the Occlusion Queries type to expand it to two targets.

    3) Can we limit the number of query objects active?

       Unresolved.  This draft doesn't.

       But, yes, we can limit the number of query objects active.
       The language to do so would be straightforward, but why?

       There can only be ONE occlusion query active at a time.  The query object
       created (or re-used) at BeginQuery contains the state to save the
       boolean.  At EndQuery the boolean for the current query is copied to
       the query object's state.

       (Clearly, OES can make another choice.  But not sure why right now.)

    4) Currently, if a draw within BeginQuery/EndQuery passes a depth
       test, and a subsequent draw later covers all the pixels, can an
       implementation return false (assuming the application queried
       the result after the subsequent draw)?

       Unresolved.  This draft defers the issue.
       The language to allow this is non-trivial.

       Please note, the typical application will:

       * set the color/depth masks to true, and blend state
       * draw the major occluders
       * draw visible opaque objects (coarsly sorted if possible)
       * set the color/depth masks to false
       * draw proxies for geometry which *might* be visible, with occlusion queries
       * set the color masks to true, and blend state
       * draw any transparent objects (sorted if possible)
       * swap

       * set the color/depth masks to true, and blend state
       * draw the major occluders
       * test the queries that are available from the prior frame
       * draw visible and newly visible opaque objects (coarsly sorted if possible)
       * set the color/depth masks to false
       * draw proxies for geometry which *might* be visible, with occlusion queries
       * set the color masks to true, and blend state
       * draw any transparent objects (sorted if possible)
       * swap

       This technique has the disadvantage that the newly exposed
       geometry is one frame late, but this is generally not considered
       an issue.  The advantage that in general the results are available by
       the time the query is tested.

       Note that no opaque occluders follow the queries in a frame.

       Another common variation is:

       * set the color/depth masks to true, and blend state
       * draw the major occluders
       * draw the visible opaque objects (corsely sorted if possible)
         with occlusion queries
       * set the color/depth mask to false
       * draw proxies for geometry which *might* be visible, with occlusion queries
       * set the color masks to true, and blend state
       * draw any transparent objects (sorted if possible)
       * swap

       * set the color/depth masks to true, and blend state
       * draw the major occluders
       * test the queries that are available from the prior frame
       * draw the still visible and newly visible opaque objects (coarsely sorted if possible)
         with occlusion queries
       * set the color/depth mask to false
       * draw proxies for geometry which *might* be visible, with occlusion queries
       * set the color masks to true, and blend state
       * draw any transparent objects (sorted if possible)
       * swap

       This technique still has the disadvantage that newly exposed
       geometry is one frame late, but still not considered an issue.
       The advantage is that in general the results are available by the
       time the query is tested.

       This technique also culls geometry that was visible as it gets
       occluded.  (Though there can be some "picket fence" issues
       and the occlusion query for actual geometry and proxy geometry
       can cycle between false-true.)

       Note that opaque occluders follow the queries in a frame.

    5) What is this extension called?

       Resolved.
       Earlier drafts were EXT_occlusion_query2.
       This draft is ARB_occlusion_query2.

    6) What should the enum be called?

       Resolved.
       ANY_SAMPLES_PASSED.

    7) Do we need "state required" for occlusion query objects?

       Resolved.  No.
       That listing of state required had been moved to asynchronous
       queries Section 2.14.  It has been excised from the occlusion
       query section.

    8) Can an occlusion query "morph" from SAMPLES_PASSED to
       ANY_SAMPLES_PASSED?  That is, can the sequence
       BeginQuery( SAMPLES_PASSED )...EndQuery( ANY_SAMPLES_PASSED )
       (or vice-versa)?

       Resolved.  No.  Note this falls naturally by leaving the
       EndQuery error language alone.  ("If the active query object
       name for target is zero when EndQuery is called, the error
       INVALID_OPERATION is generated.")

Revision History
    2010-03-21
    11, pbrown
    Minor typo/wording fixes.

    2010-01-26
    10, pbrown
    Assign enum value for ANY_SAMPLES_PASSED, using same allocation from
    OES_occlusion_query.  Enums are functionally equivalent.

    2010-01-21
    9, wwlk
    JUST update version, date

    2010-01-20
    8, wwlk
    Update issue 1, add issues 7 and 8.
    Update the spec language to make it clear that you can't
    start an occlusion query if an occlusion query (of either type) is
    already active.
    Update the spec language to trivially clarify conditional rendering.

    2009-12-10
    7, Jon Leech
    Remove ARB suffixes for core 3.3 spec inclusion. Fix equation
    typos.

    2009-11-13
    6, wwlk
    Updated name and name strings to match ARB_occlusion_query2

    2009-10-22
    5, wwlk
    Renamed to ARB_occlusion_query2 (and changed EXT to ARB)
    Renamed ENUM (and updated ENUMs throughout) to ANY_SAMPLES_PASSED
    Misc typos (Synchronos, COmpatibility)

    2009-10-02
    4, wwlk
    Updates from comments.
    Renamed to EXT_occlusion_query_boolean
    Renamed ENUM(s)
    Elaborated on issue 4.

    2009-09-29
    3, wwlk
    updates from comments.
    Added issues 4,5,6.

    2009-09-28
    2, wwlk
    minor typo, add conditional rendering

    2009-09-28
    1, wwlk
    first draft
