Native-side proxy

 Remove RemoteGLRenderer
 Remove reflection-based control

Change-Id: If17c2bbb61c7141986d88c4763def77ed1074985
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 1811111..986c296 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -1413,15 +1413,13 @@
     }
 
     @Override
-    boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
+    void attachFunctor(View.AttachInfo attachInfo, int functor) {
         if (mCanvas != null) {
             mCanvas.attachFunctor(functor);
             mFunctorsRunnable.attachInfo = attachInfo;
             attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
             attachInfo.mHandler.postDelayed(mFunctorsRunnable,  0);
-            return true;
         }
-        return false;
     }
 
     /**
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ac728f1..676ac10 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -466,9 +466,8 @@
      * @see HardwareCanvas#callDrawGLFunction(int)
      * @see #detachFunctor(int)
      *
-     * @return true if the functor was attached successfully
      */
-    abstract boolean attachFunctor(View.AttachInfo attachInfo, int functor);
+    abstract void attachFunctor(View.AttachInfo attachInfo, int functor);
 
     /**
      * Initializes the hardware renderer for the specified surface and setup the
diff --git a/core/java/android/view/RemoteGLRenderer.java b/core/java/android/view/RemoteGLRenderer.java
deleted file mode 100644
index 2919775..0000000
--- a/core/java/android/view/RemoteGLRenderer.java
+++ /dev/null
@@ -1,1074 +0,0 @@
-/*
- * 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.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Surface.OutOfResourcesException;
-
-import java.io.PrintWriter;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Hardware renderer using OpenGL that's used as the remote endpoint
- * of ThreadedRenderer
- *
- * Currently this is mostly a copy of GLRenderer, but with a few modifications
- * to deal with the threading issues. Ideally native-side functionality
- * will replace this, but we need this to bootstrap without risking breaking
- * changes in GLRenderer
- *
- * @hide
- */
-public class RemoteGLRenderer extends HardwareRenderer {
-    static final int SURFACE_STATE_ERROR = 0;
-    static final int SURFACE_STATE_SUCCESS = 1;
-    static final int SURFACE_STATE_UPDATED = 2;
-
-    static final int FUNCTOR_PROCESS_DELAY = 4;
-
-    /**
-     * Number of frames to profile.
-     */
-    private static final int PROFILE_MAX_FRAMES = 128;
-
-    /**
-     * Number of floats per profiled frame.
-     */
-    private static final int PROFILE_FRAME_DATA_COUNT = 3;
-
-    private static final int PROFILE_DRAW_MARGIN = 0;
-    private static final int PROFILE_DRAW_WIDTH = 3;
-    private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
-    private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
-    private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
-    private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
-    private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
-    private static final String[] VISUALIZERS = {
-            PROFILE_PROPERTY_VISUALIZE_BARS,
-            PROFILE_PROPERTY_VISUALIZE_LINES
-    };
-
-    private static final String[] OVERDRAW = {
-            OVERDRAW_PROPERTY_SHOW,
-            OVERDRAW_PROPERTY_COUNT
-    };
-    private static final int OVERDRAW_TYPE_COUNT = 1;
-    private static final int GL_VERSION = 2;
-
-    int mWidth = -1, mHeight = -1;
-
-    HardwareCanvas mCanvas;
-
-    String mName;
-
-    long mFrameCount;
-    Paint mDebugPaint;
-
-    boolean mDirtyRegionsEnabled;
-    boolean mSurfaceUpdated;
-
-    boolean mProfileEnabled;
-    int mProfileVisualizerType = -1;
-    float[] mProfileData;
-    ReentrantLock mProfileLock;
-    int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-
-    GraphDataProvider mDebugDataProvider;
-    float[][] mProfileShapes;
-    Paint mProfilePaint;
-
-    boolean mDebugDirtyRegions;
-    int mDebugOverdraw = -1;
-    HardwareLayer mDebugOverdrawLayer;
-    Paint mDebugOverdrawPaint;
-
-    final boolean mTranslucent;
-
-    private final Rect mRedrawClip = new Rect();
-
-    private final int[] mSurfaceSize = new int[2];
-    private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
-
-    private long mDrawDelta = Long.MAX_VALUE;
-
-    private GLES20Canvas mGlCanvas;
-
-    private DisplayMetrics mDisplayMetrics;
-    private ThreadedRenderer mOwningRenderer;
-    private long mNativeCanvasContext;
-
-    HardwareCanvas createCanvas() {
-        return mGlCanvas = new GLES20Canvas(mTranslucent);
-    }
-
-    void initCaches() {
-        if (GLES20Canvas.initCaches()) {
-            // Caches were (re)initialized, rebind atlas
-            initAtlas();
-        }
-    }
-
-    void initAtlas() {
-        IBinder binder = ServiceManager.getService("assetatlas");
-        if (binder == null) return;
-
-        IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
-        try {
-            if (atlas.isCompatible(android.os.Process.myPpid())) {
-                GraphicBuffer buffer = atlas.getBuffer();
-                if (buffer != null) {
-                    int[] map = atlas.getMap();
-                    if (map != null) {
-                        GLES20Canvas.initAtlas(buffer, map);
-                    }
-                    // If IAssetAtlas is not the same class as the IBinder
-                    // we are using a remote service and we can safely
-                    // destroy the graphic buffer
-                    if (atlas.getClass() != binder.getClass()) {
-                        buffer.destroy();
-                    }
-                }
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Could not acquire atlas", e);
-        }
-    }
-
-    boolean canDraw() {
-        return mCanvas != null && mGlCanvas != null;
-    }
-
-    int onPreDraw(Rect dirty) {
-        return mGlCanvas.onPreDraw(dirty);
-    }
-
-    void onPostDraw() {
-        mGlCanvas.onPostDraw();
-    }
-
-    void drawProfileData(View.AttachInfo attachInfo) {
-        if (mDebugDataProvider != null) {
-            final GraphDataProvider provider = mDebugDataProvider;
-            initProfileDrawData(attachInfo, provider);
-
-            final int height = provider.getVerticalUnitSize();
-            final int margin = provider.getHorizontaUnitMargin();
-            final int width = provider.getHorizontalUnitSize();
-
-            int x = 0;
-            int count = 0;
-            int current = 0;
-
-            final float[] data = provider.getData();
-            final int elementCount = provider.getElementCount();
-            final int graphType = provider.getGraphType();
-
-            int totalCount = provider.getFrameCount() * elementCount;
-            if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
-                totalCount -= elementCount;
-            }
-
-            for (int i = 0; i < totalCount; i += elementCount) {
-                if (data[i] < 0.0f) break;
-
-                int index = count * 4;
-                if (i == provider.getCurrentFrame() * elementCount) current = index;
-
-                x += margin;
-                int x2 = x + width;
-
-                int y2 = mHeight;
-                int y1 = (int) (y2 - data[i] * height);
-
-                switch (graphType) {
-                    case GraphDataProvider.GRAPH_TYPE_BARS: {
-                        for (int j = 0; j < elementCount; j++) {
-                            //noinspection MismatchedReadAndWriteOfArray
-                            final float[] r = mProfileShapes[j];
-                            r[index] = x;
-                            r[index + 1] = y1;
-                            r[index + 2] = x2;
-                            r[index + 3] = y2;
-
-                            y2 = y1;
-                            if (j < elementCount - 1) {
-                                y1 = (int) (y2 - data[i + j + 1] * height);
-                            }
-                        }
-                    } break;
-                    case GraphDataProvider.GRAPH_TYPE_LINES: {
-                        for (int j = 0; j < elementCount; j++) {
-                            //noinspection MismatchedReadAndWriteOfArray
-                            final float[] r = mProfileShapes[j];
-                            r[index] = (x + x2) * 0.5f;
-                            r[index + 1] = index == 0 ? y1 : r[index - 1];
-                            r[index + 2] = r[index] + width;
-                            r[index + 3] = y1;
-
-                            y2 = y1;
-                            if (j < elementCount - 1) {
-                                y1 = (int) (y2 - data[i + j + 1] * height);
-                            }
-                        }
-                    } break;
-                }
-
-
-                x += width;
-                count++;
-            }
-
-            x += margin;
-
-            drawGraph(graphType, count);
-            drawCurrentFrame(graphType, current);
-            drawThreshold(x, height);
-        }
-    }
-
-    private void drawGraph(int graphType, int count) {
-        for (int i = 0; i < mProfileShapes.length; i++) {
-            mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
-            switch (graphType) {
-                case GraphDataProvider.GRAPH_TYPE_BARS:
-                    mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
-                    break;
-                case GraphDataProvider.GRAPH_TYPE_LINES:
-                    mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
-                    break;
-            }
-        }
-    }
-
-    private void drawCurrentFrame(int graphType, int index) {
-        if (index >= 0) {
-            mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
-            switch (graphType) {
-                case GraphDataProvider.GRAPH_TYPE_BARS:
-                    mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
-                            mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
-                            mProfilePaint);
-                    break;
-                case GraphDataProvider.GRAPH_TYPE_LINES:
-                    mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
-                            mProfileShapes[2][index], mHeight, mProfilePaint);
-                    break;
-            }
-        }
-    }
-
-    private void drawThreshold(int x, int height) {
-        float threshold = mDebugDataProvider.getThreshold();
-        if (threshold > 0.0f) {
-            mDebugDataProvider.setupThresholdPaint(mProfilePaint);
-            int y = (int) (mHeight - threshold * height);
-            mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
-        }
-    }
-
-    private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
-        if (mProfileShapes == null) {
-            final int elementCount = provider.getElementCount();
-            final int frameCount = provider.getFrameCount();
-
-            mProfileShapes = new float[elementCount][];
-            for (int i = 0; i < elementCount; i++) {
-                mProfileShapes[i] = new float[frameCount * 4];
-            }
-
-            mProfilePaint = new Paint();
-        }
-
-        mProfilePaint.reset();
-        if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
-            mProfilePaint.setAntiAlias(true);
-        }
-
-        if (mDisplayMetrics == null) {
-            mDisplayMetrics = new DisplayMetrics();
-        }
-
-        attachInfo.mDisplay.getMetrics(mDisplayMetrics);
-        provider.prepare(mDisplayMetrics);
-    }
-
-    @Override
-    void destroy(boolean full) {
-        try {
-            if (full && mCanvas != null) {
-                mCanvas = null;
-            }
-            if (mNativeCanvasContext != 0) {
-                destroyContext(mNativeCanvasContext);
-                mNativeCanvasContext = 0;
-            }
-            setEnabled(false);
-        } finally {
-            if (full && mGlCanvas != null) {
-                mGlCanvas = null;
-            }
-        }
-    }
-
-    @Override
-    void pushLayerUpdate(HardwareLayer layer) {
-        mGlCanvas.pushLayerUpdate(layer);
-    }
-
-    @Override
-    void cancelLayerUpdate(HardwareLayer layer) {
-        mGlCanvas.cancelLayerUpdate(layer);
-    }
-
-    @Override
-    void flushLayerUpdates() {
-        mGlCanvas.flushLayerUpdates();
-    }
-
-    @Override
-    HardwareLayer createHardwareLayer(boolean isOpaque) {
-        return new GLES20TextureLayer(isOpaque);
-    }
-
-    @Override
-    public HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) {
-        return new GLES20RenderLayer(width, height, isOpaque);
-    }
-
-    void countOverdraw(HardwareCanvas canvas) {
-        ((GLES20Canvas) canvas).setCountOverdrawEnabled(true);
-    }
-
-    float getOverdraw(HardwareCanvas canvas) {
-        return ((GLES20Canvas) canvas).getOverdraw();
-    }
-
-    @Override
-    public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
-        return ((GLES20TextureLayer) layer).getSurfaceTexture();
-    }
-
-    @Override
-    void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) {
-        ((GLES20TextureLayer) layer).setSurfaceTexture(surfaceTexture);
-    }
-
-    @Override
-    boolean safelyRun(Runnable action) {
-        boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-
-        if (needsContext) {
-            if (!usePBufferSurface()) {
-                return false;
-            }
-        }
-
-        action.run();
-
-        return true;
-    }
-
-    @Override
-    void destroyLayers(final View view) {
-        if (view != null) {
-            safelyRun(new Runnable() {
-                @Override
-                public void run() {
-                    if (mCanvas != null) {
-                        mCanvas.clearLayerUpdates();
-                    }
-                    destroyHardwareLayer(view);
-                    GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
-                }
-            });
-        }
-    }
-
-    private static void destroyHardwareLayer(View view) {
-        view.destroyLayer(true);
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-
-            int count = group.getChildCount();
-            for (int i = 0; i < count; i++) {
-                destroyHardwareLayer(group.getChildAt(i));
-            }
-        }
-    }
-
-    @Override
-    void destroyHardwareResources(final View view) {
-        if (view != null) {
-            safelyRun(new Runnable() {
-                @Override
-                public void run() {
-                    if (mCanvas != null) {
-                        mCanvas.clearLayerUpdates();
-                    }
-                    destroyResources(view);
-                    GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
-                }
-            });
-        }
-    }
-
-    private static void destroyResources(View view) {
-        view.destroyHardwareResources();
-
-        if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup) view;
-
-            int count = group.getChildCount();
-            for (int i = 0; i < count; i++) {
-                destroyResources(group.getChildAt(i));
-            }
-        }
-    }
-
-    RemoteGLRenderer(ThreadedRenderer owningRenderer, boolean translucent) {
-        mOwningRenderer = owningRenderer;
-        mTranslucent = translucent;
-
-        loadSystemProperties();
-    }
-
-    @Override
-    boolean loadSystemProperties() {
-        boolean value;
-        boolean changed = false;
-
-        String profiling = SystemProperties.get(PROFILE_PROPERTY);
-        int graphType = search(VISUALIZERS, profiling);
-        value = graphType >= 0;
-
-        if (graphType != mProfileVisualizerType) {
-            changed = true;
-            mProfileVisualizerType = graphType;
-
-            mProfileShapes = null;
-            mProfilePaint = null;
-
-            if (value) {
-                mDebugDataProvider = new DrawPerformanceDataProvider(graphType);
-            } else {
-                mDebugDataProvider = null;
-            }
-        }
-
-        // If on-screen profiling is not enabled, we need to check whether
-        // console profiling only is enabled
-        if (!value) {
-            value = Boolean.parseBoolean(profiling);
-        }
-
-        if (value != mProfileEnabled) {
-            changed = true;
-            mProfileEnabled = value;
-
-            if (mProfileEnabled) {
-                Log.d(LOG_TAG, "Profiling hardware renderer");
-
-                int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
-                        PROFILE_MAX_FRAMES);
-                mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
-                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
-                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
-                }
-
-                mProfileLock = new ReentrantLock();
-            } else {
-                mProfileData = null;
-                mProfileLock = null;
-                mProfileVisualizerType = -1;
-            }
-
-            mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-        }
-
-        value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
-        if (value != mDebugDirtyRegions) {
-            changed = true;
-            mDebugDirtyRegions = value;
-
-            if (mDebugDirtyRegions) {
-                Log.d(LOG_TAG, "Debugging dirty regions");
-            }
-        }
-
-        String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
-        int debugOverdraw = search(OVERDRAW, overdraw);
-        if (debugOverdraw != mDebugOverdraw) {
-            changed = true;
-            mDebugOverdraw = debugOverdraw;
-
-            if (mDebugOverdraw != OVERDRAW_TYPE_COUNT) {
-                if (mDebugOverdrawLayer != null) {
-                    mDebugOverdrawLayer.destroy();
-                    mDebugOverdrawLayer = null;
-                    mDebugOverdrawPaint = null;
-                }
-            }
-        }
-
-        if (GLRenderer.loadProperties()) {
-            changed = true;
-        }
-
-        return changed;
-    }
-
-    private static int search(String[] values, String value) {
-        for (int i = 0; i < values.length; i++) {
-            if (values[i].equals(value)) return i;
-        }
-        return -1;
-    }
-
-    @Override
-    void dumpGfxInfo(PrintWriter pw) {
-        if (mProfileEnabled) {
-            pw.printf("\n\tDraw\tProcess\tExecute\n");
-
-            mProfileLock.lock();
-            try {
-                for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
-                    if (mProfileData[i] < 0) {
-                        break;
-                    }
-                    pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
-                            mProfileData[i + 2]);
-                    mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
-                }
-                mProfileCurrentFrame = mProfileData.length;
-            } finally {
-                mProfileLock.unlock();
-            }
-        }
-    }
-
-    @Override
-    long getFrameCount() {
-        return mFrameCount;
-    }
-
-    /**
-     * Indicates whether this renderer instance can track and update dirty regions.
-     */
-    boolean hasDirtyRegions() {
-        return mDirtyRegionsEnabled;
-    }
-
-    private void triggerSoftwareFallback() {
-        destroy(true);
-        // we'll try again if it was context lost
-        setRequested(false);
-        Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
-                + "Switching back to software rendering.");
-    }
-
-    @Override
-    boolean initialize(Surface surface) throws OutOfResourcesException {
-        if (isRequested() && !isEnabled()) {
-            mNativeCanvasContext = createContext();
-            boolean surfaceCreated = createEglSurface(surface);
-
-            if (surfaceCreated) {
-                if (mCanvas == null) {
-                    mCanvas = createCanvas();
-                }
-                setEnabled(true);
-                initAtlas();
-                return true;
-            } else {
-                destroy(true);
-                setRequested(false);
-            }
-        }
-        return false;
-    }
-
-    @Override
-    void updateSurface(Surface surface) throws OutOfResourcesException {
-        if (isRequested() && isEnabled()) {
-            createEglSurface(surface);
-        }
-    }
-
-    boolean createEglSurface(Surface surface) throws OutOfResourcesException {
-        // Create an EGL surface we can render into.
-        if (!setSurface(mNativeCanvasContext, surface)) {
-            return false;
-        }
-        makeCurrent(mNativeCanvasContext);
-        mSurfaceUpdated = true;
-
-        initCaches();
-        return true;
-    }
-
-    @Override
-    void invalidate(Surface surface) {
-        setSurface(mNativeCanvasContext, null);
-        setEnabled(false);
-
-        if (surface.isValid()) {
-            if (createEglSurface(surface) && mCanvas != null) {
-                setEnabled(true);
-            }
-        }
-    }
-
-    @Override
-    boolean validate() {
-        return checkRenderContext() != SURFACE_STATE_ERROR;
-    }
-
-    @Override
-    void setup(int width, int height) {
-        if (validate()) {
-            mCanvas.setViewport(width, height);
-            mWidth = width;
-            mHeight = height;
-        }
-    }
-
-    @Override
-    int getWidth() {
-        return mWidth;
-    }
-
-    @Override
-    int getHeight() {
-        return mHeight;
-    }
-
-    @Override
-    void setName(String name) {
-        mName = name;
-    }
-
-    // TODO: Ping pong is fun and all, but this isn't the time or place
-    // However we don't yet have the ability for the RenderThread to run
-    // independently nor have a way to postDelayed, so this will work for now
-    private Runnable mDispatchFunctorsRunnable = new Runnable() {
-        @Override
-        public void run() {
-            ThreadedRenderer.postToRenderThread(mFunctorsRunnable);
-        }
-    };
-
-    class FunctorsRunnable implements Runnable {
-        View.AttachInfo attachInfo;
-
-        @Override
-        public void run() {
-            final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
-            if (renderer == null || !renderer.isEnabled() || renderer != mOwningRenderer) {
-                return;
-            }
-
-            if (checkRenderContext() != SURFACE_STATE_ERROR) {
-                int status = mCanvas.invokeFunctors(mRedrawClip);
-                handleFunctorStatus(attachInfo, status);
-            }
-        }
-    }
-
-    /**
-     * @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.
-     */
-    void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo,
-            HardwareDrawCallbacks callbacks, Rect dirty) {
-        if (canDraw()) {
-            if (!hasDirtyRegions()) {
-                dirty = null;
-            }
-
-            final int surfaceState = checkRenderContext();
-            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 = GLRenderer.getSystemTime();
-                try {
-                    status = prepareFrame(dirty);
-
-                    saveCount = canvas.save();
-                    callbacks.onHardwarePreDraw(canvas);
-
-                    status |= doDrawDisplayList(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 = GLRenderer.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) {
-        throw new IllegalAccessError();
-    }
-
-    private void debugOverdraw(View.AttachInfo attachInfo, Rect dirty,
-            HardwareCanvas canvas, DisplayList displayList) {
-
-        if (mDebugOverdraw == OVERDRAW_TYPE_COUNT) {
-            if (mDebugOverdrawLayer == null) {
-                mDebugOverdrawLayer = createHardwareLayer(mWidth, mHeight, true);
-            } else if (mDebugOverdrawLayer.getWidth() != mWidth ||
-                    mDebugOverdrawLayer.getHeight() != mHeight) {
-                mDebugOverdrawLayer.resize(mWidth, mHeight);
-            }
-
-            if (!mDebugOverdrawLayer.isValid()) {
-                mDebugOverdraw = -1;
-                return;
-            }
-
-            HardwareCanvas layerCanvas = mDebugOverdrawLayer.start(canvas, dirty);
-            countOverdraw(layerCanvas);
-            final int restoreCount = layerCanvas.save();
-            layerCanvas.drawDisplayList(displayList, null, DisplayList.FLAG_CLIP_CHILDREN);
-            layerCanvas.restoreToCount(restoreCount);
-            mDebugOverdrawLayer.end(canvas);
-
-            float overdraw = getOverdraw(layerCanvas);
-            DisplayMetrics metrics = attachInfo.mRootView.getResources().getDisplayMetrics();
-
-            drawOverdrawCounter(canvas, overdraw, metrics.density);
-        }
-    }
-
-    private void drawOverdrawCounter(HardwareCanvas canvas, float overdraw, float density) {
-        final String text = String.format("%.2fx", overdraw);
-        final Paint paint = setupPaint(density);
-        // HSBtoColor will clamp the values in the 0..1 range
-        paint.setColor(Color.HSBtoColor(0.28f - 0.28f * overdraw / 3.5f, 0.8f, 1.0f));
-
-        canvas.drawText(text, density * 4.0f, mHeight - paint.getFontMetrics().bottom, paint);
-    }
-
-    private Paint setupPaint(float density) {
-        if (mDebugOverdrawPaint == null) {
-            mDebugOverdrawPaint = new Paint();
-            mDebugOverdrawPaint.setAntiAlias(true);
-            mDebugOverdrawPaint.setShadowLayer(density * 3.0f, 0.0f, 0.0f, 0xff000000);
-            mDebugOverdrawPaint.setTextSize(density * 20.0f);
-        }
-        return mDebugOverdrawPaint;
-    }
-
-    private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
-        // We had to change the current surface and/or context, redraw everything
-        if (surfaceState == SURFACE_STATE_UPDATED) {
-            dirty = null;
-            GLRenderer.beginFrame(null);
-        } else {
-            int[] size = mSurfaceSize;
-            GLRenderer.beginFrame(size);
-
-            if (size[1] != mHeight || size[0] != mWidth) {
-                mWidth = size[0];
-                mHeight = size[1];
-
-                canvas.setViewport(mWidth, mHeight);
-
-                dirty = null;
-            }
-        }
-
-        if (mDebugDataProvider != null) dirty = null;
-
-        return dirty;
-    }
-
-    private int prepareFrame(Rect dirty) {
-        int status;
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
-        try {
-            status = onPreDraw(dirty);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-        return status;
-    }
-
-    private int doDrawDisplayList(View.AttachInfo attachInfo, HardwareCanvas canvas,
-            DisplayList displayList, int status) {
-
-        long drawDisplayListStartTime = 0;
-        if (mProfileEnabled) {
-            drawDisplayListStartTime = System.nanoTime();
-        }
-
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
-        try {
-            status |= canvas.drawDisplayList(displayList, mRedrawClip,
-                    DisplayList.FLAG_CLIP_CHILDREN);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-
-        if (mProfileEnabled) {
-            long now = System.nanoTime();
-            float total = (now - drawDisplayListStartTime) * 0.000001f;
-            mProfileData[mProfileCurrentFrame + 1] = total;
-        }
-
-        handleFunctorStatus(attachInfo, status);
-        return status;
-    }
-
-    private void swapBuffers(int status) {
-        if ((status & DisplayList.STATUS_DREW) == DisplayList.STATUS_DREW) {
-            long eglSwapBuffersStartTime = 0;
-            if (mProfileEnabled) {
-                eglSwapBuffersStartTime = System.nanoTime();
-            }
-
-            if (!swapBuffers(mNativeCanvasContext)) {
-                triggerSoftwareFallback();
-            }
-            mSurfaceUpdated = false;
-
-            if (mProfileEnabled) {
-                long now = System.nanoTime();
-                float total = (now - eglSwapBuffersStartTime) * 0.000001f;
-                mProfileData[mProfileCurrentFrame + 2] = total;
-            }
-        }
-    }
-
-    private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
-        if (mDebugDirtyRegions) {
-            if (mDebugPaint == null) {
-                mDebugPaint = new Paint();
-                mDebugPaint.setColor(0x7fff0000);
-            }
-
-            if (dirty != null && (mFrameCount & 1) == 0) {
-                canvas.drawRect(dirty, mDebugPaint);
-            }
-        }
-    }
-
-    private void handleFunctorStatus(final View.AttachInfo attachInfo, int status) {
-        // If the draw flag is set, functors will be invoked while executing
-        // the tree of display lists
-        if ((status & DisplayList.STATUS_DRAW) != 0) {
-            // TODO: Can we just re-queue ourselves up to draw next frame instead
-            // of bouncing back to the UI thread?
-            // TODO: Respect mRedrawClip - for now just full inval
-            attachInfo.mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    attachInfo.mViewRootImpl.invalidate();
-                }
-            });
-            mRedrawClip.setEmpty();
-        }
-
-        if ((status & DisplayList.STATUS_INVOKE) != 0 ||
-                attachInfo.mHandler.hasCallbacks(mDispatchFunctorsRunnable)) {
-            attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
-            mFunctorsRunnable.attachInfo = attachInfo;
-            attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable, FUNCTOR_PROCESS_DELAY);
-        }
-    }
-
-    @Override
-    void detachFunctor(int functor) {
-        if (mCanvas != null) {
-            mCanvas.detachFunctor(functor);
-        }
-    }
-
-    @Override
-    boolean attachFunctor(View.AttachInfo attachInfo, int functor) {
-        if (mCanvas != null) {
-            mCanvas.attachFunctor(functor);
-            mFunctorsRunnable.attachInfo = attachInfo;
-            attachInfo.mHandler.removeCallbacks(mDispatchFunctorsRunnable);
-            attachInfo.mHandler.postDelayed(mDispatchFunctorsRunnable,  0);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Ensures the current EGL context and surface are the ones we expect.
-     * This method throws an IllegalStateException if invoked from a thread
-     * that did not initialize EGL.
-     *
-     * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
-     *         {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
-     *         {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
-     *
-     * @see #checkRenderContextUnsafe()
-     */
-    int checkRenderContext() {
-        if (!makeCurrent(mNativeCanvasContext)) {
-            triggerSoftwareFallback();
-            return SURFACE_STATE_ERROR;
-        }
-        return mSurfaceUpdated ? SURFACE_STATE_UPDATED : SURFACE_STATE_SUCCESS;
-    }
-
-    private static int dpToPx(int dp, float density) {
-        return (int) (dp * density + 0.5f);
-    }
-
-    class DrawPerformanceDataProvider extends GraphDataProvider {
-        private final int mGraphType;
-
-        private int mVerticalUnit;
-        private int mHorizontalUnit;
-        private int mHorizontalMargin;
-        private int mThresholdStroke;
-
-        DrawPerformanceDataProvider(int graphType) {
-            mGraphType = graphType;
-        }
-
-        @Override
-        void prepare(DisplayMetrics metrics) {
-            final float density = metrics.density;
-
-            mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
-            mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
-            mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
-            mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
-        }
-
-        @Override
-        int getGraphType() {
-            return mGraphType;
-        }
-
-        @Override
-        int getVerticalUnitSize() {
-            return mVerticalUnit;
-        }
-
-        @Override
-        int getHorizontalUnitSize() {
-            return mHorizontalUnit;
-        }
-
-        @Override
-        int getHorizontaUnitMargin() {
-            return mHorizontalMargin;
-        }
-
-        @Override
-        float[] getData() {
-            return mProfileData;
-        }
-
-        @Override
-        float getThreshold() {
-            return 16;
-        }
-
-        @Override
-        int getFrameCount() {
-            return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        int getElementCount() {
-            return PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        int getCurrentFrame() {
-            return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
-        }
-
-        @Override
-        void setupGraphPaint(Paint paint, int elementIndex) {
-            paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
-            if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
-        }
-
-        @Override
-        void setupThresholdPaint(Paint paint) {
-            paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
-            paint.setStrokeWidth(mThresholdStroke);
-        }
-
-        @Override
-        void setupCurrentFramePaint(Paint paint) {
-            paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
-            if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
-        }
-    }
-
-    static native long createContext();
-    static native boolean usePBufferSurface();
-    static native boolean setSurface(long nativeCanvasContext, Surface surface);
-    static native boolean swapBuffers(long nativeCanvasContext);
-    static native boolean makeCurrent(long nativeCanvasContext);
-    static native void destroyContext(long nativeCanvasContext);
-}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2cd1b6e..567f862 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -18,25 +18,26 @@
 
 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.
+ * are currently synchronous.
+ * TODO: Make draw() async.
+ * TODO: Figure out how to share the DisplayList between two threads (global lock?)
  *
  * The UI thread can block on the RenderThread, but RenderThread must never
  * block on the UI thread.
  *
+ * ThreadedRenderer creates an instance of RenderProxy. RenderProxy in turn creates
+ * and manages a CanvasContext on the RenderThread. The CanvasContext is fully managed
+ * by the lifecycle of the RenderProxy.
+ *
  * 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
@@ -48,49 +49,29 @@
 public class ThreadedRenderer extends HardwareRenderer {
     private static final String LOGTAG = "ThreadedRenderer";
 
-    @SuppressWarnings("serial")
-    static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{
-        Method[] methods = RemoteGLRenderer.class.getDeclaredMethods();
-        for (Method m : methods) {
-            m.setAccessible(true);
-            put(m.getName() + ":" + m.getParameterTypes().length, m);
-        }
-    }};
-    static boolean sNeedsInit = true;
+    private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1);
 
-    private RemoteGLRenderer mRemoteRenderer;
     private int mWidth, mHeight;
-    private RTJob mPreviousDraw;
+    private long mNativeProxy;
 
     ThreadedRenderer(boolean translucent) {
-        mRemoteRenderer = new RemoteGLRenderer(this, translucent);
-        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();
-                }
-            });
-        }
+        mNativeProxy = nCreateProxy(translucent);
+        setEnabled(mNativeProxy != 0);
     }
 
     @Override
     void destroy(boolean full) {
-        run("destroy", full);
+        nDestroyCanvas(mNativeProxy);
     }
 
     @Override
     boolean initialize(Surface surface) throws OutOfResourcesException {
-        return (Boolean) run("initialize", surface);
+        return nInitialize(mNativeProxy, surface);
     }
 
     @Override
     void updateSurface(Surface surface) throws OutOfResourcesException {
-        post("updateSurface", surface);
+        nUpdateSurface(mNativeProxy, surface);
     }
 
     @Override
