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;