Add the EGL_ANDROID_get_frame_timestamps extension
Change-Id: Ia7d1c10f0b8bd1f2f6dc7dc180764cb1b4fdbf6f
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index bef5f02..2e18698 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -621,6 +621,24 @@
#define EGL_MUTABLE_RENDER_BUFFER_BIT_KHR 0x1000
#endif
+#ifndef EGL_ANDROID_get_frame_timestamps
+#define EGL_ANDROID_get_frame_timestamps 1
+#define EGL_TIMESTAMPS_ANDROID 0x314D
+#define EGL_QUEUE_TIME_ANDROID 0x314E
+#define EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
+#define EGL_COMPOSITION_START_TIME_ANDROID 0x3430
+#define EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
+#define EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
+#define EGL_READS_DONE_TIME_ANDROID 0x3433
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+#else
+typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLAPI EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
+#endif
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index e793852..7a0cce4 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -84,6 +84,7 @@
"EGL_KHR_swap_buffers_with_damage "
"EGL_ANDROID_create_native_client_buffer "
"EGL_ANDROID_front_buffer_auto_refresh "
+ "EGL_ANDROID_get_frame_timestamps "
;
extern char const * const gExtensionString =
"EGL_KHR_image " // mandatory
@@ -207,6 +208,12 @@
(__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR },
{ "eglCreateStreamFromFileDescriptorKHR",
(__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
+
+ // EGL_ANDROID_get_frame_timestamps
+ { "eglGetFrameTimestampsANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
+ { "eglQueryTimestampSupportedANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglQueryTimestampSupportedANDROID },
};
/*
@@ -1196,7 +1203,7 @@
if (!_s.get())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t * const s = get_surface(surface);
if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) {
int err = native_window_set_auto_refresh(s->win.get(),
@@ -1205,6 +1212,11 @@
setError(EGL_BAD_SURFACE, EGL_FALSE);
}
+ if (attribute == EGL_TIMESTAMPS_ANDROID) {
+ s->enableTimestamps = value;
+ return EGL_TRUE;
+ }
+
if (s->cnx->egl.eglSurfaceAttrib) {
return s->cnx->egl.eglSurfaceAttrib(
dp->disp.dpy, s->surface, attribute, value);
@@ -1935,3 +1947,103 @@
return EGL_FALSE;
}
+
+EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+ EGLnsecsANDROID *values)
+{
+ clearError();
+
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->enableTimestamps) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ nsecs_t* postedTime = nullptr;
+ nsecs_t* acquireTime = nullptr;
+ nsecs_t* refreshStartTime = nullptr;
+ nsecs_t* GLCompositionDoneTime = nullptr;
+ nsecs_t* displayRetireTime = nullptr;
+ nsecs_t* releaseTime = nullptr;
+
+ for (int i = 0; i < numTimestamps; i++) {
+ switch (timestamps[i]) {
+ case EGL_QUEUE_TIME_ANDROID:
+ postedTime = &values[i];
+ break;
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ acquireTime = &values[i];
+ break;
+ case EGL_COMPOSITION_START_TIME_ANDROID:
+ refreshStartTime = &values[i];
+ break;
+ case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+ GLCompositionDoneTime = &values[i];
+ break;
+ case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+ displayRetireTime = &values[i];
+ break;
+ case EGL_READS_DONE_TIME_ANDROID:
+ releaseTime = &values[i];
+ break;
+ default:
+ setError(EGL_BAD_PARAMETER, EGL_FALSE);
+ return EGL_FALSE;
+ }
+ }
+
+ status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
+ postedTime, acquireTime, refreshStartTime, GLCompositionDoneTime,
+ displayRetireTime, releaseTime);
+
+ if (ret != NO_ERROR) {
+ setError(EGL_BAD_ACCESS, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ return EGL_TRUE;
+}
+
+EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLint timestamp)
+{
+ clearError();
+
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
+
+ switch (timestamp) {
+ case EGL_QUEUE_TIME_ANDROID:
+ case EGL_RENDERING_COMPLETE_TIME_ANDROID:
+ case EGL_COMPOSITION_START_TIME_ANDROID:
+ case EGL_COMPOSITION_FINISHED_TIME_ANDROID:
+ case EGL_DISPLAY_RETIRE_TIME_ANDROID:
+ case EGL_READS_DONE_TIME_ANDROID:
+ return EGL_TRUE;
+ default:
+ return EGL_FALSE;
+ }
+}
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index 90f27d1..cfecf77 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -68,7 +68,7 @@
EGLNativeWindowType win, EGLSurface surface,
egl_connection_t const* cnx) :
egl_object_t(dpy), surface(surface), config(config), win(win), cnx(cnx),
- connected(true)
+ enableTimestamps(false), connected(true)
{
if (win) {
getDisplay()->onWindowSurfaceCreated();
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 8f3b9cb..97eda4c 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -139,6 +139,7 @@
EGLConfig config;
sp<ANativeWindow> win;
egl_connection_t const* cnx;
+ bool enableTimestamps;
private:
bool connected;
void disconnect();
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
new file mode 100644
index 0000000..30337ad
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -0,0 +1,145 @@
+Name
+
+ ANDROID_get_frame_timestamps
+
+Name Strings
+
+ EGL_ANDROID_get_frame_timestamps
+
+Contributors
+
+ Pablo Ceballos
+
+Contact
+
+ Pablo Ceballos, Google Inc. (pceballos 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, May 31, 2016
+
+Number
+
+ EGL Extension #XXX
+
+Dependencies
+
+ Requires EGL 1.2
+
+ This extension is written against the wording of the EGL 1.5 Specification
+
+Overview
+
+ This extension allows querying various timestamps related to the composition
+ and display of window surfaces.
+
+ Some examples of how this might be used:
+ - The display retire time can be used to calculate end-to-end latency of
+ the entire graphics pipeline.
+ - The queue time and rendering complete time can be used to determine
+ how long the application's rendering took to complete. Likewise, the
+ composition start time and finish time can be used to determine how
+ long the compositor's rendering work took. In combination these can be
+ used to help determine if the system is GPU or CPU bound.
+
+New Types
+
+ /*
+ * EGLnsecsANDROID is a signed integer type for representing a time in
+ * nanoseconds.
+ */
+ #include <khrplatform.h>
+ typedef khronos_stime_nanoseconds_t EGLnsecsANDROID;
+
+New Procedures and Functions
+
+ EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+ EGLnsecsANDROID *values);
+
+ EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
+ surface, EGLint timestamp);
+
+New Tokens
+
+ EGL_TIMESTAMPS_ANDROID 0x314D
+ EGL_QUEUE_TIME_ANDROID 0x314E
+ EGL_RENDERING_COMPLETE_TIME_ANDROID 0x314F
+ EGL_COMPOSITION_START_TIME_ANDROID 0x3430
+ EGL_COMPOSITION_FINISHED_TIME_ANDROID 0x3431
+ EGL_DISPLAY_RETIRE_TIME_ANDROID 0x3432
+ EGL_READS_DONE_TIME_ANDROID 0x3433
+
+Add to the list of supported tokens for eglSurfaceAttrib in section 3.5.6
+"Surface Attributes", page 43:
+
+ If attribute is EGL_TIMESTAMPS_ANDROID, then values specifies whether to
+ enable/disable timestamp collection for this surface. A value of EGL_TRUE
+ enables timestamp collection, while a value of EGL_FALSE disables it. The
+ initial value is false. If surface is not a window surface this has no
+ effect.
+
+Changes to Chapter 3 of the EGL 1.5 Specification (EGL Functions and Errors)
+
+ Add a new subsection under Section 3,
+
+ "3.13 Composition and Display Timestamps
+
+ The function
+
+ EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface
+ surface, EGLint framesAgo, EGLint numTimestamps,
+ const EGLint *timestamps, EGLnsecsANDROID *values);
+
+ allows querying various timestamps related to the composition and display of
+ a window surface.
+
+ The framesAgo parameter indicates how many frames before the last posted
+ frame to query. So a value of zero would indicate that the query is for the
+ last posted frame. Note that the implementation maintains a limited history
+ of timestamp data. If a query is made for a frame whose timestamp history
+ no longer exists then EGL_BAD_ACCESS is generated. If timestamp collection
+ has not been enabled for the surface then EGL_BAD_SURFACE is generated.
+ Timestamps for events that will not occur or have not yet occurred will be
+ zero. Timestamp queries that are not supported will generate an
+ EGL_BAD_PARAMETER error. If any error is generated the function will return
+ EGL_FALSE.
+
+ The eglGetFrameTimestampsANDROID function takes an array of timestamps to
+ query and returns timestamps in the corresponding indices of the values
+ array. The possible timestamps that can be queried are:
+ - EGL_QUEUE_TIME_ANDROID - The time this frame was queued by the
+ application.
+ - EGL_RENDERING_COMPLETE_TIME_ANDROID - The time when all of the
+ application's rendering to the surface was completed.
+ - EGL_COMPOSITION_START_TIME_ANDROID - The time at which the compositor
+ began preparing composition for this frame.
+ - EGL_COMPOSITION_FINISHED_TIME_ANDROID - The time at which the
+ compositor's rendering work for this frame finished. This will be zero
+ if composition was handled by the display and the compositor didn't do
+ any rendering.
+ - EGL_DISPLAY_RETIRE_TIME_ANDROID - The time at which this frame was
+ replaced by the next frame on-screen.
+ - EGL_READS_DONE_TIME_ANDROID - The time at which all reads for the
+ purpose of display/composition were completed for this frame.
+
+ Not all implementations may support all off the above timestamp queries. The
+ function
+
+ EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
+ surface, EGLint timestamp);
+
+ allows querying which timestamps are supported on the implementation."
+
+Issues
+
+ None
+
+Revision History
+
+#1 (Pablo Ceballos, May 31, 2016)
+ - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
index 8f1eaf3..f0c024e 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -19,4 +19,11 @@
0x314A EGL_IMAGE_CROP_RIGHT_ANDROID (EGL_ANDROID_image_crop)
0x314B EGL_IMAGE_CROP_BOTTOM_ANDROID (EGL_ANDROID_image_crop)
0x314C EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID (EGL_ANDROID_front_buffer_auto_refresh)
-0x314D - 0x314F (unused)
+0x314D EGL_TIMESTAMPS_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314E EGL_QUEUE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x314F EGL_RENDERING_COMPLETE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3430 EGL_COMPOSITION_START_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3431 EGL_COMPOSITION_FINISHED_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3432 EGL_DISPLAY_RETIRE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3433 EGL_READS_DONE_TIME_ANDROID (EGL_ANDROID_get_frame_timestamps)
+0x3434 - 0x343F (unused)