@@ -100,12 +81,27 @@
 
     @Override
     void destroyHardwareResources(View view) {
-        run("destroyHardwareResources", view);
+        // TODO: canvas.clearLayerUpdates()
+        destroyResources(view);
+        // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+    }
+
+    private static void destroyResources(View view) {
+        view.destroyHardwareResources();
+
+        if (view instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) view;
+
+            int count = group.getChildCount();
+            for (int i = 0; i < count; i++) {
+                destroyResources(group.getChildAt(i));
+            }
+        }
     }
 
     @Override
     void invalidate(Surface surface) {
-        post("invalidate", surface);
+        updateSurface(surface);
     }
 
     @Override
@@ -116,14 +112,15 @@
 
     @Override
     boolean safelyRun(Runnable action) {
-        return (Boolean) run("safelyRun", action);
+        // TODO:
+        return false;
     }
 
     @Override
     void setup(int width, int height) {
         mWidth = width;
         mHeight = height;
-        post("setup", width, height);
+        nSetup(mNativeProxy, width, height);
     }
 
     @Override
@@ -149,7 +146,7 @@
 
     @Override
     boolean loadSystemProperties() {
-        return (Boolean) run("loadSystemProperties");
+        return false;
     }
 
     @Override
@@ -174,20 +171,10 @@
      *
      *  @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;
