EGL: Use frameId instead of framesAgo for frame events.
Using a frameId allows an app to poll for timestamps
from a thread other than the swapping thread.
Test: adb shell /data/nativetest/libgui_test/libgui_test
--gtest_filter=*GetFrameTimestamps*
Change-Id: I3faac0513929837982a2e63f7e0d3d529bd28f10
diff --git a/include/gui/Surface.h b/include/gui/Surface.h
index 750e653..4baa6aa 100644
--- a/include/gui/Surface.h
+++ b/include/gui/Surface.h
@@ -206,6 +206,7 @@
int dispatchSetSurfaceDamage(va_list args);
int dispatchSetSharedBufferMode(va_list args);
int dispatchSetAutoRefresh(va_list args);
+ int dispatchGetNextFrameId(va_list args);
int dispatchEnableFrameTimestamps(va_list args);
int dispatchGetFrameTimestamps(va_list args);
int dispatchGetDisplayRefreshCycleDuration(va_list args);
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index c2ed91a..2e3a7de 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -833,6 +833,9 @@
case NATIVE_WINDOW_SET_AUTO_REFRESH:
res = dispatchSetAutoRefresh(args);
break;
+ case NATIVE_WINDOW_GET_NEXT_FRAME_ID:
+ res = dispatchGetNextFrameId(args);
+ break;
case NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS:
res = dispatchEnableFrameTimestamps(args);
break;
@@ -962,6 +965,12 @@
return setAutoRefresh(autoRefresh);
}
+int Surface::dispatchGetNextFrameId(va_list args) {
+ uint64_t* nextFrameId = va_arg(args, uint64_t*);
+ *nextFrameId = getNextFrameNumber();
+ return NO_ERROR;
+}
+
int Surface::dispatchEnableFrameTimestamps(va_list args) {
bool enable = va_arg(args, int);
enableFrameTimestamps(enable);
@@ -969,7 +978,7 @@
}
int Surface::dispatchGetFrameTimestamps(va_list args) {
- uint32_t framesAgo = va_arg(args, uint32_t);
+ uint64_t frameId = va_arg(args, uint64_t);
nsecs_t* outRequestedPresentTime = va_arg(args, int64_t*);
nsecs_t* outAcquireTime = va_arg(args, int64_t*);
nsecs_t* outLatchTime = va_arg(args, int64_t*);
@@ -980,7 +989,7 @@
nsecs_t* outDisplayRetireTime = va_arg(args, int64_t*);
nsecs_t* outDequeueReadyTime = va_arg(args, int64_t*);
nsecs_t* outReleaseTime = va_arg(args, int64_t*);
- return getFrameTimestamps(getNextFrameNumber() - 1 - framesAgo,
+ return getFrameTimestamps(frameId,
outRequestedPresentTime, outAcquireTime, outLatchTime,
outFirstRefreshStartTime, outLastRefreshStartTime,
outGlCompositionDoneTime, outDisplayPresentTime,
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 412c0f6..3f56665 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -598,8 +598,8 @@
mFrameTimestampsEnabled = true;
}
- int getAllFrameTimestamps(uint32_t framesAgo) {
- return native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ int getAllFrameTimestamps(uint64_t frameId) {
+ return native_window_get_frame_timestamps(mWindow.get(), frameId,
&outRequestedPresentTime, &outAcquireTime, &outLatchTime,
&outFirstRefreshStartTime, &outLastRefreshStartTime,
&outGpuCompositionDoneTime, &outDisplayPresentTime,
@@ -619,6 +619,13 @@
outReleaseTime = -1;
}
+ uint64_t getNextFrameId() {
+ uint64_t frameId = -1;
+ int status = native_window_get_next_frame_id(mWindow.get(), &frameId);
+ EXPECT_EQ(status, NO_ERROR);
+ return frameId;
+ }
+
void dequeueAndQueue(uint64_t frameIndex) {
int fence = -1;
ANativeWindowBuffer* buffer = nullptr;
@@ -748,6 +755,8 @@
EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+ const uint64_t fId = getNextFrameId();
+
// Verify the producer doesn't get frame timestamps piggybacked on dequeue.
ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
@@ -761,8 +770,7 @@
EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
// Verify attempts to get frame timestamps fail.
- const uint32_t framesAgo = 0;
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId);
EXPECT_EQ(INVALID_OPERATION, result);
EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
}
@@ -778,6 +786,8 @@
EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
EXPECT_EQ(0, mFakeConsumer->mGetFrameTimestampsCount);
+ const uint64_t fId1 = getNextFrameId();
+
// Verify getFrameTimestamps is piggybacked on dequeue.
ASSERT_EQ(NO_ERROR, mWindow->dequeueBuffer(mWindow.get(), &buffer, &fence));
EXPECT_EQ(0, mFakeConsumer->mAddFrameTimestampsCount);
@@ -802,8 +812,7 @@
// Verify queries for timestamps that the producer doesn't know about
// triggers a call to see if the consumer has any new timestamps.
- const uint32_t framesAgo = 0;
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId1);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(3, mFakeConsumer->mGetFrameTimestampsCount);
}
@@ -833,16 +842,17 @@
QueryPresentRetireSupported(false, true);
}
-// This test verifies that:
-// 1) The timestamps recorded in the consumer's FrameTimestampsHistory are
-// properly retrieved by the producer for the correct frames.
-// 2) When framesAgo is 0, it is querying for the most recently queued frame.
+// This verifies the timestamps recorded in the consumer's
+// FrameTimestampsHistory are properly retrieved by the producer for the
+// correct frames.
TEST_F(GetFrameTimestampsTest, TimestampsAssociatedWithCorrectFrame) {
enableFrameTimestamps();
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
mFrames[0].signalQueueFences();
+ const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
mFrames[1].signalQueueFences();
@@ -853,9 +863,8 @@
mFrames[1].signalRefreshFences();
// Verify timestamps are correct for frame 1.
- uint32_t framesAgo = 1;
resetTimestamps();
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId1);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
@@ -870,9 +879,8 @@
EXPECT_EQ(mFrames[0].kReleaseTime, outReleaseTime);
// Verify timestamps are correct for frame 2.
- framesAgo = 0;
resetTimestamps();
- result = getAllFrameTimestamps(framesAgo);
+ result = getAllFrameTimestamps(fId2);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
EXPECT_EQ(mFrames[1].kProducerAcquireTime, outAcquireTime);
@@ -893,9 +901,8 @@
enableFrameTimestamps();
mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
- const uint32_t framesAgo = 0;
-
// Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
// Verify queue-related timestamps for f1 are available immediately in the
@@ -903,7 +910,7 @@
// acquire fence.
resetTimestamps();
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
&outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
@@ -915,7 +922,7 @@
mFrames[0].signalQueueFences();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ result = native_window_get_frame_timestamps(mWindow.get(), fId1,
&outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
@@ -924,6 +931,7 @@
EXPECT_EQ(mFrames[0].kProducerAcquireTime, outAcquireTime);
// Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
// Verify queue-related timestamps for f2 are available immediately in the
@@ -931,7 +939,7 @@
// acquire fence.
resetTimestamps();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ result = native_window_get_frame_timestamps(mWindow.get(), fId2,
&outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
@@ -943,7 +951,7 @@
mFrames[1].signalQueueFences();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ result = native_window_get_frame_timestamps(mWindow.get(), fId2,
&outRequestedPresentTime, &outAcquireTime, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
@@ -961,6 +969,7 @@
mFrames[0].signalQueueFences();
// Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
mFrames[1].signalQueueFences();
@@ -971,9 +980,8 @@
mFrames[1].signalRefreshFences();
// Verify a request for no timestamps doesn't result in a sync call.
- const uint32_t framesAgo = 0;
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId2,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr);
EXPECT_EQ(NO_ERROR, result);
@@ -987,6 +995,7 @@
mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
// Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
mFrames[0].signalQueueFences();
@@ -1001,10 +1010,9 @@
// fence has been signaled.
// Note: A sync call is necessary here since the events triggered by
// addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
- uint32_t framesAgo = 1;
resetTimestamps();
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1021,10 +1029,9 @@
// Verify available timestamps are correct for frame 1 again, before any
// fence has been signaled.
// This time a sync call should not be necessary.
- framesAgo = 1;
resetTimestamps();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = getAllFrameTimestamps(framesAgo);
+ result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1043,10 +1050,9 @@
mFrames[0].signalReleaseFences();
// Verify all timestamps are available without a sync call.
- framesAgo = 1;
resetTimestamps();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = getAllFrameTimestamps(framesAgo);
+ result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1069,9 +1075,8 @@
enableFrameTimestamps();
mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
- const uint32_t framesAgo = 1;
-
// Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
mFrames[0].signalQueueFences();
@@ -1088,7 +1093,7 @@
// addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
resetTimestamps();
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1110,7 +1115,7 @@
// sync call.
resetTimestamps();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = getAllFrameTimestamps(framesAgo);
+ result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1132,10 +1137,12 @@
mSurface->mFakeSurfaceComposer->setSupportedTimestamps(true, true);
// Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
mFrames[0].signalQueueFences();
// Dequeue and queue frame 2.
+ const uint64_t fId2 = getNextFrameId();
dequeueAndQueue(1);
mFrames[1].signalQueueFences();
@@ -1146,10 +1153,9 @@
// fence has been signaled.
// Note: A sync call is necessary here since the events triggered by
// addFrameEvents didn't get to piggyback on the earlier queues/dequeues.
- uint32_t framesAgo = 1;
resetTimestamps();
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = getAllFrameTimestamps(framesAgo);
+ int result = getAllFrameTimestamps(fId1);
EXPECT_EQ(oldCount + 1, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[0].kRequestedPresentTime, outRequestedPresentTime);
@@ -1167,15 +1173,14 @@
mFrames[0].signalReleaseFences();
mFrames[1].signalRefreshFences();
- // Verify querying for all timestmaps of f2 does not do a sync call.
- // Even though the lastRefresh, retire, dequeueReady, and release times aren't
+ // Verify querying for all timestmaps of f2 does not do a sync call. Even
+ // though the lastRefresh, retire, dequeueReady, and release times aren't
// available, a sync call should not occur because it's not possible for f2
// to encounter the final value for those events until another frame is
// queued.
- framesAgo = 0;
resetTimestamps();
oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- result = getAllFrameTimestamps(framesAgo);
+ result = getAllFrameTimestamps(fId2);
EXPECT_EQ(oldCount, mFakeConsumer->mGetFrameTimestampsCount);
EXPECT_EQ(NO_ERROR, result);
EXPECT_EQ(mFrames[1].kRequestedPresentTime, outRequestedPresentTime);
@@ -1200,14 +1205,14 @@
displayPresentSupported, displayRetireSupported);
// Dequeue and queue frame 1.
+ const uint64_t fId1 = getNextFrameId();
dequeueAndQueue(0);
// Verify a query for the Present and Retire times do not trigger
// a sync call if they are not supported.
- const uint32_t framesAgo = 0;
resetTimestamps();
int oldCount = mFakeConsumer->mGetFrameTimestampsCount;
- int result = native_window_get_frame_timestamps(mWindow.get(), framesAgo,
+ int result = native_window_get_frame_timestamps(mWindow.get(), fId1,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
displayPresentSupported ? nullptr : &outDisplayPresentTime,
displayRetireSupported ? nullptr : &outDisplayRetireTime,
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 027c18d..6485ae5 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -643,10 +643,12 @@
#define EGL_DEQUEUE_READY_TIME_ANDROID 0x3156
#define EGL_READS_DONE_TIME_ANDROID 0x3157
#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+EGLAPI EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+EGLAPI EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
EGLAPI EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
#else
-typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETNEXTFRAMEIDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR *frameId);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETFRAMETIMESTAMPSANDROID) (EGLDisplay dpy, EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, EGLnsecsANDROID *values);
typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYTIMESTAMPSUPPORTEDANDROID) (EGLDisplay dpy, EGLSurface surface, EGLint timestamp);
#endif
#endif
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 1672397..c2fc6bd 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -216,6 +216,8 @@
(__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR },
// EGL_ANDROID_get_frame_timestamps
+ { "eglGetNextFrameIdANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID },
{ "eglGetFrameTimestampsANDROID",
(__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID },
{ "eglQueryTimestampSupportedANDROID",
@@ -2042,8 +2044,42 @@
return EGL_FALSE;
}
+EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR *frameId) {
+ clearError();
+
+ const egl_display_ptr dp = validate_display(dpy);
+ if (!dp) {
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ SurfaceRef _s(dp.get(), surface);
+ if (!_s.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (!s->win.get()) {
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
+ uint64_t nextFrameId = 0;
+ status_t ret = native_window_get_next_frame_id(s->win.get(), &nextFrameId);
+
+ if (ret != NO_ERROR) {
+ // This should not happen. Return an error that is not in the spec
+ // so it's obvious something is very wrong.
+ ALOGE("eglGetNextFrameId: Unexpected error.");
+ return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
+ }
+
+ *frameId = nextFrameId;
+ return EGL_TRUE;
+}
+
EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
+ EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps,
EGLnsecsANDROID *values)
{
clearError();
@@ -2112,7 +2148,7 @@
}
}
- status_t ret = native_window_get_frame_timestamps(s->win.get(), framesAgo,
+ status_t ret = native_window_get_frame_timestamps(s->win.get(), frameId,
requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime,
lastRefreshStartTime, GLCompositionDoneTime, displayPresentTime,
displayRetireTime, dequeueReadyTime, releaseTime);
@@ -2129,6 +2165,7 @@
default:
// This should not happen. Return an error that is not in the spec
// so it's obvious something is very wrong.
+ ALOGE("eglGetFrameTimestamps: Unexpected error.");
return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
}
}
diff --git a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
index 7aa0d30..f24d634 100644
--- a/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
+++ b/opengl/specs/EGL_ANDROID_get_frame_timestamps.txt
@@ -57,9 +57,12 @@
New Procedures and Functions
+ EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR *frameId);
+
EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface surface,
- EGLint framesAgo, EGLint numTimestamps, const EGLint *timestamps,
- EGLnsecsANDROID *values);
+ EGLuint64KHR frameId, EGLint numTimestamps,
+ const EGLint *timestamps, EGLnsecsANDROID *values);
EGLBoolean eglQueryTimestampSupportedANDROID(EGLDisplay dpy, EGLSurface
surface, EGLint timestamp);
@@ -95,23 +98,31 @@
The function
- EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy, EGLSurface
- surface, EGLint framesAgo, EGLint numTimestamps,
+ EGLBoolean eglGetNextFrameIdANDROID(EGLDisplay dpy, EGLSurface surface,
+ EGLuint64KHR *frameId);
+
+ Returns an identifier for the next frame to be swapped. The identifier can
+ be used to correlate a particular eglSwapBuffers with its timestamps in
+ eglGetFrameTimestampsANDROID. If any error is generated, the function will
+ return EGL_FALSE.
+
+ The function
+
+ EGLBoolean eglGetFrameTimestampsANDROID(EGLDisplay dpy,
+ EGLSurface surface, EGLuint64KHR frameId, EGLint numTimestamps,
const EGLint *timestamps, EGLnsecsANDROID *values);
- allows querying various timestamps related to the composition and display of
- a window surface.
+ allows querying various timestamps related to the composition and display
+ of specific frames of a window surface.
- The framesAgo parameter indicates how many frames before the last queued
- frame to query. So a value of zero would indicate that the query is for the
- last queued 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 frameId indicates which frame to query. 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
@@ -175,3 +186,6 @@
- Add EGL_COMPOSITION_LATCH_TIME_ANDROID,
EGL_LAST_COMPOSITION_START_TIME_ANDROID, and
EGL_DEQUEUE_READY_TIME_ANDROID.
+
+#4 (Brian Anderson, January 10, 2017)
+ - Use an absolute frameId rather than a relative framesAgo.
diff --git a/opengl/specs/README b/opengl/specs/README
index 8a3a7aa..1853214 100644
--- a/opengl/specs/README
+++ b/opengl/specs/README
@@ -1,5 +1,5 @@
This directory contains OpenGL ES and EGL extension specifications that have
-been or are being defined for Android.
+been or are being defined for Android.
The table below tracks usage of EGL enumerant values that have been reserved
for use by Android extensions.
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index d1582d1..1afa86c 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -20,7 +20,7 @@
#include <gui/BufferQueue.h>
#include <sync/sync.h>
#include <utils/StrongPointer.h>
-#include <utils/SortedVector.h>
+#include <utils/Vector.h>
#include "driver.h"
@@ -108,19 +108,11 @@
class TimingInfo {
public:
- TimingInfo()
- : vals_{0, 0, 0, 0, 0},
- timestamp_desired_present_time_(0),
- timestamp_actual_present_time_(0),
- timestamp_render_complete_time_(0),
- timestamp_composition_latch_time_(0) {}
- TimingInfo(const VkPresentTimeGOOGLE* qp)
+ TimingInfo() = default;
+ TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId)
: vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0},
- timestamp_desired_present_time_(0),
- timestamp_actual_present_time_(0),
- timestamp_render_complete_time_(0),
- timestamp_composition_latch_time_(0) {}
- bool ready() {
+ native_frame_id_(nativeFrameId) {}
+ bool ready() const {
return (timestamp_desired_present_time_ &&
timestamp_actual_present_time_ &&
timestamp_render_complete_time_ &&
@@ -148,27 +140,20 @@
vals_.earliestPresentTime = early_time;
vals_.presentMargin = margin;
}
- void get_values(VkPastPresentationTimingGOOGLE* values) { *values = vals_; }
+ void get_values(VkPastPresentationTimingGOOGLE* values) const {
+ *values = vals_;
+ }
public:
- VkPastPresentationTimingGOOGLE vals_;
+ VkPastPresentationTimingGOOGLE vals_ { 0, 0, 0, 0, 0 };
- uint64_t timestamp_desired_present_time_;
- uint64_t timestamp_actual_present_time_;
- uint64_t timestamp_render_complete_time_;
- uint64_t timestamp_composition_latch_time_;
+ uint64_t native_frame_id_ { 0 };
+ uint64_t timestamp_desired_present_time_ { 0 };
+ uint64_t timestamp_actual_present_time_ { 0 };
+ uint64_t timestamp_render_complete_time_ { 0 };
+ uint64_t timestamp_composition_latch_time_ { 0 };
};
-static inline int compare_type(const TimingInfo& lhs, const TimingInfo& rhs) {
- // TODO(ianelliott): Change this from presentID to the frame ID once
- // brianderson lands the appropriate patch:
- if (lhs.vals_.presentID < rhs.vals_.presentID)
- return -1;
- if (lhs.vals_.presentID > rhs.vals_.presentID)
- return 1;
- return 0;
-}
-
// ----------------------------------------------------------------------------
struct Surface {
@@ -195,7 +180,6 @@
: surface(surface_),
num_images(num_images_),
frame_timestamps_enabled(false) {
- timing.clear();
ANativeWindow* window = surface.window.get();
int64_t rdur;
native_window_get_refresh_cycle_duration(
@@ -221,7 +205,7 @@
bool dequeued;
} images[android::BufferQueue::NUM_BUFFER_SLOTS];
- android::SortedVector<TimingInfo> timing;
+ android::Vector<TimingInfo> timing;
};
VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) {
@@ -293,73 +277,64 @@
}
uint32_t get_num_ready_timings(Swapchain& swapchain) {
- uint32_t num_ready = 0;
- uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
- uint32_t frames_ago = num_timings;
- for (uint32_t i = 0; i < num_timings; i++) {
- TimingInfo* ti = &swapchain.timing.editItemAt(i);
- if (ti) {
- if (ti->ready()) {
- // This TimingInfo is ready to be reported to the user. Add it
- // to the num_ready.
- num_ready++;
- } else {
- // This TimingInfo is not yet ready to be reported to the user,
- // and so we should look for any available timestamps that
- // might make it ready.
- int64_t desired_present_time = 0;
- int64_t render_complete_time = 0;
- int64_t composition_latch_time = 0;
- int64_t actual_present_time = 0;
- for (uint32_t f = MIN_NUM_FRAMES_AGO; f < frames_ago; f++) {
- // Obtain timestamps:
- int ret = native_window_get_frame_timestamps(
- swapchain.surface.window.get(), f,
- &desired_present_time, &render_complete_time,
- &composition_latch_time,
- NULL, //&first_composition_start_time,
- NULL, //&last_composition_start_time,
- NULL, //&composition_finish_time,
- // TODO(ianelliott): Maybe ask if this one is
- // supported, at startup time (since it may not be
- // supported):
- &actual_present_time,
- NULL, //&display_retire_time,
- NULL, //&dequeue_ready_time,
- NULL /*&reads_done_time*/);
- if (ret) {
- break;
- } else if (!ret) {
- // We obtained at least one valid timestamp. See if it
- // is for the present represented by this TimingInfo:
- if (static_cast<uint64_t>(desired_present_time) ==
- ti->vals_.desiredPresentTime) {
- // Record the timestamp(s) we received, and then
- // see if this TimingInfo is ready to be reported
- // to the user:
- ti->timestamp_desired_present_time_ =
- static_cast<uint64_t>(desired_present_time);
- ti->timestamp_actual_present_time_ =
- static_cast<uint64_t>(actual_present_time);
- ti->timestamp_render_complete_time_ =
- static_cast<uint64_t>(render_complete_time);
- ti->timestamp_composition_latch_time_ =
- static_cast<uint64_t>(composition_latch_time);
+ if (swapchain.timing.size() < MIN_NUM_FRAMES_AGO) {
+ return 0;
+ }
- if (ti->ready()) {
- // The TimingInfo has received enough
- // timestamps, and should now use those
- // timestamps to calculate the info that should
- // be reported to the user:
- //
- ti->calculate(swapchain.refresh_duration);
- num_ready++;
- }
- break;
- }
- }
- }
- }
+ uint32_t num_ready = 0;
+ const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1;
+ for (uint32_t i = 0; i < num_timings; i++) {
+ TimingInfo& ti = swapchain.timing.editItemAt(i);
+ if (ti.ready()) {
+ // This TimingInfo is ready to be reported to the user. Add it
+ // to the num_ready.
+ num_ready++;
+ continue;
+ }
+ // This TimingInfo is not yet ready to be reported to the user,
+ // and so we should look for any available timestamps that
+ // might make it ready.
+ int64_t desired_present_time = 0;
+ int64_t render_complete_time = 0;
+ int64_t composition_latch_time = 0;
+ int64_t actual_present_time = 0;
+ // Obtain timestamps:
+ int ret = native_window_get_frame_timestamps(
+ swapchain.surface.window.get(), ti.native_frame_id_,
+ &desired_present_time, &render_complete_time,
+ &composition_latch_time,
+ NULL, //&first_composition_start_time,
+ NULL, //&last_composition_start_time,
+ NULL, //&composition_finish_time,
+ // TODO(ianelliott): Maybe ask if this one is
+ // supported, at startup time (since it may not be
+ // supported):
+ &actual_present_time,
+ NULL, //&display_retire_time,
+ NULL, //&dequeue_ready_time,
+ NULL /*&reads_done_time*/);
+
+ if (ret != android::NO_ERROR) {
+ continue;
+ }
+
+ // Record the timestamp(s) we received, and then see if this TimingInfo
+ // is ready to be reported to the user:
+ ti.timestamp_desired_present_time_ =
+ static_cast<uint64_t>(desired_present_time);
+ ti.timestamp_actual_present_time_ =
+ static_cast<uint64_t>(actual_present_time);
+ ti.timestamp_render_complete_time_ =
+ static_cast<uint64_t>(render_complete_time);
+ ti.timestamp_composition_latch_time_ =
+ static_cast<uint64_t>(composition_latch_time);
+
+ if (ti.ready()) {
+ // The TimingInfo has received enough timestamps, and should now
+ // use those timestamps to calculate the info that should be
+ // reported to the user:
+ ti.calculate(swapchain.refresh_duration);
+ num_ready++;
}
}
return num_ready;
@@ -369,29 +344,35 @@
void copy_ready_timings(Swapchain& swapchain,
uint32_t* count,
VkPastPresentationTimingGOOGLE* timings) {
- uint32_t num_copied = 0;
- uint32_t num_timings = static_cast<uint32_t>(swapchain.timing.size());
- if (*count < num_timings) {
- num_timings = *count;
+ if (swapchain.timing.empty()) {
+ *count = 0;
+ return;
}
- for (uint32_t i = 0; i < num_timings; i++) {
- TimingInfo* ti = &swapchain.timing.editItemAt(i);
- if (ti && ti->ready()) {
- ti->get_values(&timings[num_copied]);
- num_copied++;
- // We only report the values for a given present once, so remove
- // them from swapchain.timing:
- //
- // TODO(ianelliott): SEE WHAT HAPPENS TO THE LOOP WHEN THE
- // FOLLOWING IS DONE:
- swapchain.timing.removeAt(i);
- i--;
- num_timings--;
- if (*count == num_copied) {
- break;
- }
+
+ size_t last_ready = swapchain.timing.size() - 1;
+ while (!swapchain.timing[last_ready].ready()) {
+ if (last_ready == 0) {
+ *count = 0;
+ return;
}
+ last_ready--;
}
+
+ uint32_t num_copied = 0;
+ size_t num_to_remove = 0;
+ for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) {
+ const TimingInfo& ti = swapchain.timing[i];
+ if (ti.ready()) {
+ ti.get_values(&timings[num_copied]);
+ num_copied++;
+ }
+ num_to_remove++;
+ }
+
+ // Discard old frames that aren't ready if newer frames are ready.
+ // We don't expect to get the timing info for those old frames.
+ swapchain.timing.removeItemsAt(0, num_to_remove);
+
*count = num_copied;
}
@@ -1219,13 +1200,20 @@
native_window_enable_frame_timestamps(window, true);
swapchain.frame_timestamps_enabled = true;
}
- // Record this presentID and desiredPresentTime so it can
- // be later correlated to this present.
- TimingInfo timing_record(time);
- swapchain.timing.add(timing_record);
- uint32_t num_timings =
- static_cast<uint32_t>(swapchain.timing.size());
- if (num_timings > MAX_TIMING_INFOS) {
+
+ // Record the nativeFrameId so it can be later correlated to
+ // this present.
+ uint64_t nativeFrameId = 0;
+ err = native_window_get_next_frame_id(
+ window, &nativeFrameId);
+ if (err != android::NO_ERROR) {
+ ALOGE("Failed to get next native frame ID.");
+ }
+
+ // Add a new timing record with the user's presentID and
+ // the nativeFrameId.
+ swapchain.timing.push_back(TimingInfo(time, nativeFrameId));
+ while (swapchain.timing.size() > MAX_TIMING_INFOS) {
swapchain.timing.removeAt(0);
}
if (time->desiredPresentTime) {