INTEL_performance_query

Name

INTEL_performance_query

Name Strings

GL_INTEL_performance_query

Contact

Tomasz Madajczak, Intel (tomasz.madajczak ‘at' intel.com)

Contributors

Piotr Uminski, Intel
Slawomir Grajewski, Intel

Status

Complete, shipping on selected Intel graphics.

Version

Last Modified Date: December 20, 2013
Revision: 3

Number

OpenGL Extension #443
OpenGL ES Extension #164

Dependencies

OpenGL dependencies:

    OpenGL 3.0 is required.

    The extension is written against the OpenGL 4.4 Specification, Core
    Profile, October 18, 2013.

OpenGL ES dependencies:

    This extension is written against the OpenGL ES 2.0.25 Specification
    and OpenGL ES 3.0.2 Specification.

Overview

The purpose of this extension is to expose Intel proprietary hardware
performance counters to the OpenGL applications. Performance counters may
count:

- number of hardware events such as number of spawned vertex shaders. In
  this case the results represent the number of events.

- duration of certain activity, like time took by all fragment shader
  invocations. In that case the result usually represents the number of
  clocks in which the particular HW unit was busy. In order to use such
  counter efficiently, it should be normalized to the range of <0,1> by
  dividing its value by the number of render clocks.

- used throughput of certain memory types such as texture memory. In that
  case the result of performance counter usually represents the number of
  bytes transferred between GPU and memory.

This extension specifies universal API to manage performance counters on
different Intel hardware platforms. Performance counters are grouped
together into proprietary, hardware-specific, fixed sets of counters that
are measured together by the GPU.

It is assumed that performance counters are started and ended on any
arbitrary boundaries during rendering. 

A set of performance counters is represented by a unique query type. Each
query type is identified by assigned name and ID. Multiple query types
(sets of performance counters) are supported by the Intel hardware. However
each Intel hardware generation supports different sets of performance
counters.  Therefore the query types between hardware generations can be
different. The definition of query types and their results structures can
be learned through the API. It is also documented in a separate document of
Intel OGL Performance Counters Specification issued per each new hardware
generation.

The API allows to create multiple instances of any query type and to sample
different fragments of 3D rendering with such instances. Query instances
are identified with handles.

New Procedures and Functions

void GetFirstPerfQueryIdINTEL(uint *queryId);

void GetNextPerfQueryIdINTEL(uint queryId, uint *nextQueryId);

void GetPerfQueryIdByNameINTEL(char *queryName, uint *queryId);

void GetPerfQueryInfoINTEL(uint queryId,
         uint queryNameLength, char *queryName,
         uint *dataSize, uint *noCounters, 
         uint *noInstances, uint *capsMask);

void GetPerfCounterInfoINTEL(uint queryId, uint counterId,
         uint counterNameLength, char *counterName,
         uint counterDescLength, char *counterDesc,
         uint *counterOffset, uint *counterDataSize, uint *counterTypeEnum,
         uint *counterDataTypeEnum, uint64 *rawCounterMaxValue);

void CreatePerfQueryINTEL(uint queryId, uint *queryHandle);

void DeletePerfQueryINTEL(uint queryHandle);

void BeginPerfQueryINTEL(uint queryHandle);

void EndPerfQueryINTEL(uint queryHandle);

void GetPerfQueryDataINTEL(uint queryHandle, uint flags,
         sizei dataSize, void *data, uint *bytesWritten);

New Tokens

Returned by the capsMask parameter of GetPerfQueryInfoINTEL

    PERFQUERY_SINGLE_CONTEXT_INTEL          0x0000
    PERFQUERY_GLOBAL_CONTEXT_INTEL          0x0001

Accepted by the flags parameter of GetPerfQueryDataINTEL

    PERFQUERY_WAIT_INTEL                    0x83FB
    PERFQUERY_FLUSH_INTEL                   0x83FA
    PERFQUERY_DONOT_FLUSH_INTEL             0x83F9

Returned by GetPerfCounterInfoINTEL function as counter type enumeration in
location pointed by counterTypeEnum

    PERFQUERY_COUNTER_EVENT_INTEL           0x94F0
    PERFQUERY_COUNTER_DURATION_NORM_INTEL   0x94F1
    PERFQUERY_COUNTER_DURATION_RAW_INTEL    0x94F2
    PERFQUERY_COUNTER_THROUGHPUT_INTEL      0x94F3
    PERFQUERY_COUNTER_RAW_INTEL             0x94F4
    PERFQUERY_COUNTER_TIMESTAMP_INTEL       0x94F5

Returned by glGetPerfCounterInfoINTEL function as counter data type
enumeration in location pointed by counterDataTypeEnum

    PERFQUERY_COUNTER_DATA_UINT32_INTEL     0x94F8
    PERFQUERY_COUNTER_DATA_UINT64_INTEL     0x94F9
    PERFQUERY_COUNTER_DATA_FLOAT_INTEL      0x94FA
    PERFQUERY_COUNTER_DATA_DOUBLE_INTEL     0x94FB
    PERFQUERY_COUNTER_DATA_BOOL32_INTEL     0x94FC

Accepted by the parameter of GetIntegerv:

    PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL   0x94FD
    PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE
    PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF

Accepted by the <pname> parameter of GetBooleanv:

    PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL   0x9500

Add new Section 4.4 to Chapter 4, Event Model for OpenGL 4.4 Add new Section 2.18 to Chapter 2, OpenGL ES Operation for OpenGL ES 3.0.2

4.4 Performance Queries (for OpenGL 4.4)
2.18 Performance Queries (for OpenGL ES 3.0.2)

Hardware and software performance counters can be used to obtain
information about GPU activity. Performance counters are grouped into query
types. Different query types can be supported on different hardware
platforms and/or driver versions. One or more instances of the query types
can be created.

Each query type has unique query ID. Query ids supported on given platform
can be queried in the run-time. Function:

    void GetFirstPerfQueryIdINTEL(uint *queryId);

returns the identifier of the first performance query type that is
supported on a given platform. The result is passed in location pointed by
queryId parameter. If the given hardware platform doesn't support any
performance queries, then the value of 0 is returned and INVALID_OPERATION
error is raised. If queryId pointer is equal to 0, INVALID_VALUE error is
generated.

Next query ids can be queried by multiply call to the function:

    void GetNextPerfQueryIdINTEL(uint queryId, uint *nextQueryId);

This function returns the integer identifier of the next performance query
on a given platform to the specified with queryId. The result is passed in
location pointed by nextQueryId. If query identified by queryId is the last
query available the value of 0 is returned. If the specified performance
query identifier is invalid then INVALID_VALUE error is generated. If
nextQueryId pointer is equal to 0, an INVALID_VALUE error is
generated. Whenever error is generated, the value of 0 is returned.

Each performance query type has a name and a unique identifier. The query
identifier for a given query name be read using function:

    void GetPerfQueryIdByNameINTEL(char *queryName, uint *queryId);

This function returns the identified of the query type specified by the
string provided as queryName parameter.  If queryName does not reference a
valid query name, an INVALID_VALUE error is generated.

General description of a query type can be read using the function:

    void GetPerfQueryInfoINTEL(uint queryId, uint queryNameLength,
        char *queryName, uint *dataSize,
        uint *noCounters, uint *maxInstances,
        uint *noActiveInstances, uint *capsMask);

The function returns information about the performance query specified with
queryId parameter, particularly:

-  query name in queryName location. The maximal name is specified by
   queryNameLength

-  size of query output structure in bytes in dataSize location

-  number of performance counters in the query output structure in
   noCounters location

-  the maximal allowed number of query instances that can be created on a
   given architecture in maxInstances location. Because the other type queries
   are created using the same resources, it may happen that the actual amount
   of created instances is smaller than the returned number

-  the actual number of already created query instances in maxInstances
   location

-  mask of query capabilities in capsMask location.

If the mask returned in capsMask contains PERFQUERY_SINGLE_CONTEXT_INTEL
token this means the query supports context sensitive measurements,
otherwise, if the mask contains token of GL_PERFQUERY_GLOBAL_CONTEXT_INTEL
this means the query doesn't support that feature and the counters will be
updated for all render contexts as they are global for hardware.

If queryId does not reference a valid query type, an INVALID_VALUE error is
generated.

Performance counters that belong to the same query type have unique
ids. Performance counter ids values start with 1. Performance counter id 0
is reserved as an invalid counter. Information about performance counters
that belongs to a given query type can be read using the function:

void GetPerfCounterInfoINTEL(uint queryId, uint counterId, 
     uint counterNameLength, char *counterName, 
     uint counterDescLength, char *counterDesc, 
     uint *counterOffset, uint *counterDataSize, uint *counterTypeEnum,
     uint *counterDataTypeEnum, uint64 *rawCounterMaxValue);

The function returns descriptive information about each particular
performance counter that is an element of the performance query. The
counter is identified with a pair of queryId and counterId parameters. The
following parameters are returned:

-  counter name in counterName location. The maximal length of copied name
   is specified with counterNameLength.

-  counter description text in  counterDesc location. The maximal length of
   copied text is specified with counterDescLength.

-  byte offset of the counter from the start of the query structure in
   counterOffset location.

-  counter size in bytes in  counterDataSize location.

-  counter type enumeration in counterTypeEnum location. It can be one o
   the following tokens:
       PERFQUERY_COUNTER_EVENT_INTEL
       PERFQUERY_COUNTER_DURATION_NORM_INTEL
       PERFQUERY_COUNTER_DURATION_RAW_INTEL
       PERFQUERY_COUNTER_THROUGHPUT_INTEL
       PERFQUERY_COUNTER_RAW_INTEL
       PERFQUERY_COUNTER_TIMESTAMP_INTEL

-  counter data type enumeration, in counterDataTypeEnum location. It can
   be one o the following tokens:
       PERFQUERY_COUNTER_DATA_UINT32_INTEL
       PERFQUERY_COUNTER_DATA_UINT64_INTEL
       PERFQUERY_COUNTER_DATA_FLOAT_INTEL
       PERFQUERY_COUNTER_DATA_DOUBLE_INTEL
       PERFQUERY_COUNTER_DATA_BOOL32_INTEL

-  for some raw counters for which the maximal value is deterministic, the
   maximal value of the counter in 1 second is returned in the location
   pointed by rawCounterMaxValue, otherwise, the location is written with
   the value of 0.

If the pair of queryId and counterId does not reference a valid counter,
an INVALID_VALUE error is generated.

A single instance of the performance query of a given type can be created
using function:

    void CreatePerfQueryINTEL(uint queryId, uint *queryHandle);

The handle to newly created query instance is returned in queryHandle
location. If queryId does not reference a valid query type,
an INVALID_VALUE error is generated. If the query instance cannot be
created due to exceeding the number of allowed instances or driver fails
query creation due to an insufficient memory reason, an OUT_OF_MEMORY error
is generated, and the location pointed by queryHandle returns NULL.
Existing query instance can be deleted using function

    void DeletePerfQueryINTEL(uint queryHandle);

queryHandle must be a query instance handle returned by
CreatePerfQueryINTEL(). If a query handle doesn't reference a previously
created performance query instance, an INVALID_VALUE error is generated.

A new measurement session for a given query instance can be started using
function:

    void BeginPerfQueryINTEL(uint queryHandle);

where queryHandle must be a query instance handle returned by
CreatePerfQueryINTEL(). If a query handle doesn't reference a previously
created performance query instance, an INVALID_VALUE error is
generated. Note that some query types, they cannot be collected in the same
time. Therefore calls of BeginPerfQueryINTEL() cannot be nested if they
refer to queries of such different types. In such case INVALID_OPERATION
error is generated.

The counters may not start immediately after BeginPerfQueryINTEL().
Because the API and GPU are asynchronous, the start of performance counters
is delayed until the graphics hardware actually executes the hardware
commands issued by this function.  However, it is guaranteed that collecting
of performance counters will start before any draw calls specified in the
same context after call to BeginPerfQueryINTEL().

Collecting performance counters may be stopped by a function:

    void EndPerfQueryINTEL(uint queryHandle);

where queryHandle must be a query instance handle returned by
CreatePerfQueryINTEL(). The function ends the measurement session started
by BeginPerfQueryINTEL().  If a performance query is not currently started,
an INVALID_OPERATION error will be generated. Similarly as in
glBeginPerfQueryINTEL() case, the execution of glEndPerfQueryINTEL() is not
immediate. The end of measurement is delayed until graphics hardware
completes processing of the hardware commands issued by this
function. However, it is guaranteed that results any draw calls specified in
the same context after call to EndPerfQueryINTEL() will be not measured by
this query.

The query result can be read using function:

    void GetPerfQueryDataINTEL(uint queryHandle, uint flags, sizei
        dataSize, void *data, uint *bytesWritten);

The function returns the values of counters which have been measured within
the query session identified by queryHandle.  The call may end without
returning any data if they are not ready for reading as the measurement
session is still pending (the EndPerfQueryINTEL() command processing is not
finished by hardware). In this case location pointed by the bytesWritten
parameter will be set to 0. The meaning of the flags parameter is the
following:

-  PERFQUERY_DONOT_FLUSH_INTEL means that the call of
   GetPerfQueryDataINTEL() is non-blocking, which checks for results and
   returns them if they are available. Otherwise, (if the results of the
   query are not ready) it returns without flushing any outstanding 3D
   commands  to the GPU. The use case for this is when a flush of
   outstanding 3D commands to GPU has already been ensured with other
   OpenGL API calls. 
 
-  PERFQUERY_FLUSH_INTEL means that the call of GetPerfQueryDataINTEL() is
   non-blocking, which checks for results and returns them if they are
   available. Otherwise, it implicitly submits any outstanding 3D commands
   to the GPU for execution. In that case the subsequent call of
   glGetPerfQueryDataINTEL() may return data once the query completes.

-  PERFQUERY_WAIT_INTEL means that the call of GetPerfQueryDataINTEL() is
   blocking and waits till the query results are available and returns
   them. It means that if the query results are not yet available then it
   implicitly submits any outstanding 3D commands to GPU and waits for the
   query completion.

If the measurement session indentified by queryHandle is completed then the
call of GetPerfQueryDataINTEL() always writes query result to the location
pointed by the data parameter and the amount of bytes written is stored in
the location pointed by the bytesWritten parameter.

If bytesWritten or data pointers are NULL then an INVALID_VALUE error is
generated.

New Implementation Dependent State

Add new Table 23.75 to Chapter 23, State Tables (OpenGL 4.4) Add new Table 6.37 to Chapter 6.2, State Tables (OpenGL ES 3.0.2)

Get Value                              Type Get Command Value Description
------------------------------         ---- ----------- ----- -------------
PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL   Z+ GetIntegerv  256   max query name length
PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL Z+ GetIntegerv  256   max counter name length
PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL Z+ GetIntegerv  1024  max description length
PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL   B  GetBooleanv  -     extended counters available

Issues

1. What is the usage model of this extension?

Generally there are two approaches of measuring performance with Intel OGL
Performance Queries, such as:

- Per draw call measurements - performance counters can be used to assess
  the business of particular 3D hardware units under assumption that 3D
  hardware is almost 100% time busy from the CPU point of view.

- Per 3D scene measurements - performance counters can be used to assess
  the balance of CPU and GPU processing times. Such assessment shows whether
  the workload is CPU whether GPU bound.

2. How per draw call measurements are performed?

   In the per-draw call usage model each call to the draw routine
   (e.g. glDrawArrays, glDrawElements) should be surrounded by a dedicated
   query instance. That means that each draw operation should be measured
   independently. It is recommended to measure the GPU performance
   characteristics for a single draw call to find possible bottlenecks
   for the application executed on a given hardware.

3. How per scene measurements are performed?

   The usage model assumes that one performance query instance measures a
   complete scene. It is recommended to figure out if the workload is CPU
   or GPU bound. It should be noted that:

   - For a longer scope of performance query the probability of 3D hardware
     frequency change is higher. The higher probability of frequency change
     causes that the larger percentage of results may be biased with gross
     errors. 

   - For complicated 3D scenes the condition of render commands split is
     always met. 

   Thus, to calculate an average 3D hardware unit utilization for a longer
   period of time it is recommended to use a larger number of per draw call
   queries rather than a lower number of per 3D scene queries. It is
   recommended to use this method when application uses full screen mode as
   current implementation of queries supports only global context.

4. How results of the query can be read?

   Results of the queries cannot be read before the entire drawing is done
   by the GPU. This means that the application programmer has to decide
   about the synchronization method it uses to read the query
   results. There are the following options:

   - Use glFlush to trigger submission of any pending commands to the
     GPU. Later check results availability with repetitive non-blocking
     calls to GetPerfQueryDataINTEL function using the synchronization flag
     of GL_PERFQUERY_DONOT_FLUSH_INTEL. 

   - Use flag GL_PERFQUERY_FLUSH_INTEL in glGetPerfQueryDataINTEL to
     trigger submission of any pending commands to the GPU. If results are
     not immediately available, check their availability with repetitive
     non-blocking calls to GetPerfQueryDataINTEL function using the
     synchronization flag of GL_PERFQUERY_DONOT_FLUSH_INTEL.

   - Do a blocking call to glGetPerfQueryDataINTEL() with
     GL_PERFQUERY_WAIT_INTEL flag set. The flag ensures that any pending GPU
     commands are submitted and function blocks till GPU results are
     available.

   It is allowed to perform simultaneous measurements with multiple active
   queries of the same type. However it may be not allowed to perform
   simultaneous measurements of queries with different types, as it may
   require reprogramming of the same hardware part and could destroy the
   hardware settings of the previous query.

5. Are query results always accurate?

   There are certain hardware conditions which may cause the results
   of performance counters expressed in hardware clocks to be inaccurate.
   The conditions may include:

   - Render clock change -  the condition usually causes that all counter
     values expressed in hardware clocks are incorrect. It is indicated by
     FrequencyChanged flag.
     
   - Render commands split - in some cases GPU has to split execution of
     drawing operations surrounded by the query into at least two
     parts. The condition usually causes that counter values expressed in
     time domains (in microseconds) may be substantially larger than the
     average values of that counter. It is indicated by SplitOccured flag.

   - Rendering preemption - if GPU is shared among two or more 3D
     applications, the hardware counters gathered in a global mode contain
     additive results for these applications. The condition is also
     indicated with SplitOccured flag.

   The above conditions are indicated in special fields in the query
   results structures. It is up to the user to decide if the results are to
   be processed further or dropped. In certain cases it can be determined
   that the render commands split condition always occurs and has to be
   accepted.

6. Are query results per-context or global?

   Some GPU platforms and/or driver versions support only global GPU
   counters. In such cases, the query instance has to have
   GL_PERFQUERY_GLOBAL_CONTEXT_INTEL flag set when creating query
   instance. Otherwise, creation will fail and an INVALID_OPERATION error
   will be generated.

   Support for a global context means that a single query instance measures
   all GPU activities performed between query start and query end. Query
   measures not only current OpenGL context but also activities of other
   OpenGL contexts, other 3D API like DX and operating system windows draw
   calls.

Program examples

1. Reading counter  meta data example
  
   // query data has proprietary predefined structure layout
   // associated with the vendor query ID
   GL_QUERY_PIPELINE_METRICS * pQueryData;
  
   uint queryId;
   uint nextQueryId;
   uint queryHandle;
   uint dataSize;
   uint noCounters;
   uint noInstances;
   uint capsMask;
  
   const uint queryNameLen = 32;
   char queryName[queryNameLen];
  
   const uint counterNameLen = 32;
   char counterName[counterNameLen];
  
   const uint counterDescLen = 256;
   char counterDesc[counterDescLen];
  
   //get first vendor queryID
   glGetFirstPerfQueryIdINTEL(&queryId);
  
   nextQueryId = queryId;
   while(nextQueryId)
   {
       glGetPerfQueryInfoINTEL(
           nextQueryId,
           queryNameLen,
           &queryName,
           &dataSize,
           &noCounters,
           &noInstances,
           &capsMask);
  
           for(int counterId = 1; counterId <= noCounters; counterId++)
       {
           uint counterOffset;
           uint counterDataSize;
           uint counterTypeEnum;
           uint counterDataTypeEnum;
           UINT64 rawCounterMaxValue;
  
           glGetPerfCounterInfoINTEL(
               nextQueryId,
               counterId,
               counterNameLen,
               counterName,
               counterDescLen,
               counterDesc,
               &counterOffset,
               &counterDataSize,
               &counterTypeEnum,
               &counterDataTypeEnum,
               &rawCounterMaxValue);
  
               // use returned values here
               ...
       }
   }
  
2. Measuring a single draw call example
  
   Note that GL_QUERY_PIPELINE_METRICS is a proprietary structure defined
   by vendor and is used as example and function named according to the
   convention of glFuntionINTEL are wrappers to dynamically linked-by-name
   procedures.
  
   // query data has proprietary predefined structure layout
   // associated with the vendor query ID
   GL_QUERY_PIPELINE_METRICS * pQueryData;
  
   uint queryId;
   uint queryHandle;
   char queryName[] = "Intel_Pipeline_Query";
  
   // get vendor queryID by name
   glGetPerfQueryIdByNameINTEL(queryName, &queryId);
  
   // create query instance of queryId type
   glCreatePerfQueryINTEL(queryId, &queryHandle);
  
   glBeginPerfQueryINTEL(queryHandle); // Start query
  
   glDrawElements(...); // Issue graphics commands, do whatever
  
   glEndPerfQueryINTEL(queryHandle); // End query
  
   // perform other application activities
  
   uint bytesWritten = 0;
   uint dataSize = sizeof(GL_QUERY_PIPELINE_METRICS);
  
   pQueryData = (GL_QUERY_PIPELINE_METRICS *) malloc(dataSize);
  
   // for the first time use GL_PERFQUERY_FLUSH_INTEL flag to ensure graphics
   // commands were submitted to hardware
  
   glGetPerfQueryDataINTEL(
       queryHandle,
       GL_PERFQUERY_FLUSH_INTEL,
       dataSize,
       pQueryData,
       &bytesWritten);
  
   while(bytesWritten == 0)
   {
       // Now enough to use GL_PERFQUERY_DONOT_FLUSH_INTEL flag
       glGetPerfQueryDataINTEL(
           queryHandle,
           GL__PERFQUERY_DONOT_FLUSH_INTEL,
               dataSize,
           pQueryData,
           &bytesWritten);
   }
  
   if(bytesWritten == dataSize)
   {
       // Use counters' data here
       uint64 vertexShaderKernelsRunCount =
            pQueryData->VertexShaderInvocations;
       uint64 fragmentShaderKernelsRunCount =
            pQueryData->FragmentShaderInvocations;
       ...
   }
   else
   {
      // error handling case
   }
  
   glDeletePerfQueryINTEL(queryHandle); // query instance is released
  
3. Measuring multiple draw calls with synchronous wait for result
  
   Note that GL_QUERY_HD_HW_METRICS is a proprietary structure defined by
   vendor and is used as example and function named according to the
   convention of glFuntionINTEL are wrappers to dynamically linked-by-name
   procedures.
  
   // query data has proprietary predefined structure layout
   // associated with the vendor query ID
   GL_QUERY_HD_HW_METRICS * pQueryData;
  
   uint queryId;
   UINT32 queryHandle[1000];
   char queryName[] = "Intel_HD_Hardware_Counters";
  
   // get vendor queryID by name
   glGetPerfQueryIdByNameINTEL(queryName, &queryId);
  
   // create memory for 1000 results
   uint dataSize = sizeof(GL_QUERY_HD_HW_METRICS);
   pQueryData = (GL_QUERY_HD_HW_METRICS *) malloc(dataSize * 1000);
  
   // create 1000 query instances of queryId type
   for(int i = 0; i < 1000; i++)
   {
       glCreatePerfQueryINTEL(queryId, &queryHandle[i]);
   }
  
   uint currentDrawNumber = 0;
  
   // start 1st query
   glBeginPerfQueryINTEL(queryHandle[currentDrawNumber]);
  
   glDrawElements(...); // Issue graphics commands
  
   // end query
   glEndPerfQueryINTEL(queryHandle[currentDrawNumber++]);
  
   ...
  
   // start nth query
   glBeginPerfQueryINTEL(queryHandle[currentDrawNumber]);
  
   glDrawElements(...); // Issue graphics commands
  
   // end query
   glEndPerfQueryINTEL(queryHandle[currentDrawNumber++]);
  
   ...
  
   // assume currentDrawNumber == 1000 here
   // so get all results after these 1000 draws
  
   GL_QUERY_HD_HW_METRICS *pData = pQueryData;
  
   for(int i = 0; i < 1000; i++)
   {
       uint bytesWritten = 0;
  
       // use GL_PERFQUERY_WAIT_INTEL flag to cause the function will wait
       // for the query completion
       glGetPerfQueryDataINTEL(
           queryHandle[i],
           GL_PERFQUERY_WAIT_INTEL,
           dataSize,
           pData,
           &bytesWritten);
  
       if(bytesWritten != sizeof(GL_QUERY_HD_HW_METRICS))
       {
            // query error case
            assert(false);
            ...
                // some cleanup needed also
            ...
            return ERROR;
       }
  
       pData++;
    }
  
    // use counters data
    ...
  
    // repeat measurements if needed reusing the query instances
    ...
  
    // query instances are no longer needed so release all of them
    for(int i = 0; i < 1000; i++)
    {
        glDeletePerfQueryINTEL(queryHandle[i]);
    }
  
    return SUCCESS;

Revision History

1.3   20/12/13 Jon Leech  Assign extension #s and enum values. Fix
                          a few typos (Bug 11345).

1.2   29/11/13 sgrajewski Extension upgraded to 4.4 core specification.
                          ES3.0.2 dependencies added.

1.1   06/06/11 puminski   Initial revision.