RenderThread work

 Hacky prototype needs a private API to enable

Change-Id: I21e0ddf3cdbd38a4036354b5d6012449e1a34849
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index a195231..70428bc 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -635,11 +635,11 @@
     GLRenderer(boolean translucent) {
         mTranslucent = translucent;
 
-        loadSystemProperties(null);
+        loadSystemProperties();
     }
 
     @Override
-    boolean loadSystemProperties(Surface surface) {
+    boolean loadSystemProperties() {
         boolean value;
         boolean changed = false;
 
@@ -1102,11 +1102,6 @@
     }
 
     @Override
-    HardwareCanvas getCanvas() {
-        return mCanvas;
-    }
-
-    @Override
     void setName(String name) {
         mName = name;
     }
@@ -1129,6 +1124,66 @@
     }
 
     @Override
+    void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
+            HardwareDrawCallbacks callbacks, Rect dirty) {
+        if (canDraw()) {
+            if (!hasDirtyRegions()) {
+                dirty = null;
+            }
+
+            // We are already on the correct thread
+            final int surfaceState = checkRenderContextUnsafe();
+            if (surfaceState != SURFACE_STATE_ERROR) {
+                HardwareCanvas canvas = mCanvas;
+
+                if (mProfileEnabled) {
+                    mProfileLock.lock();
+                }
+
+                dirty = beginFrame(canvas, dirty, surfaceState);
+
+                int saveCount = 0;
+                int status = DisplayList.STATUS_DONE;
+
+                long start = getSystemTime();
+                try {
+                    status = prepareFrame(dirty);
+
+                    saveCount = canvas.save();
+                    callbacks.onHardwarePreDraw(canvas);
+
+                    status |= drawDisplayList(attachInfo, canvas, displayList, status);
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "An error has occurred while drawing:", e);
+                } finally {
+                    callbacks.onHardwarePostDraw(canvas);
+                    canvas.restoreToCount(saveCount);
+
+                    mDrawDelta = getSystemTime() - start;
+
+                    if (mDrawDelta > 0) {
+                        mFrameCount++;
+
+                        debugOverdraw(attachInfo, dirty, canvas, displayList);
+                        debugDirtyRegions(dirty, canvas);
+                        drawProfileData(attachInfo);
+                    }
+                }
+
+                onPostDraw();
+
+                swapBuffers(status);
+
+                if (mProfileEnabled) {
+                    mProfileLock.unlock();
+                }
+
+                attachInfo.mIgnoreDirtyState = false;
+            }
+        }
+    }
+
+    @Override
     void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
             Rect dirty) {
         if (canDraw()) {
@@ -1144,7 +1199,6 @@
             final int surfaceState = checkRenderContextUnsafe();
             if (surfaceState != SURFACE_STATE_ERROR) {
                 HardwareCanvas canvas = mCanvas;
-                attachInfo.mHardwareCanvas = canvas;
 
                 if (mProfileEnabled) {
                     mProfileLock.lock();
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5c0be4a..434d473 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -188,6 +188,9 @@
      */
     public static boolean sSystemRendererDisabled = false;
 
+    /** @hide */
+    public static boolean sUseRenderThread = false;
+
     private boolean mEnabled;
     private boolean mRequested = true;
 
@@ -306,13 +309,6 @@
     abstract int getHeight();
 
     /**
-     * Gets the current canvas associated with this HardwareRenderer.
-     *
-     * @return the current HardwareCanvas
-     */
-    abstract HardwareCanvas getCanvas();
-
-    /**
      * Outputs extra debugging information in the specified file descriptor.
      * @param pw
      */
@@ -335,7 +331,7 @@
      *
      * @return True if a property has changed.
      */
-    abstract boolean loadSystemProperties(Surface surface);
+    abstract boolean loadSystemProperties();
 
     /**
      * Sets the directory to use as a persistent storage for hardware rendering
@@ -412,6 +408,18 @@
             Rect dirty);
 
     /**
+     * Temporary hook to draw a display list directly, only used if sUseRenderThread
+     * is true.
+     *
+     * @param displayList The display list to draw
+     * @param attachInfo AttachInfo tied to the specified view.
+     * @param callbacks Callbacks invoked when drawing happens.
+     * @param dirty The dirty rectangle to update, can be null.
+     */
+    abstract void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
+            HardwareDrawCallbacks callbacks, Rect dirty);
+
+    /**
      * Creates a new hardware layer. A hardware layer built by calling this
      * method will be treated as a texture layer, instead of as a render target.
      *
@@ -517,10 +525,14 @@
      * @return A hardware renderer backed by OpenGL.
      */
     static HardwareRenderer create(boolean translucent) {
+        HardwareRenderer renderer = null;
         if (GLES20Canvas.isAvailable()) {
-            return new GLRenderer(translucent);
+            renderer = new GLRenderer(translucent);
         }
-        return null;
+        if (renderer != null && sUseRenderThread) {
+            renderer = new ThreadedRenderer((GLRenderer)renderer);
+        }
+        return renderer;
     }
 
     /**
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
new file mode 100644
index 0000000..4087313
--- /dev/null
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Log;
+import android.view.Surface.OutOfResourcesException;
+import android.view.View.AttachInfo;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * Hardware renderer that proxies the rendering to a render thread. Most calls
+ * are synchronous, however a few such as draw() are posted async. The display list
+ * is shared between the two threads and is guarded by a top level lock.
+ *
+ * The UI thread can block on the RenderThread, but RenderThread must never
+ * block on the UI thread.
+ *
+ * Note that although currently the EGL context & surfaces are created & managed
+ * by the render thread, the goal is to move that into a shared structure that can
+ * be managed by both threads. EGLSurface creation & deletion should ideally be
+ * done on the UI thread and not the RenderThread to avoid stalling the
+ * RenderThread with surface buffer allocation.
+ *
+ * @hide
+ */
+public class ThreadedRenderer extends HardwareRenderer {
+    private static final String LOGTAG = "ThreadedRenderer";
+
+    @SuppressWarnings("serial")
+    static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{
+        Method[] methods = HardwareRenderer.class.getDeclaredMethods();
+        for (Method m : methods) {
+            put(m.getName(), m);
+        }
+    }};
+    static boolean sNeedsInit = true;
+
+    private HardwareRenderer mRemoteRenderer;
+    private int mWidth, mHeight;
+    private RTJob mPreviousDraw;
+
+    ThreadedRenderer(GLRenderer backingRenderer) {
+        mRemoteRenderer = backingRenderer;
+        setEnabled(true);
+        if (sNeedsInit) {
+            sNeedsInit = false;
+            postToRenderThread(new Runnable() {
+                @Override
+                public void run() {
+                    // Hack to allow GLRenderer to create a handler to post the EGL
+                    // destruction to, although it'll never run
+                    Looper.prepare();
+                }
+            });
+        }
+    }
+
+    @Override
+    void destroy(boolean full) {
+        run("destroy", full);
+    }
+
+    @Override
+    boolean initialize(Surface surface) throws OutOfResourcesException {
+        return (Boolean) run("initialize", surface);
+    }
+
+    @Override
+    void updateSurface(Surface surface) throws OutOfResourcesException {
+        post("updateSurface", surface);
+    }
+
+    @Override
+    void destroyLayers(View view) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void destroyHardwareResources(View view) {
+        run("destroyHardwareResources", view);
+    }
+
+    @Override
+    void invalidate(Surface surface) {
+        post("invalidate", surface);
+    }
+
+    @Override
+    boolean validate() {
+        // TODO Remove users of this API
+        return false;
+    }
+
+    @Override
+    boolean safelyRun(Runnable action) {
+        return (Boolean) run("safelyRun", action);
+    }
+
+    @Override
+    void setup(int width, int height) {
+        mWidth = width;
+        mHeight = height;
+        post("setup", width, height);
+    }
+
+    @Override
+    int getWidth() {
+        return mWidth;
+    }
+
+    @Override
+    int getHeight() {
+        return mHeight;
+    }
+
+    @Override
+    void dumpGfxInfo(PrintWriter pw) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    long getFrameCount() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    boolean loadSystemProperties() {
+        return (Boolean) run("loadSystemProperties");
+    }
+
+    @Override
+    void pushLayerUpdate(HardwareLayer layer) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void cancelLayerUpdate(HardwareLayer layer) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void flushLayerUpdates() {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void drawDisplayList(DisplayList displayList, AttachInfo attachInfo,
+            HardwareDrawCallbacks callbacks, Rect dirty) {
+        throw new NoSuchMethodError();
+    }
+
+    /**
+     * TODO: Remove
+     * Temporary hack to allow RenderThreadTest prototype app to trigger
+     * replaying a DisplayList after modifying the displaylist properties
+     *
+     *  @hide */
+    public void repeatLastDraw() {
+        if (mPreviousDraw == null) {
+            throw new IllegalStateException("There isn't a previous draw");
+        }
+        synchronized (mPreviousDraw) {
+            mPreviousDraw.completed = false;
+        }
+        mPreviousDraw.args[3] = null;
+        postToRenderThread(mPreviousDraw);
+    }
+
+    @Override
+    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
+        requireCompletion(mPreviousDraw);
+
+        attachInfo.mIgnoreDirtyState = true;
+        attachInfo.mDrawingTime = SystemClock.uptimeMillis();
+        view.mPrivateFlags |= View.PFLAG_DRAWN;
+
+        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
+                == View.PFLAG_INVALIDATED;
+        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
+
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+        DisplayList displayList = view.getDisplayList();
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+
+        view.mRecreateDisplayList = false;
+
+        mPreviousDraw = post("drawDisplayList", displayList, attachInfo,
+                callbacks, dirty);
+    }
+
+    @Override
+    HardwareLayer createHardwareLayer(boolean isOpaque) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void detachFunctor(int functor) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    boolean attachFunctor(AttachInfo attachInfo, int functor) {
+        throw new NoSuchMethodError();
+    }
+
+    @Override
+    void setName(String name) {
+        post("setName", name);
+    }
+
+    private static void requireCompletion(RTJob job) {
+        if (job != null) {
+            synchronized (job) {
+                if (!job.completed) {
+                    try {
+                        job.wait();
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        }
+    }
+
+    private RTJob post(String method, Object... args) {
+        RTJob job = new RTJob();
+        job.method = sMethodLut.get(method);
+        job.args = args;
+        job.target = mRemoteRenderer;
+        if (job.method == null) {
+            throw new NullPointerException("Couldn't find method: " + method);
+        }
+        postToRenderThread(job);
+        return job;
+    }
+
+    private Object run(String method, Object... args) {
+        RTJob job = new RTJob();
+        job.method = sMethodLut.get(method);
+        job.args = args;
+        job.target = mRemoteRenderer;
+        if (job.method == null) {
+            throw new NullPointerException("Couldn't find method: " + method);
+        }
+        synchronized (job) {
+            postToRenderThread(job);
+            try {
+                job.wait();
+                return job.ret;
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    static class RTJob implements Runnable {
+        Method method;
+        Object[] args;
+        Object target;
+        Object ret;
+        boolean completed = false;
+
+        @Override
+        public void run() {
+            try {
+                ret = method.invoke(target, args);
+                synchronized (this) {
+                    completed = true;
+                    notify();
+                }
+            } catch (Exception e) {
+                Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e);
+            }
+        }
+    }
+
+    /** @hide */
+    public static native void postToRenderThread(Runnable runnable);
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5d264b6..05366a7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11209,6 +11209,13 @@
     }
 
     /**
+     * @hide
+     */
+    public HardwareRenderer getHardwareRenderer() {
+        return mAttachInfo != null ? mAttachInfo.mHardwareRenderer : null;
+    }
+
+    /**
      * <p>Causes the Runnable to be added to the message queue.
      * The runnable will be run on the user interface thread.</p>
      *
@@ -18843,8 +18850,6 @@
 
         final Callbacks mRootCallbacks;
 
-        HardwareCanvas mHardwareCanvas;
-
         IWindowId mIWindowId;
         WindowId mWindowId;
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ff74f9d..cbaf921 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -266,10 +266,10 @@
     int mScrollY;
     int mCurScrollY;
     Scroller mScroller;
-    HardwareLayer mResizeBuffer;
-    long mResizeBufferStartTime;
-    int mResizeBufferDuration;
-    static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
+//    HardwareLayer mResizeBuffer;
+//    long mResizeBufferStartTime;
+//    int mResizeBufferDuration;
+//    static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
     private ArrayList<LayoutTransition> mPendingTransitions;
 
     final ViewConfiguration mViewConfiguration;
@@ -933,17 +933,17 @@
         return mAppVisible ? mView.getVisibility() : View.GONE;
     }
 
-    void disposeResizeBuffer() {
-        if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) {
-            mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
-                @Override
-                public void run() {
-                    mResizeBuffer.destroy();
-                    mResizeBuffer = null;
-                }
-            });
-        }
-    }
+//    void disposeResizeBuffer() {
+//        if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) {
+//            mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() {
+//                @Override
+//                public void run() {
+//                    mResizeBuffer.destroy();
+//                    mResizeBuffer = null;
+//                }
+//            });
+//        }
+//    }
 
     /**
      * Add LayoutTransition to the list of transitions to be started in the next traversal.
@@ -1454,75 +1454,76 @@
                 final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals(
                         mAttachInfo.mVisibleInsets);
                 if (contentInsetsChanged) {
-                    if (mWidth > 0 && mHeight > 0 && lp != null &&
-                            ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
-                                    & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 &&
-                            mSurface != null && mSurface.isValid() &&
-                            !mAttachInfo.mTurnOffWindowResizeAnim &&
-                            mAttachInfo.mHardwareRenderer != null &&
-                            mAttachInfo.mHardwareRenderer.isEnabled() &&
-                            mAttachInfo.mHardwareRenderer.validate() &&
-                            lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
-
-                        disposeResizeBuffer();
-
-                        boolean completed = false;
-                        HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
-                        HardwareCanvas layerCanvas = null;
-                        try {
-                            if (mResizeBuffer == null) {
-                                mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
-                                        mWidth, mHeight, false);
-                            } else if (mResizeBuffer.getWidth() != mWidth ||
-                                    mResizeBuffer.getHeight() != mHeight) {
-                                mResizeBuffer.resize(mWidth, mHeight);
-                            }
-                            // TODO: should handle create/resize failure
-                            layerCanvas = mResizeBuffer.start(hwRendererCanvas);
-                            final int restoreCount = layerCanvas.save();
-
-                            int yoff;
-                            final boolean scrolling = mScroller != null
-                                    && mScroller.computeScrollOffset();
-                            if (scrolling) {
-                                yoff = mScroller.getCurrY();
-                                mScroller.abortAnimation();
-                            } else {
-                                yoff = mScrollY;
-                            }
-
-                            layerCanvas.translate(0, -yoff);
-                            if (mTranslator != null) {
-                                mTranslator.translateCanvas(layerCanvas);
-                            }
-
-                            DisplayList displayList = mView.mDisplayList;
-                            if (displayList != null && displayList.isValid()) {
-                                layerCanvas.drawDisplayList(displayList, null,
-                                        DisplayList.FLAG_CLIP_CHILDREN);
-                            } else {
-                                mView.draw(layerCanvas);
-                            }
-
-                            drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
-
-                            mResizeBufferStartTime = SystemClock.uptimeMillis();
-                            mResizeBufferDuration = mView.getResources().getInteger(
-                                    com.android.internal.R.integer.config_mediumAnimTime);
-                            completed = true;
-
-                            layerCanvas.restoreToCount(restoreCount);
-                        } catch (OutOfMemoryError e) {
-                            Log.w(TAG, "Not enough memory for content change anim buffer", e);
-                        } finally {
-                            if (mResizeBuffer != null) {
-                                mResizeBuffer.end(hwRendererCanvas);
-                                if (!completed) {
-                                    disposeResizeBuffer();
-                                }
-                            }
-                        }
-                    }
+//                    TODO: Do something with this...
+//                    if (mWidth > 0 && mHeight > 0 && lp != null &&
+//                            ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility)
+//                                    & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 &&
+//                            mSurface != null && mSurface.isValid() &&
+//                            !mAttachInfo.mTurnOffWindowResizeAnim &&
+//                            mAttachInfo.mHardwareRenderer != null &&
+//                            mAttachInfo.mHardwareRenderer.isEnabled() &&
+//                            mAttachInfo.mHardwareRenderer.validate() &&
+//                            lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
+//
+//                        disposeResizeBuffer();
+//
+//                        boolean completed = false;
+//                        HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas();
+//                        HardwareCanvas layerCanvas = null;
+//                        try {
+//                            if (mResizeBuffer == null) {
+//                                mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+//                                        mWidth, mHeight, false);
+//                            } else if (mResizeBuffer.getWidth() != mWidth ||
+//                                    mResizeBuffer.getHeight() != mHeight) {
+//                                mResizeBuffer.resize(mWidth, mHeight);
+//                            }
+//                            // TODO: should handle create/resize failure
+//                            layerCanvas = mResizeBuffer.start(hwRendererCanvas);
+//                            final int restoreCount = layerCanvas.save();
+//
+//                            int yoff;
+//                            final boolean scrolling = mScroller != null
+//                                    && mScroller.computeScrollOffset();
+//                            if (scrolling) {
+//                                yoff = mScroller.getCurrY();
+//                                mScroller.abortAnimation();
+//                            } else {
+//                                yoff = mScrollY;
+//                            }
+//
+//                            layerCanvas.translate(0, -yoff);
+//                            if (mTranslator != null) {
+//                                mTranslator.translateCanvas(layerCanvas);
+//                            }
+//
+//                            DisplayList displayList = mView.mDisplayList;
+//                            if (displayList != null && displayList.isValid()) {
+//                                layerCanvas.drawDisplayList(displayList, null,
+//                                        DisplayList.FLAG_CLIP_CHILDREN);
+//                            } else {
+//                                mView.draw(layerCanvas);
+//                            }
+//
+//                            drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
+//
+//                            mResizeBufferStartTime = SystemClock.uptimeMillis();
+//                            mResizeBufferDuration = mView.getResources().getInteger(
+//                                    com.android.internal.R.integer.config_mediumAnimTime);
+//                            completed = true;
+//
+//                            layerCanvas.restoreToCount(restoreCount);
+//                        } catch (OutOfMemoryError e) {
+//                            Log.w(TAG, "Not enough memory for content change anim buffer", e);
+//                        } finally {
+//                            if (mResizeBuffer != null) {
+//                                mResizeBuffer.end(hwRendererCanvas);
+//                                if (!completed) {
+//                                    disposeResizeBuffer();
+//                                }
+//                            }
+//                        }
+//                    }
                     mAttachInfo.mContentInsets.set(mPendingContentInsets);
                     if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
                             + mAttachInfo.mContentInsets);
@@ -1582,7 +1583,7 @@
                     if (mScroller != null) {
                         mScroller.abortAnimation();
                     }
-                    disposeResizeBuffer();
+//                    disposeResizeBuffer();
                     // Our surface is gone
                     if (mAttachInfo.mHardwareRenderer != null &&
                             mAttachInfo.mHardwareRenderer.isEnabled()) {
@@ -2181,23 +2182,28 @@
 
     @Override
     public void onHardwarePostDraw(HardwareCanvas canvas) {
-        if (mResizeBuffer != null) {
-            mResizePaint.setAlpha(mResizeAlpha);
-            canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
+//        if (mResizeBuffer != null) {
+//            mResizePaint.setAlpha(mResizeAlpha);
+//            canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
+//        }
+        // TODO: this
+        if (!HardwareRenderer.sUseRenderThread) {
+            drawAccessibilityFocusedDrawableIfNeeded(canvas);
         }
-        drawAccessibilityFocusedDrawableIfNeeded(canvas);
     }
 
     /**
      * @hide
      */
     void outputDisplayList(View view) {
-        if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) {
-            DisplayList displayList = view.getDisplayList();
-            if (displayList != null) {
-                mAttachInfo.mHardwareCanvas.outputDisplayList(displayList);
-            }
-        }
+        // TODO - route through HardwareCanvas so it can be
+        //        proxied to the correct thread
+//        if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) {
+//            DisplayList displayList = view.getDisplayList();
+//            if (displayList != null) {
+//                mAttachInfo.mHardwareCanvas.outputDisplayList(displayList);
+//            }
+//        }
     }
 
     /**
@@ -2342,17 +2348,17 @@
         final boolean scalingRequired = attachInfo.mScalingRequired;
 
         int resizeAlpha = 0;
-        if (mResizeBuffer != null) {
-            long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
-            if (deltaTime < mResizeBufferDuration) {
-                float amt = deltaTime/(float) mResizeBufferDuration;
-                amt = mResizeInterpolator.getInterpolation(amt);
-                animating = true;
-                resizeAlpha = 255 - (int)(amt*255);
-            } else {
-                disposeResizeBuffer();
-            }
-        }
+//        if (mResizeBuffer != null) {
+//            long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
+//            if (deltaTime < mResizeBufferDuration) {
+//                float amt = deltaTime/(float) mResizeBufferDuration;
+//                amt = mResizeInterpolator.getInterpolation(amt);
+//                animating = true;
+//                resizeAlpha = 255 - (int)(amt*255);
+//            } else {
+//                disposeResizeBuffer();
+//            }
+//        }
 
         final Rect dirty = mDirty;
         if (mSurfaceHolder != null) {
@@ -2362,7 +2368,7 @@
                 if (mScroller != null) {
                     mScroller.abortAnimation();
                 }
-                disposeResizeBuffer();
+//                disposeResizeBuffer();
             }
             return;
         }
@@ -2725,7 +2731,7 @@
         if (scrollY != mScrollY) {
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
                     + mScrollY + " , new=" + scrollY);
-            if (!immediate && mResizeBuffer == null) {
+            if (!immediate /*&& mResizeBuffer == null*/) {
                 if (mScroller == null) {
                     mScroller = new Scroller(mView.getContext());
                 }
@@ -5404,7 +5410,7 @@
 
                 // Hardware rendering
                 if (mAttachInfo.mHardwareRenderer != null) {
-                    if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mSurface)) {
+                    if (mAttachInfo.mHardwareRenderer.loadSystemProperties()) {
                         invalidate();
                     }
                 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ca519d1..a558909 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -55,6 +55,7 @@
 	android_view_GLRenderer.cpp \
 	android_view_GLES20DisplayList.cpp \
 	android_view_GLES20Canvas.cpp \
+	android_view_ThreadedRenderer.cpp \
 	android_view_MotionEvent.cpp \
 	android_view_PointerIcon.cpp \
 	android_view_VelocityTracker.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 783e794..b20dc09 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,6 +123,7 @@
 extern int register_android_view_GLES20DisplayList(JNIEnv* env);
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_GLRenderer(JNIEnv* env);
+extern int register_android_view_ThreadedRenderer(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_SurfaceControl(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
@@ -1127,6 +1128,7 @@
     REG_JNI(register_android_view_GLES20DisplayList),
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_GLRenderer),
+    REG_JNI(register_android_view_ThreadedRenderer),
     REG_JNI(register_android_view_Surface),
     REG_JNI(register_android_view_SurfaceControl),
     REG_JNI(register_android_view_SurfaceSession),
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
new file mode 100644
index 0000000..8bb6cb4
--- /dev/null
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#define LOG_TAG "GLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <renderthread/RenderTask.h>
+#include <renderthread/RenderThread.h>
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+
+namespace RT = android::uirenderer::renderthread;
+
+static jmethodID gRunnableMethod;
+
+class JavaTask : public RT::RenderTask {
+public:
+    JavaTask(JNIEnv* env, jobject jrunnable) {
+        env->GetJavaVM(&mVm);
+        mRunnable = env->NewGlobalRef(jrunnable);
+    }
+
+    virtual ~JavaTask() {
+        env()->DeleteGlobalRef(mRunnable);
+    }
+
+    virtual void run() {
+        env()->CallVoidMethod(mRunnable, gRunnableMethod);
+    };
+
+private:
+    JNIEnv* env() {
+        JNIEnv* env;
+        if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+            return 0;
+        }
+        return env;
+    }
+
+    JavaVM* mVm;
+    jobject mRunnable;
+};
+
+static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
+        jobject jrunnable) {
+    RT::RenderTask* task = new JavaTask(env, jrunnable);
+    RT::RenderThread::getInstance().queue(task);
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "android/view/ThreadedRenderer";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+    { "postToRenderThread", "(Ljava/lang/Runnable;)V",   (void*) android_view_ThreadedRenderer_postToRenderThread },
+#endif
+};
+
+int register_android_view_ThreadedRenderer(JNIEnv* env) {
+#ifdef USE_OPENGL_RENDERER
+    jclass cls = env->FindClass("java/lang/Runnable");
+    gRunnableMethod = env->GetMethodID(cls, "run", "()V");
+    env->DeleteLocalRef(cls);
+#endif
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+}; // namespace android