blob: 6d75026a243d075bef2b3c6c2b2d08570f0a416f [file] [log] [blame]
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 <pname> 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.