Precache paths from a worker thread

Change-Id: I3e7b53d67e0e03e403beaf55c39350ead7f1e309
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index d231907..105f45f 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1005,7 +1005,7 @@
             : DrawBoundedOp(paint), mPath(path) {
         float left, top, offset;
         uint32_t width, height;
-        computePathBounds(path, paint, left, top, offset, width, height);
+        PathCache::computePathBounds(path, paint, left, top, offset, width, height);
         left -= offset;
         top -= offset;
         mLocalBounds.set(left, top, left + width, top + height);
@@ -1016,6 +1016,11 @@
         return renderer.drawPath(mPath, getPaint(renderer));
     }
 
+    virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
+        SkPaint* paint = getPaint(renderer);
+        renderer.getCaches().pathCache.precache(mPath, paint);
+    }
+
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
     }
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 78f9cf5..d681609 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -17,7 +17,6 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <utils/JenkinsHash.h>
-#include <utils/threads.h>
 
 #include "Caches.h"
 #include "Debug.h"
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 11fdd6c..7fe0a69 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -276,6 +276,12 @@
     renderOverdraw();
     endTiling();
 
+    // When finish() is invoked on FBO 0 we've reached the end
+    // of the current frame
+    if (getTargetFbo() == 0) {
+        mCaches.pathCache.trim();
+    }
+
     if (!suppressErrorChecks()) {
 #if DEBUG_OPENGL
         GLenum status = GL_NO_ERROR;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 4f682ed..9e6ec84 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -16,34 +16,84 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
+#include <sys/sysinfo.h>
+
+#include "Caches.h"
 #include "PathCache.h"
 #include "Properties.h"
 
 namespace android {
 namespace uirenderer {
 
-// Defined in ShapeCache.h
+///////////////////////////////////////////////////////////////////////////////
+// Path precaching
+///////////////////////////////////////////////////////////////////////////////
 
-void computePathBounds(const SkPath* path, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-    const SkRect& bounds = path->getBounds();
-    computeBounds(bounds, paint, left, top, offset, width, height);
+bool PathCache::PrecacheThread::threadLoop() {
+    mSignal.wait();
+    Vector<Task> tasks;
+    {
+        Mutex::Autolock l(mLock);
+        tasks = mTasks;
+        mTasks.clear();
+    }
+
+    Caches& caches = Caches::getInstance();
+    uint32_t maxSize = caches.maxTextureSize;
+
+    ATRACE_BEGIN("pathPrecache");
+    for (size_t i = 0; i < tasks.size(); i++) {
+        const Task& task = tasks.itemAt(i);
+
+        float left, top, offset;
+        uint32_t width, height;
+        PathCache::computePathBounds(task.path, task.paint, left, top, offset, width, height);
+
+        if (width <= maxSize && height <= maxSize) {
+            SkBitmap* bitmap = new SkBitmap();
+
+            PathTexture* texture = task.texture;
+            texture->left = left;
+            texture->top = top;
+            texture->offset = offset;
+            texture->width = width;
+            texture->height = height;
+
+            PathCache::drawPath(task.path, task.paint, *bitmap, left, top, offset, width, height);
+
+            texture->future()->produce(bitmap);
+        } else {
+            task.texture->future()->produce(NULL);
+        }
+    }
+    ATRACE_END();
+    return true;
 }
 
-void computeBounds(const SkRect& bounds, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
-    const float pathWidth = fmax(bounds.width(), 1.0f);
-    const float pathHeight = fmax(bounds.height(), 1.0f);
+void PathCache::PrecacheThread::addTask(PathTexture* texture, SkPath* path, SkPaint* paint) {
+    if (!isRunning()) {
+        run("libhwui:pathPrecache", PRIORITY_DEFAULT);
+    }
 
-    left = bounds.fLeft;
-    top = bounds.fTop;
+    Task task;
+    task.texture = texture;
+    task.path = path;
+    task.paint = paint;
 
-    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+    Mutex::Autolock l(mLock);
+    mTasks.add(task);
+    mSignal.signal();
+}
 
-    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
-    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+void PathCache::PrecacheThread::exit() {
+    {
+        Mutex::Autolock l(mLock);
+        mTasks.clear();
+    }
+    requestExit();
+    mSignal.signal();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -51,7 +101,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
-        PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
+        PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE), mThread(new PrecacheThread()) {
+}
+
+PathCache::~PathCache() {
+    mThread->exit();
 }
 
 void PathCache::remove(SkPath* path) {
@@ -60,7 +114,7 @@
 
     while (i.next()) {
         const PathCacheEntry& key = i.key();
-        if (key.path == path) {
+        if (key.path == path || key.path == path->getSourcePath()) {
             pathsToRemove.push(key);
         }
     }
@@ -71,12 +125,12 @@
 }
 
 void PathCache::removeDeferred(SkPath* path) {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock l(mLock);
     mGarbage.push(path);
 }
 
 void PathCache::clearGarbage() {
-    Mutex::Autolock _l(mLock);
+    Mutex::Autolock l(mLock);
     size_t count = mGarbage.size();
     for (size_t i = 0; i < count; i++) {
         remove(mGarbage.itemAt(i));
@@ -84,24 +138,86 @@
     mGarbage.clear();
 }
 
-PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+/**
+ * To properly handle path mutations at draw time we always make a copy
+ * of paths objects when recording display lists. The source path points
+ * to the path we originally copied the path from. This ensures we use
+ * the original path as a cache key the first time a path is inserted
+ * in the cache. The source path is also used to reclaim garbage when a
+ * Dalvik Path object is collected.
+ */
+static SkPath* getSourcePath(SkPath* path) {
     const SkPath* sourcePath = path->getSourcePath();
     if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
-        path = const_cast<SkPath*>(sourcePath);
+        return const_cast<SkPath*>(sourcePath);
     }
+    return path;
+}
+
+PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
+    path = getSourcePath(path);
 
     PathCacheEntry entry(path, paint);
     PathTexture* texture = mCache.get(entry);
 
     if (!texture) {
         texture = addTexture(entry, path, paint);
-    } else if (path->getGenerationID() != texture->generation) {
-        mCache.remove(entry);
-        texture = addTexture(entry, path, paint);
+    } else {
+        // A bitmap is attached to the texture, this means we need to
+        // upload it as a GL texture
+        if (texture->future() != NULL) {
+            // But we must first wait for the worker thread to be done
+            // producing the bitmap, so let's wait
+            SkBitmap* bitmap = texture->future()->get();
+            if (bitmap) {
+                addTexture(entry, bitmap, texture);
+                texture->clearFuture();
+            } else {
+                ALOGW("Path too large to be rendered into a texture (%dx%d)",
+                        texture->width, texture->height);
+                texture->clearFuture();
+                texture = NULL;
+                mCache.remove(entry);
+            }
+        } else if (path->getGenerationID() != texture->generation) {
+            mCache.remove(entry);
+            texture = addTexture(entry, path, paint);
+        }
     }
 
     return texture;
 }
 
+void PathCache::precache(SkPath* path, SkPaint* paint) {
+    path = getSourcePath(path);
+
+    PathCacheEntry entry(path, paint);
+    PathTexture* texture = mCache.get(entry);
+
+    bool generate = false;
+    if (!texture) {
+        generate = true;
+    } else if (path->getGenerationID() != texture->generation) {
+        mCache.remove(entry);
+        generate = true;
+    }
+
+    if (generate) {
+        // It is important to specify the generation ID so we do not
+        // attempt to precache the same path several times
+        texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID(), true);
+
+        // During the precaching phase we insert path texture objects into
+        // the cache that do not point to any GL texture. They are instead
+        // treated as a task for the precaching worker thread. This is why
+        // we do not check the cache limit when inserting these objects.
+        // The conversion into GL texture will happen in get(), when a client
+        // asks for a path texture. This is also when the cache limit will
+        // be enforced.
+        mCache.put(entry, texture);
+        mThread->addTask(texture, path, paint);
+    }
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 8a0235b..1d28ecb 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -17,14 +17,21 @@
 #ifndef ANDROID_HWUI_PATH_CACHE_H
 #define ANDROID_HWUI_PATH_CACHE_H
 
+#include <utils/Thread.h>
 #include <utils/Vector.h>
 
 #include "Debug.h"
 #include "ShapeCache.h"
+#include "thread/Signal.h"
+
+class SkPaint;
+class SkPath;
 
 namespace android {
 namespace uirenderer {
 
+class Caches;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
@@ -69,6 +76,7 @@
 class PathCache: public ShapeCache<PathCacheEntry> {
 public:
     PathCache();
+    ~PathCache();
 
     /**
      * Returns the texture associated with the specified path. If the texture
@@ -89,7 +97,35 @@
      */
     void clearGarbage();
 
+    void precache(SkPath* path, SkPaint* paint);
+
 private:
+    class PrecacheThread: public Thread {
+    public:
+        PrecacheThread(): mSignal(Condition::WAKE_UP_ONE) { }
+
+        void addTask(PathTexture* texture, SkPath* path, SkPaint* paint);
+        void exit();
+
+    private:
+        struct Task {
+            PathTexture* texture;
+            SkPath* path;
+            SkPaint* paint;
+        };
+
+        virtual bool threadLoop();
+
+        // Lock for the list of tasks
+        Mutex mLock;
+        Vector<Task> mTasks;
+
+        // Signal used to wake up the thread when a new
+        // task is available in the list
+        mutable Signal mSignal;
+    };
+
+    sp<PrecacheThread> mThread;
     Vector<SkPath*> mGarbage;
     mutable Mutex mLock;
 }; // class PathCache
diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h
index 47cab83..67ae85b 100644
--- a/libs/hwui/ShapeCache.h
+++ b/libs/hwui/ShapeCache.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_HWUI_SHAPE_CACHE_H
 #define ANDROID_HWUI_SHAPE_CACHE_H
 
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
 #include <GLES2/gl2.h>
 
 #include <SkBitmap.h>
@@ -27,10 +29,13 @@
 
 #include <utils/JenkinsHash.h>
 #include <utils/LruCache.h>
+#include <utils/Trace.h>
+#include <utils/CallStack.h>
 
 #include "Debug.h"
 #include "Properties.h"
 #include "Texture.h"
+#include "thread/Future.h"
 
 namespace android {
 namespace uirenderer {
@@ -57,6 +62,16 @@
     PathTexture(): Texture() {
     }
 
+    PathTexture(bool hasFuture): Texture() {
+        if (hasFuture) {
+            mFuture = new Future<SkBitmap*>();
+        }
+    }
+
+    ~PathTexture() {
+        clearFuture();
+    }
+
     /**
      * Left coordinate of the path bounds.
      */
@@ -69,6 +84,20 @@
      * Offset to draw the path at the correct origin.
      */
     float offset;
+
+    sp<Future<SkBitmap*> > future() const {
+        return mFuture;
+    }
+
+    void clearFuture() {
+        if (mFuture != NULL) {
+            delete mFuture->get();
+            mFuture.clear();
+        }
+    }
+
+private:
+    sp<Future<SkBitmap*> > mFuture;
 }; // struct PathTexture
 
 /**
@@ -449,6 +478,52 @@
      */
     uint32_t getSize();
 
+    /**
+     * Trims the contents of the cache, removing items until it's under its
+     * specified limit.
+     *
+     * Trimming is used for caches that support pre-caching from a worker
+     * thread. During pre-caching the maximum limit of the cache can be
+     * exceeded for the duration of the frame. It is therefore required to
+     * trim the cache at the end of the frame to keep the total amount of
+     * memory used under control.
+     *
+     * Only the PathCache currently supports pre-caching.
+     */
+    void trim();
+
+    static void computePathBounds(const SkPath* path, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+        const SkRect& bounds = path->getBounds();
+        computeBounds(bounds, paint, left, top, offset, width, height);
+    }
+
+    static void computeBounds(const SkRect& bounds, const SkPaint* paint,
+            float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
+        const float pathWidth = fmax(bounds.width(), 1.0f);
+        const float pathHeight = fmax(bounds.height(), 1.0f);
+
+        left = bounds.fLeft;
+        top = bounds.fTop;
+
+        offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
+
+        width = uint32_t(pathWidth + offset * 2.0 + 0.5);
+        height = uint32_t(pathHeight + offset * 2.0 + 0.5);
+    }
+
+    static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
+            float left, float top, float offset, uint32_t width, uint32_t height) {
+        initBitmap(bitmap, width, height);
+
+        SkPaint pathPaint(*paint);
+        initPaint(pathPaint);
+
+        SkCanvas canvas(bitmap);
+        canvas.translate(-left + offset, -top + offset);
+        canvas.drawPath(*path, pathPaint);
+    }
+
 protected:
     PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
     PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
@@ -460,17 +535,51 @@
      */
     void purgeCache(uint32_t width, uint32_t height);
 
-    void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
-    void initPaint(SkPaint& paint);
-
-    bool checkTextureSize(uint32_t width, uint32_t height);
-
     PathTexture* get(Entry entry) {
         return mCache.get(entry);
     }
 
     void removeTexture(PathTexture* texture);
 
+    bool checkTextureSize(uint32_t width, uint32_t height) {
+        if (width > mMaxTextureSize || height > mMaxTextureSize) {
+            ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
+                    mName, width, height, mMaxTextureSize, mMaxTextureSize);
+            return false;
+        }
+        return true;
+    }
+
+    static PathTexture* createTexture(float left, float top, float offset,
+            uint32_t width, uint32_t height, uint32_t id, bool hasFuture = false) {
+        PathTexture* texture = new PathTexture(hasFuture);
+        texture->left = left;
+        texture->top = top;
+        texture->offset = offset;
+        texture->width = width;
+        texture->height = height;
+        texture->generation = id;
+        return texture;
+    }
+
+    static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
+        bitmap.setConfig(SkBitmap::kA8_Config, width, height);
+        bitmap.allocPixels();
+        bitmap.eraseColor(0);
+    }
+
+    static void initPaint(SkPaint& paint) {
+        // Make sure the paint is opaque, color, alpha, filter, etc.
+        // will be applied later when compositing the alpha8 texture
+        paint.setColor(0xff000000);
+        paint.setAlpha(255);
+        paint.setColorFilter(NULL);
+        paint.setMaskFilter(NULL);
+        paint.setShader(NULL);
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+        SkSafeUnref(paint.setXfermode(mode));
+    }
+
     LruCache<Entry, PathTexture*> mCache;
     uint32_t mSize;
     uint32_t mMaxSize;
@@ -617,23 +726,6 @@
     }
 }
 
-void computePathBounds(const SkPath* path, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
-void computeBounds(const SkRect& bounds, const SkPaint* paint,
-        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
-
-static PathTexture* createTexture(float left, float top, float offset,
-        uint32_t width, uint32_t height, uint32_t id) {
-    PathTexture* texture = new PathTexture;
-    texture->left = left;
-    texture->top = top;
-    texture->offset = offset;
-    texture->width = width;
-    texture->height = height;
-    texture->generation = id;
-    return texture;
-}
-
 template<class Entry>
 void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
     const uint32_t size = width * height;
@@ -646,38 +738,16 @@
 }
 
 template<class Entry>
-void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-    bitmap.allocPixels();
-    bitmap.eraseColor(0);
-}
-
-template<class Entry>
-void ShapeCache<Entry>::initPaint(SkPaint& paint) {
-    // Make sure the paint is opaque, color, alpha, filter, etc.
-    // will be applied later when compositing the alpha8 texture
-    paint.setColor(0xff000000);
-    paint.setAlpha(255);
-    paint.setColorFilter(NULL);
-    paint.setMaskFilter(NULL);
-    paint.setShader(NULL);
-    SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
-    SkSafeUnref(paint.setXfermode(mode));
-}
-
-template<class Entry>
-bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) {
-    if (width > mMaxTextureSize || height > mMaxTextureSize) {
-        ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
-                mName, width, height, mMaxTextureSize, mMaxTextureSize);
-        return false;
+void ShapeCache<Entry>::trim() {
+    while (mSize > mMaxSize) {
+        mCache.removeOldest();
     }
-    return true;
 }
 
 template<class Entry>
 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
         const SkPaint* paint) {
+    ATRACE_CALL();
 
     float left, top, offset;
     uint32_t width, height;
@@ -688,16 +758,10 @@
     purgeCache(width, height);
 
     SkBitmap bitmap;
-    initBitmap(bitmap, width, height);
+    drawPath(path, paint, bitmap, left, top, offset, width, height);
 
-    SkPaint pathPaint(*paint);
-    initPaint(pathPaint);
-
-    SkCanvas canvas(bitmap);
-    canvas.translate(-left + offset, -top + offset);
-    canvas.drawPath(*path, pathPaint);
-
-    PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
+    PathTexture* texture = createTexture(left, top, offset, width, height,
+            path->getGenerationID());
     addTexture(entry, &bitmap, texture);
 
     return texture;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 5cff5a5..2378eb5 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -20,7 +20,7 @@
 
 #include <SkCanvas.h>
 
-#include <utils/threads.h>
+#include <utils/Mutex.h>
 
 #include "Caches.h"
 #include "TextureCache.h"
diff --git a/libs/hwui/thread/Barrier.h b/libs/hwui/thread/Barrier.h
new file mode 100644
index 0000000..6cb23e5
--- /dev/null
+++ b/libs/hwui/thread/Barrier.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_BARRIER_H
+#define ANDROID_HWUI_BARRIER_H
+
+#include <utils/Condition.h>
+
+namespace android {
+namespace uirenderer {
+
+class Barrier {
+public:
+    Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mOpened(false) { }
+    ~Barrier() { }
+
+    void open() {
+        Mutex::Autolock l(mLock);
+        mOpened = true;
+        mCondition.signal(mType);
+    }
+
+    void close() {
+        Mutex::Autolock l(mLock);
+        mOpened = false;
+    }
+
+    void wait() const {
+        Mutex::Autolock l(mLock);
+        while (!mOpened) {
+            mCondition.wait(mLock);
+        }
+    }
+
+private:
+    Condition::WakeUpType mType;
+    volatile bool mOpened;
+    mutable Mutex mLock;
+    mutable Condition mCondition;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_BARRIER_H
diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h
new file mode 100644
index 0000000..340fec7
--- /dev/null
+++ b/libs/hwui/thread/Future.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FUTURE_H
+#define ANDROID_HWUI_FUTURE_H
+
+#include <utils/RefBase.h>
+
+#include "Barrier.h"
+
+namespace android {
+namespace uirenderer {
+
+template<class T>
+class Future: public LightRefBase<Future<T> > {
+public:
+    Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE): mBarrier(type), mResult() { }
+    ~Future() { }
+
+    /**
+     * Returns the result of this future, blocking if
+     * the result is not available yet.
+     */
+    T get() const {
+        mBarrier.wait();
+        return mResult;
+    }
+
+    /**
+     * This method must be called only once.
+     */
+    void produce(T result) {
+        mResult = result;
+        mBarrier.open();
+    }
+
+private:
+    Barrier mBarrier;
+    T mResult;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FUTURE_H
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
new file mode 100644
index 0000000..dcf5449
--- /dev/null
+++ b/libs/hwui/thread/Signal.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_SIGNAL_H
+#define ANDROID_HWUI_SIGNAL_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/threads.h>
+
+namespace android {
+namespace uirenderer {
+
+class Signal {
+public:
+    Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mSignaled(false) { }
+    ~Signal() { }
+
+    void signal() {
+        Mutex::Autolock l(mLock);
+        mSignaled = true;
+        mCondition.signal(mType);
+    }
+
+    void wait() {
+        Mutex::Autolock l(mLock);
+        while (!mSignaled) {
+            mCondition.wait(mLock);
+        }
+        mSignaled = false;
+    }
+
+private:
+    Condition::WakeUpType mType;
+    volatile bool mSignaled;
+    mutable Mutex mLock;
+    mutable Condition mCondition;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_SIGNAL_H