Merge "BufferQueue no longer hardcodes buffer counts"
diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c
index 25505f8..4556505 100644
--- a/cmds/dumpstate/utils.c
+++ b/cmds/dumpstate/utils.c
@@ -383,7 +383,7 @@
}
/* create a new, empty traces.txt file to receive stack dumps */
- int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); /* -rw-rw-rw- */
+ int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666); /* -rw-rw-rw- */
if (fd < 0) {
fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
return NULL;
diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp
index d532296..f7d8abe 100644
--- a/libs/utils/Trace.cpp
+++ b/libs/utils/Trace.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "Trace"
+
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/Trace.h>
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 8bfa16d..8df7d39 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -715,6 +715,69 @@
return addr;
}
+class FrameCompletionThread : public Thread {
+public:
+
+ static void queueSync(EGLSyncKHR sync) {
+ static sp<FrameCompletionThread> thread(new FrameCompletionThread);
+ static bool running = false;
+ if (!running) {
+ thread->run("GPUFrameCompletion");
+ running = true;
+ }
+ {
+ Mutex::Autolock lock(thread->mMutex);
+ ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
+ thread->mFramesQueued).string());
+ thread->mQueue.push_back(sync);
+ thread->mCondition.signal();
+ thread->mFramesQueued++;
+ ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
+ }
+ }
+
+private:
+ FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {}
+
+ virtual bool threadLoop() {
+ EGLSyncKHR sync;
+ uint32_t frameNum;
+ {
+ Mutex::Autolock lock(mMutex);
+ while (mQueue.isEmpty()) {
+ mCondition.wait(mMutex);
+ }
+ sync = mQueue[0];
+ frameNum = mFramesCompleted;
+ }
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ {
+ ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
+ frameNum).string());
+ EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
+ if (result == EGL_FALSE) {
+ ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("FrameCompletion: timeout waiting for fence");
+ }
+ eglDestroySyncKHR(dpy, sync);
+ }
+ {
+ Mutex::Autolock lock(mMutex);
+ mQueue.removeAt(0);
+ mFramesCompleted++;
+ ATRACE_INT("GPU Frames Outstanding", mQueue.size());
+ }
+ return true;
+ }
+
+ uint32_t mFramesQueued;
+ uint32_t mFramesCompleted;
+ Vector<EGLSyncKHR> mQueue;
+ Condition mCondition;
+ Mutex mMutex;
+};
+
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
ATRACE_CALL();
@@ -744,7 +807,19 @@
}
}
- return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+ EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
+
+ if (CC_UNLIKELY(dp->traceGpuCompletion)) {
+ EGLSyncKHR sync = EGL_NO_SYNC_KHR;
+ {
+ sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
+ }
+ if (sync != EGL_NO_SYNC_KHR) {
+ FrameCompletionThread::queueSync(sync);
+ }
+ }
+
+ return result;
}
EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index b80afd6..6b6f8cf 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -67,7 +67,7 @@
egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS];
egl_display_t::egl_display_t() :
- magic('_dpy'), finishOnSwap(false), refs(0) {
+ magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0) {
}
egl_display_t::~egl_display_t() {
@@ -239,6 +239,11 @@
finishOnSwap = true;
}
+ property_get("debug.egl.traceGpuCompletion", value, "0");
+ if (atoi(value)) {
+ traceGpuCompletion = true;
+ }
+
refs++;
if (major != NULL)
*major = VERSION_MAJOR;
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 43738ea..3e22efa 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -107,7 +107,8 @@
public:
DisplayImpl disp;
- bool finishOnSwap;
+ bool finishOnSwap; // property: debug.egl.finish
+ bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion
private:
uint32_t refs;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index e742d3e..4e13377 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -23,6 +25,7 @@
#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Thread.h>
+#include <utils/Trace.h>
#include <utils/Vector.h>
#include <hardware/hardware.h>
@@ -48,8 +51,9 @@
mNumOVLayers(0), mNumFBLayers(0),
mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE),
mEventHandler(handler),
- mRefreshPeriod(refreshPeriod)
+ mRefreshPeriod(refreshPeriod), mVSyncCount(0)
{
+ bool needVSyncThread = false;
int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
if (err == 0) {
@@ -64,12 +68,17 @@
mHwc->registerProcs(mHwc, &mCBContext.procs);
memset(mCBContext.procs.zero, 0, sizeof(mCBContext.procs.zero));
}
-
if (mHwc->common.version < HWC_DEVICE_API_VERSION_0_3) {
- // we don't have VSYNC support, we need to fake it
- mVSyncThread = new VSyncThread(*this);
+ needVSyncThread = true;
}
}
+ } else {
+ needVSyncThread = true;
+ }
+
+ if (needVSyncThread) {
+ // we don't have VSYNC support, we need to fake it
+ mVSyncThread = new VSyncThread(*this);
}
}
@@ -100,20 +109,20 @@
}
void HWComposer::vsync(int dpy, int64_t timestamp) {
+ ATRACE_INT("VSYNC", ++mVSyncCount&1);
mEventHandler.onVSyncReceived(dpy, timestamp);
}
status_t HWComposer::eventControl(int event, int enabled) {
status_t err = NO_ERROR;
- if (mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
+ if (mHwc && mHwc->common.version >= HWC_DEVICE_API_VERSION_0_3) {
err = mHwc->methods->eventControl(mHwc, event, enabled);
- } else {
- if (mVSyncThread != NULL) {
- mVSyncThread->setEnabled(enabled);
- } else {
- err = BAD_VALUE;
- }
}
+
+ if (err == NO_ERROR && mVSyncThread != NULL) {
+ mVSyncThread->setEnabled(enabled);
+ }
+
return err;
}
@@ -245,4 +254,58 @@
}
// ---------------------------------------------------------------------------
+
+HWComposer::VSyncThread::VSyncThread(HWComposer& hwc)
+ : mHwc(hwc), mEnabled(false),
+ mNextFakeVSync(0),
+ mRefreshPeriod(hwc.mRefreshPeriod)
+{
+}
+
+void HWComposer::VSyncThread::setEnabled(bool enabled) {
+ Mutex::Autolock _l(mLock);
+ mEnabled = enabled;
+ mCondition.signal();
+}
+
+void HWComposer::VSyncThread::onFirstRef() {
+ run("VSyncThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
+}
+
+bool HWComposer::VSyncThread::threadLoop() {
+ { // scope for lock
+ Mutex::Autolock _l(mLock);
+ while (!mEnabled) {
+ mCondition.wait(mLock);
+ }
+ }
+
+ const nsecs_t period = mRefreshPeriod;
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ nsecs_t next_vsync = mNextFakeVSync;
+ nsecs_t sleep = next_vsync - now;
+ if (sleep < 0) {
+ // we missed, find where the next vsync should be
+ sleep = (period - ((now - next_vsync) % period));
+ next_vsync = now + sleep;
+ }
+ mNextFakeVSync = next_vsync + period;
+
+ struct timespec spec;
+ spec.tv_sec = next_vsync / 1000000000;
+ spec.tv_nsec = next_vsync % 1000000000;
+
+ int err;
+ do {
+ err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+ } while (err<0 && errno == EINTR);
+
+ if (err == 0) {
+ mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
+ }
+
+ return true;
+}
+
+// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 60a6367..7814594 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -97,56 +97,11 @@
bool mEnabled;
mutable nsecs_t mNextFakeVSync;
nsecs_t mRefreshPeriod;
-
- virtual void onFirstRef() {
- run("VSyncThread",
- PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
- }
-
- virtual bool threadLoop() {
- { // scope for lock
- Mutex::Autolock _l(mLock);
- while (!mEnabled) {
- mCondition.wait(mLock);
- }
- }
-
- const nsecs_t period = mRefreshPeriod;
- const nsecs_t now = systemTime(CLOCK_MONOTONIC);
- nsecs_t next_vsync = mNextFakeVSync;
- nsecs_t sleep = next_vsync - now;
- if (sleep < 0) {
- // we missed, find where the next vsync should be
- sleep = (period - ((now - next_vsync) % period));
- next_vsync = now + sleep;
- }
- mNextFakeVSync = next_vsync + period;
-
- struct timespec spec;
- spec.tv_sec = next_vsync / 1000000000;
- spec.tv_nsec = next_vsync % 1000000000;
-
- // NOTE: EINTR can happen with clock_nanosleep(), in case of
- // any error (including EINTR) we go through the condition's
- // test -- this is always correct and easy.
- if (::clock_nanosleep(CLOCK_MONOTONIC,
- TIMER_ABSTIME, &spec, NULL) == 0) {
- mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
- }
- return true;
- }
-
+ virtual void onFirstRef();
+ virtual bool threadLoop();
public:
- VSyncThread(HWComposer& hwc) :
- mHwc(hwc), mEnabled(false),
- mNextFakeVSync(0),
- mRefreshPeriod(hwc.mRefreshPeriod) {
- }
- void setEnabled(bool enabled) {
- Mutex::Autolock _l(mLock);
- mEnabled = enabled;
- mCondition.signal();
- }
+ VSyncThread(HWComposer& hwc);
+ void setEnabled(bool enabled);
};
friend class VSyncThread;
@@ -187,6 +142,7 @@
cb_context mCBContext;
EventHandler& mEventHandler;
nsecs_t mRefreshPeriod;
+ size_t mVSyncCount;
sp<VSyncThread> mVSyncThread;
};
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index b05b7c2..cc44186 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -123,13 +123,9 @@
Mutex::Autolock _l(mLock);
do {
- // check if we have received a VSYNC event
- if (mVSyncTimestamp) {
- // we have a VSYNC event pending
- timestamp = mVSyncTimestamp;
- mVSyncTimestamp = 0;
- break;
- }
+ // latch VSYNC event if any
+ timestamp = mVSyncTimestamp;
+ mVSyncTimestamp = 0;
// check if we should be waiting for VSYNC events
bool waitForNextVsync = false;
@@ -144,17 +140,33 @@
}
}
- // enable or disable VSYNC events
- mHw.getHwComposer().eventControl(
- HWComposer::EVENT_VSYNC, waitForNextVsync);
+ if (timestamp) {
+ if (!waitForNextVsync) {
+ // we received a VSYNC but we have no clients
+ // don't report it, and disable VSYNC events
+ mHw.getHwComposer().eventControl(
+ HWComposer::EVENT_VSYNC, false);
+ } else {
+ // report VSYNC event
+ break;
+ }
+ } else {
+ // never disable VSYNC events immediately, instead
+ // we'll wait to receive the event and we'll
+ // reevaluate whether we need to dispatch it and/or
+ // disable VSYNC events then.
+ if (waitForNextVsync) {
+ // enable
+ mHw.getHwComposer().eventControl(
+ HWComposer::EVENT_VSYNC, true);
+ }
+ }
// wait for something to happen
mCondition.wait(mLock);
} while(true);
// process vsync event
-
- ATRACE_INT("VSYNC", mDeliveredEvents&1);
mDeliveredEvents++;
mLastVSyncTimestamp = timestamp;