@@ -202,8 +189,11 @@
 
         view.mRecreateDisplayList = false;
 
-        mPreviousDraw = post("drawDisplayList", displayList, attachInfo,
-                callbacks, dirty);
+        if (dirty == null) {
+            dirty = NULL_RECT;
+        }
+        nDrawDisplayList(mNativeProxy, displayList.getNativeDisplayList(),
+                dirty.left, dirty.top, dirty.right, dirty.bottom);
     }
 
     @Override
@@ -228,85 +218,40 @@
 
     @Override
     void detachFunctor(int functor) {
-        run("detachFunctor", functor);
+        nDetachFunctor(mNativeProxy, functor);
     }
 
     @Override
-    boolean attachFunctor(AttachInfo attachInfo, int functor) {
-        return (Boolean) run("attachFunctor", attachInfo, functor);
+    void attachFunctor(AttachInfo attachInfo, int functor) {
+        nAttachFunctor(mNativeProxy, functor);
     }
 
     @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 + ":" + args.length);
-        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 + ":" + args.length);
-        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);
-            }
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nDeleteProxy(mNativeProxy);
+        } finally {
+            super.finalize();
         }
     }
 
     /** @hide */
     public static native void postToRenderThread(Runnable runnable);
+
+    private static native long nCreateProxy(boolean translucent);
+    private static native void nDeleteProxy(long nativeProxy);
+
+    private static native boolean nInitialize(long nativeProxy, Surface window);
+    private static native void nUpdateSurface(long nativeProxy, Surface window);
+    private static native void nSetup(long nativeProxy, int width, int height);
+    private static native void nDrawDisplayList(long nativeProxy, long displayList,
+            int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+    private static native void nDestroyCanvas(long nativeProxy);
+
+    private static native void nAttachFunctor(long nativeProxy, long functor);
+    private static native void nDetachFunctor(long nativeProxy, long functor);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 41b0c67..5aa46f3 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -660,12 +660,11 @@
         mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
     }
 
