EGL migration to native

 Move EGL state management to native side for RemoteGLRenderer

Change-Id: I12b0fed70246564d4caebf87374e8bbca655c572
diff --git a/core/java/android/view/RemoteGLRenderer.java b/core/java/android/view/RemoteGLRenderer.java
index 6195fcb..0862458 100644
--- a/core/java/android/view/RemoteGLRenderer.java
+++ b/core/java/android/view/RemoteGLRenderer.java
@@ -16,40 +16,11 @@
 
 package android.view;
 
-import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
-import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
-import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
-import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
-import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
-import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
-
-import android.content.ComponentCallbacks2;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
-import android.opengl.GLUtils;
-import android.opengl.ManagedEGLContext;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -58,19 +29,9 @@
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
 
-import com.google.android.gles_jni.EGLImpl;
-
 import java.io.PrintWriter;
 import java.util.concurrent.locks.ReentrantLock;
 
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGL11;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import javax.microedition.khronos.opengles.GL;
-
 /**
  * Hardware renderer using OpenGL that's used as the remote endpoint
  * of ThreadedRenderer
@@ -119,21 +80,8 @@
     private static final int OVERDRAW_TYPE_COUNT = 1;
     private static final int GL_VERSION = 2;
 
-    static EGL10 sEgl;
-    static EGLDisplay sEglDisplay;
-    static EGLConfig sEglConfig;
-    static final Object[] sEglLock = new Object[0];
     int mWidth = -1, mHeight = -1;
 
-    static final ThreadLocal<ManagedEGLContext> sEglContextStorage
-            = new ThreadLocal<ManagedEGLContext>();
-
-    EGLContext mEglContext;
-    Thread mEglThread;
-
-    EGLSurface mEglSurface;
-
-    GL mGl;
     HardwareCanvas mCanvas;
 
     String mName;
@@ -141,17 +89,8 @@
     long mFrameCount;
     Paint mDebugPaint;
 
-    static boolean sDirtyRegions;
-    static final boolean sDirtyRegionsRequested;
-    static {
-        String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
-        sDirtyRegionsRequested = sDirtyRegions;
-    }
-
     boolean mDirtyRegionsEnabled;
-    boolean mUpdateDirtyRegions;
+    boolean mSurfaceUpdated;
 
     boolean mProfileEnabled;
     int mProfileVisualizerType = -1;
@@ -170,8 +109,6 @@
 
     final boolean mTranslucent;
 
-    private boolean mDestroyed;
-
     private final Rect mRedrawClip = new Rect();
 
     private final int[] mSurfaceSize = new int[2];
@@ -183,84 +120,12 @@
 
     private DisplayMetrics mDisplayMetrics;
     private ThreadedRenderer mOwningRenderer;
-
-    private static EGLSurface sPbuffer;
-    private static final Object[] sPbufferLock = new Object[0];
-
-    private static class GLRendererEglContext extends ManagedEGLContext {
-        final Handler mHandler = new Handler();
-
-        public GLRendererEglContext(EGLContext context) {
-            super(context);
-        }
-
-        @Override
-        public void onTerminate(final EGLContext eglContext) {
-            // Make sure we do this on the correct thread.
-            if (mHandler.getLooper() != Looper.myLooper()) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        onTerminate(eglContext);
-                    }
-                });
-                return;
-            }
-
-            synchronized (sEglLock) {
-                if (sEgl == null) return;
-
-                if (EGLImpl.getInitCount(sEglDisplay) == 1) {
-                    usePbufferSurface(eglContext);
-                    GLES20Canvas.terminateCaches();
-
-                    sEgl.eglDestroyContext(sEglDisplay, eglContext);
-                    sEglContextStorage.set(null);
-                    sEglContextStorage.remove();
-
-                    sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
-                    sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
-                            EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-                    sEgl.eglReleaseThread();
-                    sEgl.eglTerminate(sEglDisplay);
-
-                    sEgl = null;
-                    sEglDisplay = null;
-                    sEglConfig = null;
-                    sPbuffer = null;
-                }
-            }
-        }
-    }
+    private long mNativeCanvasContext;
 
     HardwareCanvas createCanvas() {
         return mGlCanvas = new GLES20Canvas(mTranslucent);
     }
 
-    ManagedEGLContext createManagedContext(EGLContext eglContext) {
-        return new GLRendererEglContext(mEglContext);
-    }
-
-    int[] getConfig(boolean dirtyRegions) {
-        //noinspection PointlessBooleanExpression,ConstantConditions
-        final int stencilSize = GLES20Canvas.getStencilSize();
-        final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
-
-        return new int[] {
-                EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
-                EGL_RED_SIZE, 8,
-                EGL_GREEN_SIZE, 8,
-                EGL_BLUE_SIZE, 8,
-                EGL_ALPHA_SIZE, 8,
-                EGL_DEPTH_SIZE, 0,
-                EGL_CONFIG_CAVEAT, EGL_NONE,
-                EGL_STENCIL_SIZE, stencilSize,
-                EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
-                EGL_NONE
-        };
-    }
-
     void initCaches() {
         if (GLES20Canvas.initCaches()) {
             // Caches were (re)initialized, rebind atlas
@@ -295,7 +160,7 @@
     }
 
     boolean canDraw() {
-        return mGl != null && mCanvas != null && mGlCanvas != null;
+        return mCanvas != null && mGlCanvas != null;
     }
 
     int onPreDraw(Rect dirty) {
@@ -458,17 +323,11 @@
             if (full && mCanvas != null) {
                 mCanvas = null;
             }
-
-            if (!isEnabled() || mDestroyed) {
-                setEnabled(false);
-                return;
+            if (mNativeCanvasContext != 0) {
+                destroyContext(mNativeCanvasContext);
+                mNativeCanvasContext = 0;
             }
-
-            destroySurface();
             setEnabled(false);
-
-            mDestroyed = true;
-            mGl = null;
         } finally {
             if (full && mGlCanvas != null) {
                 mGlCanvas = null;
@@ -524,21 +383,13 @@
         boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
 
         if (needsContext) {
-            GLRendererEglContext managedContext =
-                    (GLRendererEglContext) sEglContextStorage.get();
-            if (managedContext == null) return false;
-            usePbufferSurface(managedContext.getContext());
-        }
-
-        try {
-            action.run();
-        } finally {
-            if (needsContext) {
-                sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
-                        EGL_NO_SURFACE, EGL_NO_CONTEXT);
+            if (!usePBufferSurface()) {
+                return false;
             }
         }
 
+        action.run();
+
         return true;
     }
 
@@ -600,44 +451,6 @@
         }
     }
 
-    static void startTrimMemory(int level) {
-        if (sEgl == null || sEglConfig == null) return;
-
-        GLRendererEglContext managedContext =
-                (GLRendererEglContext) sEglContextStorage.get();
-        // We do not have OpenGL objects
-        if (managedContext == null) {
-            return;
-        } else {
-            usePbufferSurface(managedContext.getContext());
-        }
-
-        if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
-            GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
-        } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
-            GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
-        }
-    }
-
-    static void endTrimMemory() {
-        if (sEgl != null && sEglDisplay != null) {
-            sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-        }
-    }
-
-    private static void usePbufferSurface(EGLContext eglContext) {
-        synchronized (sPbufferLock) {
-            // Create a temporary 1x1 pbuffer so we have a context
-            // to clear our OpenGL objects
-            if (sPbuffer == null) {
-                sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
-                        EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
-                });
-            }
-        }
-        sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
-    }
-
     RemoteGLRenderer(ThreadedRenderer owningRenderer, boolean translucent) {
         mOwningRenderer = owningRenderer;
         mTranslucent = translucent;
@@ -771,62 +584,31 @@
         return mDirtyRegionsEnabled;
     }
 
-    /**
-     * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
-     * is invoked and the requested flag is turned off. The error code is
-     * also logged as a warning.
-     */
-    void checkEglErrors() {
-        if (isEnabled()) {
-            checkEglErrorsForced();
-        }
-    }
-
-    private void checkEglErrorsForced() {
-        int error = sEgl.eglGetError();
-        if (error != EGL_SUCCESS) {
-            // something bad has happened revert to
-            // normal rendering.
-            Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
-            fallback(error != EGL11.EGL_CONTEXT_LOST);
-        }
-    }
-
-    private void fallback(boolean fallback) {
+    private void triggerSoftwareFallback() {
         destroy(true);
-        if (fallback) {
-            // 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.");
-        }
+        // 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()) {
-            boolean contextCreated = initializeEgl();
-            mGl = createEglSurface(surface);
-            mDestroyed = false;
+            mNativeCanvasContext = createContext();
+            boolean surfaceCreated = createEglSurface(surface);
 
-            if (mGl != null) {
-                int err = sEgl.eglGetError();
-                if (err != EGL_SUCCESS) {
-                    destroy(true);
-                    setRequested(false);
-                } else {
-                    if (mCanvas == null) {
-                        mCanvas = createCanvas();
-                        mCanvas.setName(mName);
-                    }
-                    setEnabled(true);
-
-                    if (contextCreated) {
-                        initAtlas();
-                    }
+            if (surfaceCreated) {
+                if (mCanvas == null) {
+                    mCanvas = createCanvas();
+                    mCanvas.setName(mName);
                 }
-
-                return mCanvas != null;
+                setEnabled(true);
+                initAtlas();
+                return true;
+            } else {
+                destroy(true);
+                setRequested(false);
             }
         }
         return false;
@@ -839,251 +621,30 @@
         }
     }
 
-    boolean initializeEgl() {
-        synchronized (sEglLock) {
-            if (sEgl == null && sEglConfig == null) {
-                sEgl = (EGL10) EGLContext.getEGL();
-
-                // Get to the default display.
-                sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
-                if (sEglDisplay == EGL_NO_DISPLAY) {
-                    throw new RuntimeException("eglGetDisplay failed "
-                            + GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                }
-
-                // We can now initialize EGL for that display
-                int[] version = new int[2];
-                if (!sEgl.eglInitialize(sEglDisplay, version)) {
-                    throw new RuntimeException("eglInitialize failed " +
-                            GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                }
-
-                checkEglErrorsForced();
-
-                sEglConfig = loadEglConfig();
-            }
-        }
-
-        ManagedEGLContext managedContext = sEglContextStorage.get();
-        mEglContext = managedContext != null ? managedContext.getContext() : null;
-        mEglThread = Thread.currentThread();
-
-        if (mEglContext == null) {
-            mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
-            sEglContextStorage.set(createManagedContext(mEglContext));
-            return true;
-        }
-
-        return false;
-    }
-
-    private EGLConfig loadEglConfig() {
-        EGLConfig eglConfig = chooseEglConfig();
-        if (eglConfig == null) {
-            // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
-            if (sDirtyRegions) {
-                sDirtyRegions = false;
-                eglConfig = chooseEglConfig();
-                if (eglConfig == null) {
-                    throw new RuntimeException("eglConfig not initialized");
-                }
-            } else {
-                throw new RuntimeException("eglConfig not initialized");
-            }
-        }
-        return eglConfig;
-    }
-
-    private EGLConfig chooseEglConfig() {
-        EGLConfig[] configs = new EGLConfig[1];
-        int[] configsCount = new int[1];
-        int[] configSpec = getConfig(sDirtyRegions);
-
-        // Debug
-        final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
-        if ("all".equalsIgnoreCase(debug)) {
-            sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
-
-            EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
-            sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
-                    configsCount[0], configsCount);
-
-            for (EGLConfig config : debugConfigs) {
-                printConfig(config);
-            }
-        }
-
-        if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
-            throw new IllegalArgumentException("eglChooseConfig failed " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        } else if (configsCount[0] > 0) {
-            if ("choice".equalsIgnoreCase(debug)) {
-                printConfig(configs[0]);
-            }
-            return configs[0];
-        }
-
-        return null;
-    }
-
-    private static void printConfig(EGLConfig config) {
-        int[] value = new int[1];
-
-        Log.d(LOG_TAG, "EGL configuration " + config + ":");
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
-        Log.d(LOG_TAG, "  RED_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
-        Log.d(LOG_TAG, "  GREEN_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
-        Log.d(LOG_TAG, "  BLUE_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
-        Log.d(LOG_TAG, "  ALPHA_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
-        Log.d(LOG_TAG, "  DEPTH_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
-        Log.d(LOG_TAG, "  STENCIL_SIZE = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
-        Log.d(LOG_TAG, "  SAMPLE_BUFFERS = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
-        Log.d(LOG_TAG, "  SAMPLES = " + value[0]);
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
-        Log.d(LOG_TAG, "  SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
-
-        sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
-        Log.d(LOG_TAG, "  CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
-    }
-
-    GL createEglSurface(Surface surface) throws OutOfResourcesException {
-        // Check preconditions.
-        if (sEgl == null) {
-            throw new RuntimeException("egl not initialized");
-        }
-        if (sEglDisplay == null) {
-            throw new RuntimeException("eglDisplay not initialized");
-        }
-        if (sEglConfig == null) {
-            throw new RuntimeException("eglConfig not initialized");
-        }
-        if (Thread.currentThread() != mEglThread) {
-            throw new IllegalStateException("HardwareRenderer cannot be used "
-                    + "from multiple threads");
-        }
-
-        // In case we need to destroy an existing surface
-        destroySurface();
-
+    boolean createEglSurface(Surface surface) throws OutOfResourcesException {
         // Create an EGL surface we can render into.
-        if (!createSurface(surface)) {
-            return null;
+        if (!setSurface(mNativeCanvasContext, surface)) {
+            return false;
         }
+        makeCurrent(mNativeCanvasContext);
+        mSurfaceUpdated = true;
 
         initCaches();
-
-        return mEglContext.getGL();
-    }
-
-    private void enableDirtyRegions() {
-        // If mDirtyRegions is set, this means we have an EGL configuration
-        // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
-        if (sDirtyRegions) {
-            if (!(mDirtyRegionsEnabled = GLRenderer.preserveBackBuffer())) {
-                Log.w(LOG_TAG, "Backbuffer cannot be preserved");
-            }
-        } else if (sDirtyRegionsRequested) {
-            // If mDirtyRegions is not set, our EGL configuration does not
-            // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
-            // swap behavior might be EGL_BUFFER_PRESERVED, which means we
-            // want to set mDirtyRegions. We try to do this only if dirty
-            // regions were initially requested as part of the device
-            // configuration (see RENDER_DIRTY_REGIONS)
-            mDirtyRegionsEnabled = GLRenderer.isBackBufferPreserved();
-        }
-    }
-
-    EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
-        final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
-
-        EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
-                attribs);
-        if (context == null || context == EGL_NO_CONTEXT) {
-            //noinspection ConstantConditions
-            throw new IllegalStateException(
-                    "Could not create an EGL context. eglCreateContext failed with error: " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        }
-
-        return context;
-    }
-
-    void destroySurface() {
-        if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
-            if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
-                sEgl.eglMakeCurrent(sEglDisplay,
-                        EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            }
-            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
-            mEglSurface = null;
-        }
+        return true;
     }
 
     @Override
     void invalidate(Surface surface) {
-        // Cancels any existing buffer to ensure we'll get a buffer
-        // of the right size before we call eglSwapBuffers
-        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-        if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
-            sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
-            mEglSurface = null;
-            setEnabled(false);
-        }
+        setSurface(mNativeCanvasContext, null);
+        setEnabled(false);
 
         if (surface.isValid()) {
-            if (!createSurface(surface)) {
-                return;
-            }
-
-            mUpdateDirtyRegions = true;
-
-            if (mCanvas != null) {
+            if (createEglSurface(surface) && mCanvas != null) {
                 setEnabled(true);
             }
         }
     }
 
-    private boolean createSurface(Surface surface) {
-        mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
-
-        if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
-            int error = sEgl.eglGetError();
-            if (error == EGL_BAD_NATIVE_WINDOW) {
-                Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
-                return false;
-            }
-            throw new RuntimeException("createWindowSurface failed "
-                    + GLUtils.getEGLErrorString(error));
-        }
-
-        if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-            throw new IllegalStateException("eglMakeCurrent failed " +
-                    GLUtils.getEGLErrorString(sEgl.eglGetError()));
-        }
-
-        enableDirtyRegions();
-
-        return true;
-    }
-
     @Override
     boolean validate() {
         return checkRenderContext() != SURFACE_STATE_ERROR;
@@ -1153,8 +714,7 @@
                 dirty = null;
             }
 
-            // We are already on the correct thread
-            final int surfaceState = checkRenderContextUnsafe();
+            final int surfaceState = checkRenderContext();
             if (surfaceState != SURFACE_STATE_ERROR) {
                 HardwareCanvas canvas = mCanvas;
 
@@ -1328,15 +888,16 @@
                 eglSwapBuffersStartTime = System.nanoTime();
             }
 
-            sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
+            if (!swapBuffers(mNativeCanvasContext)) {
+                triggerSoftwareFallback();
+            }
+            mSurfaceUpdated = false;
 
             if (mProfileEnabled) {
                 long now = System.nanoTime();
                 float total = (now - eglSwapBuffersStartTime) * 0.000001f;
                 mProfileData[mProfileCurrentFrame + 2] = total;
             }
-
-            checkEglErrors();
         }
     }
 
@@ -1408,42 +969,11 @@
      * @see #checkRenderContextUnsafe()
      */
     int checkRenderContext() {
-        if (mEglThread != Thread.currentThread()) {
-            throw new IllegalStateException("Hardware acceleration can only be used with a " +
-                    "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
-                    "Current thread: " + Thread.currentThread());
+        if (!makeCurrent(mNativeCanvasContext)) {
+            triggerSoftwareFallback();
+            return SURFACE_STATE_ERROR;
         }
-
-        return checkRenderContextUnsafe();
-    }
-
-    /**
-     * Ensures the current EGL context and surface are the ones we expect.
-     * This method does not check the current thread.
-     *
-     * @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 #checkRenderContext()
-     */
-    private int checkRenderContextUnsafe() {
-        if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
-                !mEglContext.equals(sEgl.eglGetCurrentContext())) {
-            if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                Log.e(LOG_TAG, "eglMakeCurrent failed " +
-                        GLUtils.getEGLErrorString(sEgl.eglGetError()));
-                fallback(true);
-                return SURFACE_STATE_ERROR;
-            } else {
-                if (mUpdateDirtyRegions) {
-                    enableDirtyRegions();
-                    mUpdateDirtyRegions = false;
-                }
-                return SURFACE_STATE_UPDATED;
-            }
-        }
-        return SURFACE_STATE_SUCCESS;
+        return mSurfaceUpdated ? SURFACE_STATE_UPDATED : SURFACE_STATE_SUCCESS;
     }
 
     private static int dpToPx(int dp, float density) {
@@ -1535,4 +1065,11 @@
             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/jni/Android.mk b/core/jni/Android.mk
index f26374a..e88db54 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -58,6 +58,7 @@
 	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 89d75dc..011db5e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -124,6 +124,7 @@
 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,6 +1130,7 @@
     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
new file mode 100644
index 0000000..96a203b
--- /dev/null
+++ b/core/jni/android_view_RemoteGLRenderer.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 8bb6cb4..8d77e36 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "GLRenderer"
+#define LOG_TAG "ThreadedRenderer"
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 4f0d15a..05163c8 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -50,6 +50,7 @@
 
 	# RenderThread stuff
 	LOCAL_SRC_FILES += \
+		renderthread/CanvasContext.cpp \
 		renderthread/RenderTask.cpp \
 		renderthread/RenderThread.cpp
 
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
index 2764523..8ce57db 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/Stencil.cpp
@@ -35,7 +35,7 @@
 Stencil::Stencil(): mState(kDisabled) {
 }
 
-uint32_t Stencil::getStencilSize() {
+uint8_t Stencil::getStencilSize() {
     return STENCIL_BUFFER_SIZE;
 }
 
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
index 83ad668..c5e5186 100644
--- a/libs/hwui/Stencil.h
+++ b/libs/hwui/Stencil.h
@@ -40,7 +40,7 @@
      * Returns the desired size for the stencil buffer. If the returned value
      * is 0, then no stencil buffer is required.
      */
-    ANDROID_API static uint32_t getStencilSize();
+    ANDROID_API static uint8_t getStencilSize();
 
     /**
      * Returns the smallest stencil format accepted by render buffers.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
new file mode 100644
index 0000000..ffb8a32
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -0,0 +1,326 @@
+/*
+ * 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 "CanvasContext"
+
+#include "CanvasContext.h"
+
+#include <cutils/properties.h>
+#include <strings.h>
+
+#include "../Caches.h"
+#include "../Stencil.h"
+
+#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#define GLES_VERSION 2
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ERROR_CASE(x) case x: return #x;
+static const char* egl_error_str(EGLint error) {
+    switch (error) {
+        ERROR_CASE(EGL_SUCCESS)
+        ERROR_CASE(EGL_NOT_INITIALIZED)
+        ERROR_CASE(EGL_BAD_ACCESS)
+        ERROR_CASE(EGL_BAD_ALLOC)
+        ERROR_CASE(EGL_BAD_ATTRIBUTE)
+        ERROR_CASE(EGL_BAD_CONFIG)
+        ERROR_CASE(EGL_BAD_CONTEXT)
+        ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
+        ERROR_CASE(EGL_BAD_DISPLAY)
+        ERROR_CASE(EGL_BAD_MATCH)
+        ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
+        ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
+        ERROR_CASE(EGL_BAD_PARAMETER)
+        ERROR_CASE(EGL_BAD_SURFACE)
+        ERROR_CASE(EGL_CONTEXT_LOST)
+    default:
+        return "Unknown error";
+    }
+}
+static const char* egl_error_str() {
+    return egl_error_str(eglGetError());
+}
+
+static bool load_dirty_regions_property() {
+    char buf[PROPERTY_VALUE_MAX];
+    int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
+    return !strncasecmp("true", buf, len);
+}
+
+// This class contains the shared global EGL objects, such as EGLDisplay
+// and EGLConfig, which are re-used by CanvasContext
+class GlobalContext {
+public:
+    static GlobalContext* get();
+
+    // Returns true if EGL was initialized,
+    // false if it was already initialized
+    bool initialize();
+
+    bool 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);
+
+    bool enableDirtyRegions(EGLSurface surface);
+
+private:
+    GlobalContext();
+    // GlobalContext is never destroyed, method is purposely not implemented
+    ~GlobalContext();
+
+    bool loadConfig();
+    bool createContext();
+
+    static GlobalContext* sContext;
+
+    EGLDisplay mEglDisplay;
+    EGLConfig mEglConfig;
+    EGLContext mEglContext;
+    EGLSurface mPBufferSurface;
+
+    const bool mRequestDirtyRegions;
+    bool mCanSetDirtyRegions;
+
+    EGLSurface mCurrentSurface;
+};
+
+GlobalContext* GlobalContext::sContext = 0;
+
+GlobalContext* GlobalContext::get() {
+    if (!sContext) {
+        sContext = new GlobalContext();
+    }
+    return sContext;
+}
+
+GlobalContext::GlobalContext()
+        : mEglDisplay(EGL_NO_DISPLAY)
+        , mEglConfig(0)
+        , mEglContext(EGL_NO_CONTEXT)
+        , mPBufferSurface(EGL_NO_SURFACE)
+        , mRequestDirtyRegions(load_dirty_regions_property())
+        , mCurrentSurface(EGL_NO_SURFACE) {
+    mCanSetDirtyRegions = mRequestDirtyRegions;
+    ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+}
+
+bool GlobalContext::initialize() {
+    if (mEglDisplay != EGL_NO_DISPLAY) return false;
+
+    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    if (mEglDisplay == EGL_NO_DISPLAY) {
+        ALOGE("Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+        return false;
+    }
+
+    EGLint major, minor;
+    if (eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE) {
+        ALOGE("Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+        return false;
+    }
+    ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+
+    if (!loadConfig()) {
+        return false;
+    }
+    if (!createContext()) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GlobalContext::loadConfig() {
+    EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    EGLint attribs[] = {
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 0,
+            EGL_CONFIG_CAVEAT, EGL_NONE,
+            EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+            EGL_NONE
+    };
+
+    EGLint num_configs = 1;
+    if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
+            || num_configs != 1) {
+        // Failed to get a valid config
+        if (mCanSetDirtyRegions) {
+            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+            // Try again without dirty regions enabled
+            mCanSetDirtyRegions = false;
+            loadConfig();
+        } else {
+            ALOGE("Failed to choose config, error = %s", egl_error_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+bool 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;
+}
+
+bool GlobalContext::usePBufferSurface() {
+    if (mEglDisplay == EGL_NO_DISPLAY) return false;
+
+    if (mPBufferSurface == EGL_NO_SURFACE) {
+        EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+        mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+    }
+    return makeCurrent(mPBufferSurface);
+}
+
+EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
+    initialize();
+    return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+}
+
+void GlobalContext::destroySurface(EGLSurface surface) {
+    if (isCurrent(surface)) {
+        makeCurrent(EGL_NO_SURFACE);
+    }
+    if (!eglDestroySurface(mEglDisplay, surface)) {
+        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+    }
+}
+
+void GlobalContext::destroy() {
+    if (mEglDisplay == EGL_NO_DISPLAY) return;
+
+    usePBufferSurface();
+    if (Caches::hasInstance()) {
+        Caches::getInstance().terminate();
+    }
+
+    eglDestroyContext(mEglDisplay, mEglContext);
+    eglDestroySurface(mEglDisplay, mPBufferSurface);
+    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglTerminate(mEglDisplay);
+    eglReleaseThread();
+
+    mEglDisplay = EGL_NO_DISPLAY;
+    mEglContext = EGL_NO_CONTEXT;
+    mPBufferSurface = EGL_NO_SURFACE;
+    mCurrentSurface = EGL_NO_SURFACE;
+}
+
+bool GlobalContext::makeCurrent(EGLSurface surface) {
+    if (isCurrent(surface)) return true;
+
+    if (surface == EGL_NO_SURFACE) {
+        // If we are setting EGL_NO_SURFACE we don't care about any of the potential
+        // return errors, which would only happen if mEglDisplay had already been
+        // 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;
+    }
+    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;
+    }
+    return true;
+}
+
+bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
+    if (!mRequestDirtyRegions) return false;
+
+    if (mCanSetDirtyRegions) {
+        if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
+            ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+                    (void*) surface, egl_error_str());
+            return false;
+        }
+        return true;
+    }
+    // Perhaps it is already enabled?
+    EGLint value;
+    if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
+        ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
+                (void*) surface, egl_error_str());
+        return false;
+    }
+    return value == EGL_BUFFER_PRESERVED;
+}
+
+CanvasContext::CanvasContext()
+        : mEglSurface(EGL_NO_SURFACE)
+        , mDirtyRegionsEnabled(false) {
+    mGlobalContext = GlobalContext::get();
+}
+
+CanvasContext::~CanvasContext() {
+    setSurface(NULL);
+}
+
+bool CanvasContext::setSurface(EGLNativeWindowType window) {
+    if (mEglSurface != EGL_NO_SURFACE) {
+        mGlobalContext->destroySurface(mEglSurface);
+        mEglSurface = EGL_NO_SURFACE;
+    }
+
+    if (window) {
+        mEglSurface = mGlobalContext->createSurface(window);
+    }
+
+    if (mEglSurface != EGL_NO_SURFACE) {
+        mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+    }
+    return !window || mEglSurface != EGL_NO_SURFACE;
+}
+
+bool CanvasContext::swapBuffers() {
+    return mGlobalContext->swapBuffers(mEglSurface);
+}
+
+bool CanvasContext::makeCurrent() {
+    return mGlobalContext->makeCurrent(mEglSurface);
+}
+
+bool CanvasContext::useGlobalPBufferSurface() {
+    return GlobalContext::get()->usePBufferSurface();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
new file mode 100644
index 0000000..77ae737
--- /dev/null
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef CANVASCONTEXT_H_
+#define CANVASCONTEXT_H_
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class GlobalContext;
+
+// 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();
+
+    ANDROID_API bool setSurface(EGLNativeWindowType window);
+    ANDROID_API bool swapBuffers();
+    ANDROID_API bool makeCurrent();
+
+    ANDROID_API static bool useGlobalPBufferSurface();
+
+private:
+
+    GlobalContext* mGlobalContext;
+    EGLSurface mEglSurface;
+    bool mDirtyRegionsEnabled;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+#endif /* CANVASCONTEXT_H_ */