-    public boolean attachFunctor(int functor) {
+    public void attachFunctor(int functor) {
         //noinspection SimplifiableIfStatement
         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
-            return mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
+            mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
         }
-        return false;
     }
 
     public void detachFunctor(int functor) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index d72b054..f8d96e3 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -58,7 +58,6 @@
 	android_view_GLRenderer.cpp \
 	android_view_GLES20Canvas.cpp \
 	android_view_ThreadedRenderer.cpp \
-	android_view_RemoteGLRenderer.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 074d7ac..7ed6641 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -124,7 +124,6 @@
 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_RemoteGLRenderer(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);
@@ -1129,7 +1128,6 @@
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_GLRenderer),
     REG_JNI(register_android_view_ThreadedRenderer),
-    REG_JNI(register_android_view_RemoteGLRenderer),
     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_RemoteGLRenderer.cpp b/core/jni/android_view_RemoteGLRenderer.cpp
deleted file mode 100644
index 96a203b..0000000
--- a/core/jni/android_view_RemoteGLRenderer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2014 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 "RemoteGLRenderer"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-
-#include <utils/StrongPointer.h>
-#include <android_runtime/android_view_Surface.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <renderthread/CanvasContext.h>
-#include <system/window.h>
-
-namespace android {
-
-#ifdef USE_OPENGL_RENDERER
-
-#define CHECK_CONTEXT(c) if (!c) ALOGE("Null context passed to %s!", __func__ )
-
-namespace RT = android::uirenderer::renderthread;
-
-static jlong android_view_RemoteGLRenderer_createContext(JNIEnv* env, jobject clazz) {
-    RT::CanvasContext* context = new RT::CanvasContext();
-    return reinterpret_cast<jlong>(context);
-}
-
-static jboolean android_view_RemoteGLRenderer_usePBufferSurface(JNIEnv* env, jobject clazz) {
-    return RT::CanvasContext::useGlobalPBufferSurface();
-}
-
-static jboolean android_view_RemoteGLRenderer_setSurface(JNIEnv* env, jobject clazz,
-        jlong jcontextptr, jobject jsurface) {
-    RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
-    CHECK_CONTEXT(context);
-    sp<ANativeWindow> window;
-    if (jsurface) {
-        window = android_view_Surface_getNativeWindow(env, jsurface);
-    }
-    return context->setSurface(window.get());
-}
-
-static jboolean android_view_RemoteGLRenderer_swapBuffers(JNIEnv* env, jobject clazz,
-        jlong jcontextptr) {
-    RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
-    CHECK_CONTEXT(context);
-    return context->swapBuffers();
-}
-
-static jboolean android_view_RemoteGLRenderer_makeCurrent(JNIEnv* env, jobject clazz,
-        jlong jcontextptr) {
-    RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
-    CHECK_CONTEXT(context);
-    return context->makeCurrent();
-}
-
-static void android_view_RemoteGLRenderer_destroyContext(JNIEnv* env, jobject clazz,
-        jlong jcontextptr) {
-    RT::CanvasContext* context = reinterpret_cast<RT::CanvasContext*>(jcontextptr);
-    CHECK_CONTEXT(context);
-    delete context;
-}
-#endif
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/RemoteGLRenderer";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
-    { "createContext", "()J",   (void*) android_view_RemoteGLRenderer_createContext },
-    { "usePBufferSurface", "()Z",   (void*) android_view_RemoteGLRenderer_usePBufferSurface },
-    { "setSurface", "(JLandroid/view/Surface;)Z",   (void*) android_view_RemoteGLRenderer_setSurface },
-    { "swapBuffers", "(J)Z",   (void*) android_view_RemoteGLRenderer_swapBuffers },
-    { "makeCurrent", "(J)Z",   (void*) android_view_RemoteGLRenderer_makeCurrent },
-    { "destroyContext", "(J)V",   (void*) android_view_RemoteGLRenderer_destroyContext },
-#endif
-};
-
-int register_android_view_RemoteGLRenderer(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8d77e36..8e121de 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -20,6 +20,11 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 
+#include <utils/StrongPointer.h>
+#include <android_runtime/android_view_Surface.h>
+#include <system/window.h>
+
+#include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
 
@@ -27,23 +32,22 @@
 
 #ifdef USE_OPENGL_RENDERER
 
-namespace RT = android::uirenderer::renderthread;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
 
 static jmethodID gRunnableMethod;
 
-class JavaTask : public RT::RenderTask {
+class JavaTask : public 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);
+        env()->DeleteGlobalRef(mRunnable);
+        delete this;
     };
 
 private:
@@ -61,8 +65,70 @@
 
 static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
         jobject jrunnable) {
-    RT::RenderTask* task = new JavaTask(env, jrunnable);
-    RT::RenderThread::getInstance().queue(task);
+    RenderTask* task = new JavaTask(env, jrunnable);
+    RenderThread::getInstance().queue(task);
+}
+
+static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
+        jboolean translucent) {
+    return (jlong) new RenderProxy(translucent);
+}
+
+static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    delete proxy;
+}
+
+static jboolean android_view_ThreadedRenderer_initialize(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject jsurface) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    sp<ANativeWindow> window = android_view_Surface_getNativeWindow(env, jsurface);
+    return proxy->initialize(window.get());
+}
+
+static void android_view_ThreadedRenderer_updateSurface(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jobject jsurface) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    sp<ANativeWindow> window;
+    if (jsurface) {
+        window = android_view_Surface_getNativeWindow(env, jsurface);
+    }
+    proxy->updateSurface(window.get());
+}
+
+static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint width, jint height) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    proxy->setup(width, height);
+}
+
+static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop,
+        jint dirtyRight, jint dirtyBottom) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    DisplayList* displayList = reinterpret_cast<DisplayList*>( displayListPtr);
+    proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+}
+
+static void android_view_ThreadedRenderer_destroyCanvas(JNIEnv* env, jobject clazz,
+        jlong proxyPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    proxy->destroyCanvas();
+}
+
+static void android_view_ThreadedRenderer_attachFunctor(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong functorPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+    proxy->attachFunctor(functor);
+}
+
+static void android_view_ThreadedRenderer_detachFunctor(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jlong functorPtr) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>( proxyPtr);
+    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
+    proxy->detachFunctor(functor);
 }
 
 #endif
@@ -76,6 +142,15 @@
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
     { "postToRenderThread", "(Ljava/lang/Runnable;)V",   (void*) android_view_ThreadedRenderer_postToRenderThread },
+    { "nCreateProxy", "(Z)J", (void*) android_view_ThreadedRenderer_createProxy },
+    { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
+    { "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
+    { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
+    { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
+    { "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList},
+    { "nDestroyCanvas", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvas},
+    { "nAttachFunctor", "(JJ)V", (void*) android_view_ThreadedRenderer_attachFunctor},
+    { "nDetachFunctor", "(JJ)V", (void*) android_view_ThreadedRenderer_detachFunctor},
 #endif
 };
 
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 88f11db..e4648f6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -52,6 +52,7 @@
 	# RenderThread stuff
 	LOCAL_SRC_FILES += \
 		renderthread/CanvasContext.cpp \
+		renderthread/RenderProxy.cpp \
 		renderthread/RenderTask.cpp \
 		renderthread/RenderThread.cpp
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fb780ce..6371561 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -170,7 +170,7 @@
     int saveLayerDeferred(float left, float top, float right, float bottom,
             int alpha, SkXfermode::Mode mode, int flags);
 
-    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags);
+    virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags = 1);
     virtual status_t drawLayer(Layer* layer, float x, float y);
     virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
             const SkPaint* paint);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 9d4e83e..4799b32 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -247,8 +247,6 @@
 
     // TODO: rename for consistency
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0;
-
-private:
 }; // class Renderer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ffb8a32..a848c8f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -19,14 +19,22 @@
 #include "CanvasContext.h"
 
 #include <cutils/properties.h>
+#include <private/hwui/DrawGlInfo.h>
 #include <strings.h>
 
+#include "RenderThread.h"
 #include "../Caches.h"
+#include "../OpenGLRenderer.h"
 #include "../Stencil.h"
 
 #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
 #define GLES_VERSION 2
 
+#ifdef USE_OPENGL_RENDERER
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+#endif
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
@@ -69,19 +77,19 @@
 public:
     static GlobalContext* get();
 
-    // Returns true if EGL was initialized,
-    // false if it was already initialized
-    bool initialize();
+    // Returns true on success, false on failure
+    void initialize();
 
-    bool usePBufferSurface();
+    void usePBufferSurface();
     EGLSurface createSurface(EGLNativeWindowType window);
     void destroySurface(EGLSurface surface);
 
     void destroy();
 
     bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
-    bool makeCurrent(EGLSurface surface);
-    bool swapBuffers(EGLSurface surface);
+    void makeCurrent(EGLSurface surface);
+    void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+    void swapBuffers(EGLSurface surface);
 
     bool enableDirtyRegions(EGLSurface surface);
 
@@ -90,8 +98,9 @@
     // GlobalContext is never destroyed, method is purposely not implemented
     ~GlobalContext();
 
-    bool loadConfig();
-    bool createContext();
+    void loadConfig();
+    void createContext();
+    void initAtlas();
 
     static GlobalContext* sContext;
 
@@ -126,33 +135,27 @@
     ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
 }
 
-bool GlobalContext::initialize() {
-    if (mEglDisplay != EGL_NO_DISPLAY) return false;
+void GlobalContext::initialize() {
+    if (mEglDisplay != EGL_NO_DISPLAY) return;
 
     mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (mEglDisplay == EGL_NO_DISPLAY) {
-        ALOGE("Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
-        return false;
-    }
+    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
 
     EGLint major, minor;
-    if (eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE) {
-        ALOGE("Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
-        return false;
-    }
+    LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+            "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
     ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
 
-    if (!loadConfig()) {
-        return false;
-    }
-    if (!createContext()) {
-        return false;
-    }
-
-    return true;
+    loadConfig();
+    createContext();
+    usePBufferSurface();
+    Caches::getInstance().init();
+    initAtlas();
 }
 
-bool GlobalContext::loadConfig() {
+void GlobalContext::loadConfig() {
     EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
     EGLint attribs[] = {
             EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
@@ -177,31 +180,32 @@
             mCanSetDirtyRegions = false;
             loadConfig();
         } else {
-            ALOGE("Failed to choose config, error = %s", egl_error_str());
-            return false;
+            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
         }
     }
-    return true;
 }
 
-bool GlobalContext::createContext() {
+void GlobalContext::createContext() {
     EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
     mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
-    if (mEglContext == EGL_NO_CONTEXT) {
-        ALOGE("Failed to create context, error = %s", egl_error_str());
-        return false;
-    }
-    return true;
+    LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+        "Failed to create context, error = %s", egl_error_str());
 }
 
-bool GlobalContext::usePBufferSurface() {
-    if (mEglDisplay == EGL_NO_DISPLAY) return false;
+void GlobalContext::initAtlas() {
+    // TODO implement
+    // For now just run without an atlas
+}
+
+void GlobalContext::usePBufferSurface() {
+    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+            "usePBufferSurface() called on uninitialized GlobalContext!");
 
     if (mPBufferSurface == EGL_NO_SURFACE) {
         EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
         mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
     }
-    return makeCurrent(mPBufferSurface);
+    makeCurrent(mPBufferSurface);
 }
 
 EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
@@ -238,8 +242,8 @@
     mCurrentSurface = EGL_NO_SURFACE;
 }
 
-bool GlobalContext::makeCurrent(EGLSurface surface) {
-    if (isCurrent(surface)) return true;
+void GlobalContext::makeCurrent(EGLSurface surface) {
+    if (isCurrent(surface)) return;
 
     if (surface == EGL_NO_SURFACE) {
         // If we are setting EGL_NO_SURFACE we don't care about any of the potential
@@ -247,19 +251,31 @@
         // destroyed in which case the current context is already NO_CONTEXT
         eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
-        ALOGE("Failed to make current on surface %p, error=%s", (void*)surface, egl_error_str());
-        return false;
+        LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+                (void*)surface, egl_error_str());
     }
     mCurrentSurface = surface;
-    return true;
 }
 
-bool GlobalContext::swapBuffers(EGLSurface surface) {
-    if (!eglSwapBuffers(mEglDisplay, surface)) {
-        ALOGW("eglSwapBuffers failed on surface %p, error=%s", (void*)surface, egl_error_str());
-        return false;
+void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+            "Tried to beginFrame on EGL_NO_SURFACE!");
+    makeCurrent(surface);
+    if (width) {
+        eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
     }
-    return true;
+    if (height) {
+        eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+    }
+    eglBeginFrame(mEglDisplay, surface);
+}
+
+void GlobalContext::swapBuffers(EGLSurface surface) {
+    eglSwapBuffers(mEglDisplay, surface);
+    EGLint err = eglGetError();
+    // TODO: Check whether we need to special case EGL_CONTEXT_LOST
+    LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+            "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
 }
 
 bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
@@ -283,17 +299,32 @@
     return value == EGL_BUFFER_PRESERVED;
 }
 
-CanvasContext::CanvasContext()
-        : mEglSurface(EGL_NO_SURFACE)
-        , mDirtyRegionsEnabled(false) {
+CanvasContext::CanvasContext(bool translucent)
+        : mRenderThread(RenderThread::getInstance())
+        , mEglSurface(EGL_NO_SURFACE)
+        , mDirtyRegionsEnabled(false)
+        , mOpaque(!translucent)
+        , mCanvas(0)
+        , mHaveNewSurface(false)
+        , mInvokeFunctorsPending(false)
+        , mInvokeFunctorsTask(this) {
     mGlobalContext = GlobalContext::get();
 }
 
 CanvasContext::~CanvasContext() {
+    removeFunctorsTask();
+    destroyCanvas();
+}
+
+void CanvasContext::destroyCanvas() {
+    if (mCanvas) {
+        delete mCanvas;
+        mCanvas = 0;
+    }
     setSurface(NULL);
 }
 
-bool CanvasContext::setSurface(EGLNativeWindowType window) {
+void CanvasContext::setSurface(EGLNativeWindowType window) {
     if (mEglSurface != EGL_NO_SURFACE) {
         mGlobalContext->destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
@@ -301,24 +332,134 @@
 
     if (window) {
         mEglSurface = mGlobalContext->createSurface(window);
+        LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+                "Failed to create EGLSurface for window %p, eglErr = %s",
+                (void*) window, egl_error_str());
     }
 
     if (mEglSurface != EGL_NO_SURFACE) {
         mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+        mHaveNewSurface = true;
     }
-    return !window || mEglSurface != EGL_NO_SURFACE;
 }
 
-bool CanvasContext::swapBuffers() {
-    return mGlobalContext->swapBuffers(mEglSurface);
+void CanvasContext::swapBuffers() {
+    mGlobalContext->swapBuffers(mEglSurface);
+    mHaveNewSurface = false;
 }
 
-bool CanvasContext::makeCurrent() {
-    return mGlobalContext->makeCurrent(mEglSurface);
+void CanvasContext::makeCurrent() {
+    mGlobalContext->makeCurrent(mEglSurface);
 }
 
-bool CanvasContext::useGlobalPBufferSurface() {
-    return GlobalContext::get()->usePBufferSurface();
+bool CanvasContext::initialize(EGLNativeWindowType window) {
+    if (mCanvas) return false;
+    setSurface(window);
+    makeCurrent();
+    mCanvas = new OpenGLRenderer();
+    mCanvas->initProperties();
+    return true;
+}
+
+void CanvasContext::updateSurface(EGLNativeWindowType window) {
+    setSurface(window);
+    makeCurrent();
+}
+
+void CanvasContext::setup(int width, int height) {
+    if (!mCanvas) return;
+    mCanvas->setViewport(width, height);
+}
+
+void CanvasContext::drawDisplayList(DisplayList* displayList, Rect* dirty) {
+    LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
+            "drawDisplayList called on a context with no canvas or surface!");
+
+    EGLint width, height;
+    mGlobalContext->beginFrame(mEglSurface, &width, &height);
+    if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
+        mCanvas->setViewport(width, height);
+        dirty = NULL;
+    } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
+        dirty = NULL;
+    }
+
+    status_t status;
+    if (dirty) {
+        status = mCanvas->prepareDirty(dirty->left, dirty->top,
+                dirty->right, dirty->bottom, mOpaque);
+    } else {
+        status = mCanvas->prepare(mOpaque);
+    }
+
+    Rect outBounds;
+    status |= mCanvas->drawDisplayList(displayList, outBounds);
+    handleFunctorStatus(status, outBounds);
+
+    // TODO: Draw debug info
+    // TODO: Performance tracking
+
+    mCanvas->finish();
+
+    if (status & DrawGlInfo::kStatusDrew) {
+        swapBuffers();
+    }
+}
+
+void InvokeFunctorsTask::run() {
+    mContext->invokeFunctors();
+}
+
+void CanvasContext::attachFunctor(Functor* functor) {
+    if (!mCanvas) return;
+
+    mCanvas->attachFunctor(functor);
+    removeFunctorsTask();
+    queueFunctorsTask(0);
+}
+
+void CanvasContext::detachFunctor(Functor* functor) {
+    if (!mCanvas) return;
+
+    mCanvas->detachFunctor(functor);
+}
+
+void CanvasContext::invokeFunctors() {
+    mInvokeFunctorsPending = false;
+
+    if (!mCanvas) return;
+
+    makeCurrent();
+    Rect dirty;
+    int status = mCanvas->invokeFunctors(dirty);
+    handleFunctorStatus(status, dirty);
+}
+
+void CanvasContext::handleFunctorStatus(int status, const Rect& redrawClip) {
+    if (status & DrawGlInfo::kStatusDraw) {
+        // TODO: Invalidate the redrawClip
+        // Do we need to post to ViewRootImpl like the current renderer?
+        // Can we just enqueue ourselves to re-invoke the same display list?
+        // Something else entirely? Does ChromiumView still want this in a
+        // RenderThread world?
+    }
+
+    if (status & DrawGlInfo::kStatusInvoke) {
+        queueFunctorsTask();
+    }
+}
+
+void CanvasContext::removeFunctorsTask() {
+    if (!mInvokeFunctorsPending) return;
+
+    mRenderThread.remove(&mInvokeFunctorsTask);
+}
+
+void CanvasContext::queueFunctorsTask(int delayMs) {
+    if (mInvokeFunctorsPending) return;
+
+    mInvokeFunctorsPending = true;
+    mRenderThread.queueDelayed(&mInvokeFunctorsTask, delayMs);
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 77ae737..2daa905 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -19,31 +19,75 @@
 
 #include <cutils/compiler.h>
 #include <EGL/egl.h>
+#include <utils/Functor.h>
+
+#include "RenderTask.h"
+
+#define FUNCTOR_PROCESS_DELAY 4
 
 namespace android {
 namespace uirenderer {
+
+class DisplayList;
+class OpenGLRenderer;
+class Rect;
+
 namespace renderthread {
 
 class GlobalContext;
+class CanvasContext;
+class RenderThread;
+
+class InvokeFunctorsTask : public RenderTask {
+public:
+    InvokeFunctorsTask(CanvasContext* context)
+        : mContext(context) {}
+
+    virtual void run();
+
+private:
+    CanvasContext* mContext;
+};
 
 // This per-renderer class manages the bridge between the global EGL context
 // and the render surface.
 class CanvasContext {
 public:
-    ANDROID_API CanvasContext();
-    ANDROID_API ~CanvasContext();
+    CanvasContext(bool translucent);
+    ~CanvasContext();
 
-    ANDROID_API bool setSurface(EGLNativeWindowType window);
-    ANDROID_API bool swapBuffers();
-    ANDROID_API bool makeCurrent();
+    bool initialize(EGLNativeWindowType window);
+    void updateSurface(EGLNativeWindowType window);
+    void setup(int width, int height);
+    void drawDisplayList(DisplayList* displayList, Rect* dirty);
+    void destroyCanvas();
 
-    ANDROID_API static bool useGlobalPBufferSurface();
+    void attachFunctor(Functor* functor);
+    void detachFunctor(Functor* functor);
 
 private:
+    void setSurface(EGLNativeWindowType window);
+    void swapBuffers();
+    void makeCurrent();
+
+    friend class InvokeFunctorsTask;
+    void invokeFunctors();
+    void handleFunctorStatus(int status, const Rect& redrawClip);
+    void removeFunctorsTask();
+    void queueFunctorsTask(int delayMs = FUNCTOR_PROCESS_DELAY);
 
     GlobalContext* mGlobalContext;
+    RenderThread& mRenderThread;
     EGLSurface mEglSurface;
     bool mDirtyRegionsEnabled;
+
+    bool mOpaque;
+    OpenGLRenderer* mCanvas;
+    bool mHaveNewSurface;
+
+    bool mInvokeFunctorsPending;
+    InvokeFunctorsTask mInvokeFunctorsTask;
+
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
new file mode 100644
index 0000000..25badac
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RenderProxy"
+
+#include "RenderProxy.h"
+
+#include "CanvasContext.h"
+#include "RenderTask.h"
+#include "RenderThread.h"
+
+#include "../DisplayList.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ARGS(method) method ## Args
+
+#define CREATE_BRIDGE1(name, a1) CREATE_BRIDGE(name, a1,,,,,,,)
+#define CREATE_BRIDGE2(name, a1, a2) CREATE_BRIDGE(name, a1,a2,,,,,,)
+#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
+#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
+#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
+    typedef struct { \
+        a1; a2; a3; a4; a5; a6; a7; a8; \
+    } ARGS(name); \
+    static void* Bridge_ ## name(ARGS(name)* args)
+
+#define SETUP_TASK(method) \
+    LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
+        "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \
+                METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
+    MethodInvokeRenderTask* task = createTask((RunnableMethod) Bridge_ ## method); \
+    ARGS(method) *args = (ARGS(method) *) task->payload()
+
+CREATE_BRIDGE1(createContext, bool translucent) {
+    return new CanvasContext(args->translucent);
+}
+
+RenderProxy::RenderProxy(bool translucent)
+        : mRenderThread(RenderThread::getInstance())
+        , mContext(0) {
+    SETUP_TASK(createContext);
+    args->translucent = translucent;
+    mContext = (CanvasContext*) postAndWait(task);
+}
+
+RenderProxy::~RenderProxy() {
+    destroyContext();
+}
+
+CREATE_BRIDGE1(destroyContext, CanvasContext* context) {
+    delete args->context;
+    return NULL;
+}
+
+void RenderProxy::destroyContext() {
+    if (mContext) {
+        SETUP_TASK(destroyContext);
+        args->context = mContext;
+        mContext = 0;
+        post(task);
+    }
+}
+
+CREATE_BRIDGE2(initialize, CanvasContext* context, EGLNativeWindowType window) {
+    return (void*) args->context->initialize(args->window);
+}
+
+bool RenderProxy::initialize(EGLNativeWindowType window) {
+    SETUP_TASK(initialize);
+    args->context = mContext;
+    args->window = window;
+    return (bool) postAndWait(task);
+}
+
+CREATE_BRIDGE2(updateSurface, CanvasContext* context, EGLNativeWindowType window) {
+    args->context->updateSurface(args->window);
+    return NULL;
+}
+
+void RenderProxy::updateSurface(EGLNativeWindowType window) {
+    SETUP_TASK(updateSurface);
+    args->context = mContext;
+    args->window = window;
+    post(task);
+}
+
+CREATE_BRIDGE3(setup, CanvasContext* context, int width, int height) {
+    args->context->setup(args->width, args->height);
+    return NULL;
+}
+
+void RenderProxy::setup(int width, int height) {
+    SETUP_TASK(setup);
+    args->context = mContext;
+    args->width = width;
+    args->height = height;
+    post(task);
+}
+
+CREATE_BRIDGE3(drawDisplayList, CanvasContext* context, DisplayList* displayList,
+        Rect dirty) {
+    Rect* dirty = &args->dirty;
+    if (dirty->bottom == -1 && dirty->left == -1 &&
+            dirty->top == -1 && dirty->right == -1) {
+        dirty = 0;
+    }
+    args->context->drawDisplayList(args->displayList, dirty);
+    return NULL;
+}
+
+void RenderProxy::drawDisplayList(DisplayList* displayList,
+        int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
+    SETUP_TASK(drawDisplayList);
+    args->context = mContext;
+    args->displayList = displayList;
+    args->dirty.set(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    // TODO: Switch to post() once some form of thread safety strategy is in place
+    postAndWait(task);
+}
+
+CREATE_BRIDGE1(destroyCanvas, CanvasContext* context) {
+    args->context->destroyCanvas();
+    return NULL;
+}
+
+void RenderProxy::destroyCanvas() {
+    SETUP_TASK(destroyCanvas);
+    args->context = mContext;
+    post(task);
+}
+
+CREATE_BRIDGE2(attachFunctor, CanvasContext* context, Functor* functor) {
+    args->context->attachFunctor(args->functor);
+    return NULL;
+}
+
+void RenderProxy::attachFunctor(Functor* functor) {
+    SETUP_TASK(attachFunctor);
+    args->context = mContext;
+    args->functor = functor;
+    post(task);
+}
+
+CREATE_BRIDGE2(detachFunctor, CanvasContext* context, Functor* functor) {
+    args->context->detachFunctor(args->functor);
+    return NULL;
+}
+
+void RenderProxy::detachFunctor(Functor* functor) {
+    SETUP_TASK(detachFunctor);
+    args->context = mContext;
+    args->functor = functor;
+    post(task);
+}
+
+MethodInvokeRenderTask* RenderProxy::createTask(RunnableMethod method) {
+    // TODO: Consider having a small pool of these to avoid alloc churn
+    return new MethodInvokeRenderTask(method);
+}
+
+void RenderProxy::post(RenderTask* task) {
+    mRenderThread.queue(task);
+}
+
+void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
+    void* retval;
+    task->setReturnPtr(&retval);
+    SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
+    AutoMutex _lock(mSyncMutex);
+    mRenderThread.queue(&syncTask);
+    mSyncCondition.wait(mSyncMutex);
+    return retval;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
new file mode 100644
index 0000000..113c5a8
--- /dev/null
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -0,0 +1,85 @@
+/*
+ * 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 RENDERPROXY_H_
+#define RENDERPROXY_H_
+
+#include "RenderTask.h"
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <utils/Condition.h>
+#include <utils/Functor.h>
+#include <utils/Mutex.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayList;
+class Rect;
+
+namespace renderthread {
+
+class CanvasContext;
+class ErrorChannel;
+class RenderThread;
+class RenderProxyBridge;
+
+/*
+ * RenderProxy is strictly single threaded. All methods must be invoked on the owning
+ * thread. It is important to note that RenderProxy may be deleted while it has
+ * tasks post()'d as a result. Therefore any RenderTask that is post()'d must not
+ * reference RenderProxy or any of its fields. The exception here is that postAndWait()
+ * references RenderProxy fields. This is safe as RenderProxy cannot
+ * be deleted if it is blocked inside a call.
+ */
+class ANDROID_API RenderProxy {
+public:
+    ANDROID_API RenderProxy(bool translucent);
+    ANDROID_API virtual ~RenderProxy();
+
+    ANDROID_API bool initialize(EGLNativeWindowType window);
+    ANDROID_API void updateSurface(EGLNativeWindowType window);
+    ANDROID_API void setup(int width, int height);
+    ANDROID_API void drawDisplayList(DisplayList* displayList,
+            int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+    ANDROID_API void destroyCanvas();
+
+    ANDROID_API void attachFunctor(Functor* functor);
+    ANDROID_API void detachFunctor(Functor* functor);
+
+private:
+    RenderThread& mRenderThread;
+    CanvasContext* mContext;
+
+    Mutex mSyncMutex;
+    Condition mSyncCondition;
+
+    void destroyContext();
+
+    MethodInvokeRenderTask* createTask(RunnableMethod method);
+    void post(RenderTask* task);
+    void* postAndWait(MethodInvokeRenderTask* task);
+
+    // Friend class to help with bridging
+    friend class RenderProxyBridge;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* RENDERPROXY_H_ */
diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp
index 2da91c5..7ca61e4 100644
--- a/libs/hwui/renderthread/RenderTask.cpp
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -19,15 +19,18 @@
 #include "RenderTask.h"
 
 #include <utils/Log.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
 
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
-RenderTask::RenderTask() : mNext(0) {
-}
-
-RenderTask::~RenderTask() {
+void SignalingRenderTask::run() {
+    mTask->run();
+    mLock->lock();
+    mSignal->signal();
+    mLock->unlock();
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
index 865b1e6..9fe7573 100644
--- a/libs/hwui/renderthread/RenderTask.h
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -18,19 +18,79 @@
 #define RENDERTASK_H_
 
 #include <cutils/compiler.h>
+#include <utils/Timers.h>
 
 namespace android {
+class Mutex;
+class Condition;
 namespace uirenderer {
 namespace renderthread {
 
+#define METHOD_INVOKE_PAYLOAD_SIZE (8 * sizeof(void*))
+
+/*
+ * Notes about memory management
+ *
+ * RenderThread will only invoke RenderTask::run(). It is the responsibility
+ * of the RenderTask to know if it needs to suicide at the end of run() or
+ * if some other lifecycle is being used. As such, it is not valid to reference
+ * anything on RenderTask after the first call to run().
+ *
+ * For example SignalingRenderTask
+ * is expected to be stack allocated by the calling thread, so it does not
+ * suicide in run() but instead relies on the caller to destroy it.
+ *
+ * MethodInvokeRenderTask however is currently allocated with new, so it will
+ * suicide at the end of run(). TODO: Replace this with a small pool to avoid
+ * malloc/free churn of small objects?
+ */
+
 class ANDROID_API RenderTask {
 public:
-    ANDROID_API RenderTask();
-    ANDROID_API virtual ~RenderTask();
+    ANDROID_API RenderTask() : mNext(0), mRunAt(0) {}
+    ANDROID_API virtual ~RenderTask() {}
 
     ANDROID_API virtual void run() = 0;
 
     RenderTask* mNext;
+    nsecs_t mRunAt;
+};
+
+class SignalingRenderTask : public RenderTask {
+public:
+    // Takes ownership of task, caller owns lock and signal
+    SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
+            : mTask(task), mLock(lock), mSignal(signal) {}
+    virtual void run();
+
+private:
+    RenderTask* mTask;
+    Mutex* mLock;
+    Condition* mSignal;
+};
+
+typedef void* (*RunnableMethod)(void* data);
+
+class MethodInvokeRenderTask : public RenderTask {
+public:
+    MethodInvokeRenderTask(RunnableMethod method)
+        : mMethod(method), mReturnPtr(0) {}
+
+    void* payload() { return mData; }
+    void setReturnPtr(void** retptr) { mReturnPtr = retptr; }
+
+    virtual void run() {
+        void* retval = mMethod(mData);
+        if (mReturnPtr) {
+            *mReturnPtr = retval;
+        }
+        // Commit suicide
+        delete this;
+    }
+private:
+    RunnableMethod mMethod;
+    char mData[METHOD_INVOKE_PAYLOAD_SIZE];
+    void** mReturnPtr;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index bccd6e6..e4ec164 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -18,6 +18,8 @@
 
 #include "RenderThread.h"
 
+#include "CanvasContext.h"
+#include "RenderProxy.h"
 #include <utils/Log.h>
 
 namespace android {
@@ -27,8 +29,82 @@
 namespace uirenderer {
 namespace renderthread {
 
+TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
+
+RenderTask* TaskQueue::next() {
+    RenderTask* ret = mHead;
+    if (ret) {
+        mHead = ret->mNext;
+        if (!mHead) {
+            mTail = 0;
+        }
+        ret->mNext = 0;
+    }
+    return ret;
+}
+
+RenderTask* TaskQueue::peek() {
+    return mHead;
+}
+
+void TaskQueue::queue(RenderTask* task) {
+    // Since the RenderTask itself forms the linked list it is not allowed
+    // to have the same task queued twice
+    LOG_ALWAYS_FATAL_IF(task->mNext || mTail == task, "Task is already in the queue!");
+    if (mTail) {
+        // Fast path if we can just append
+        if (mTail->mRunAt <= task->mRunAt) {
+            mTail->mNext = task;
+            mTail = task;
+        } else {
+            // Need to find the proper insertion point
+            RenderTask* previous = 0;
+            RenderTask* next = mHead;
+            while (next && next->mRunAt <= task->mRunAt) {
+                previous = next;
+                next = next->mNext;
+            }
+            if (!previous) {
+                task->mNext = mHead;
+                mHead = task;
+            } else {
+                previous->mNext = task;
+                if (next) {
+                    task->mNext = next;
+                } else {
+                    mTail = task;
+                }
+            }
+        }
+    } else {
+        mTail = mHead = task;
+    }
+}
+
+void TaskQueue::remove(RenderTask* task) {
+    // TaskQueue is strict here to enforce that users are keeping track of
+    // their RenderTasks due to how their memory is managed
+    LOG_ALWAYS_FATAL_IF(!task->mNext && mTail != task,
+            "Cannot remove a task that isn't in the queue!");
+
+    // If task is the head we can just call next() to pop it off
+    // Otherwise we need to scan through to find the task before it
+    if (peek() == task) {
+        next();
+    } else {
+        RenderTask* previous = mHead;
+        while (previous->mNext != task) {
+            previous = previous->mNext;
+        }
+        previous->mNext = task->mNext;
+        if (mTail == task) {
+            mTail = previous;
+        }
+    }
+}
+
 RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
-        , mQueueHead(0), mQueueTail(0) {
+        , mNextWakeup(LLONG_MAX) {
     mLooper = new Looper(false);
     run("RenderThread");
 }
@@ -37,16 +113,25 @@
 }
 
 bool RenderThread::threadLoop() {
+    int timeoutMillis = -1;
     for (;;) {
-        int result = mLooper->pollAll(-1);
-        if (result == Looper::POLL_ERROR) {
-            // TODO Something?
-            break;
-        }
+        int result = mLooper->pollAll(timeoutMillis);
+        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
+                "RenderThread Looper POLL_ERROR!");
+
+        nsecs_t nextWakeup;
         // Process our queue, if we have anything
-        while (RenderTask* task = nextTask()) {
+        while (RenderTask* task = nextTask(&nextWakeup)) {
             task->run();
-            delete task;
+            // task may have deleted itself, do not reference it again
+        }
+        if (nextWakeup == LLONG_MAX) {
+            timeoutMillis = -1;
+        } else {
+            timeoutMillis = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
+            if (timeoutMillis < 0) {
+                timeoutMillis = 0;
+            }
         }
     }
 
@@ -55,30 +140,40 @@
 
 void RenderThread::queue(RenderTask* task) {
     AutoMutex _lock(mLock);
-    if (mQueueTail) {
-        mQueueTail->mNext = task;
-    } else {
-        mQueueHead = task;
-    }
-    mQueueTail = task;
-    if (mQueueHead == task) {
-        // Only wake if this is the first task
+    mQueue.queue(task);
+    if (mNextWakeup && task->mRunAt < mNextWakeup) {
+        mNextWakeup = 0;
         mLooper->wake();
     }
 }
 
-RenderTask* RenderThread::nextTask() {
+void RenderThread::queueDelayed(RenderTask* task, int delayMs) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    task->mRunAt = now + delayMs;
+    queue(task);
+}
+
+void RenderThread::remove(RenderTask* task) {
     AutoMutex _lock(mLock);
-    RenderTask* ret = mQueueHead;
-    if (ret) {
-        if (mQueueTail == mQueueHead) {
-            mQueueTail = mQueueHead = 0;
-        } else {
-            mQueueHead = ret->mNext;
+    mQueue.remove(task);
+}
+
+RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
+    AutoMutex _lock(mLock);
+    RenderTask* next = mQueue.peek();
+    if (!next) {
+        mNextWakeup = LLONG_MAX;
+    } else {
+        // Most tasks won't be delayed, so avoid unnecessary systemTime() calls
+        if (next->mRunAt <= 0 || next->mRunAt <= systemTime(SYSTEM_TIME_MONOTONIC)) {
+            next = mQueue.next();
         }
-        ret->mNext = 0;
+        mNextWakeup = next->mRunAt;
     }
-    return ret;
+    if (nextWakeup) {
+        *nextWakeup = mNextWakeup;
+    }
+    return next;
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 4edd575..e444aa0 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -28,11 +28,27 @@
 namespace uirenderer {
 namespace renderthread {
 
+class TaskQueue {
+public:
+    TaskQueue();
+
+    RenderTask* next();
+    void queue(RenderTask* task);
+    RenderTask* peek();
+    void remove(RenderTask* task);
+
+private:
+    RenderTask* mHead;
+    RenderTask* mTail;
+};
+
 class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
 public:
     // RenderThread takes complete ownership of tasks that are queued
     // and will delete them after they are run
     ANDROID_API void queue(RenderTask* task);
+    void queueDelayed(RenderTask* task, int delayMs);
+    void remove(RenderTask* task);
 
 protected:
     virtual bool threadLoop();
@@ -43,13 +59,16 @@
     RenderThread();
     virtual ~RenderThread();
 
-    RenderTask* nextTask();
+    // Returns the next task to be run. If this returns NULL nextWakeup is set
+    // to the time to requery for the nextTask to run. mNextWakeup is also
+    // set to this time
+    RenderTask* nextTask(nsecs_t* nextWakeup);
 
     sp<Looper> mLooper;
     Mutex mLock;
 
-    RenderTask* mQueueHead;
-    RenderTask* mQueueTail;
+    nsecs_t mNextWakeup;
+    TaskQueue mQueue;
 };
 
 } /* namespace renderthread */