Initial import of the flexible GL surface view
authorPatrick Walton <pwalton@mozilla.com>
Tue, 31 Jan 2012 21:35:52 -0800
changeset 89046 dc18daa0b61de3c280f9426637beb785dd338ec5
parent 89045 7b8accc06c2648c34e9057e0925fb259b9fc309a
child 89047 7ee7e99b22dbbb65189a110548ef9346da5873ae
push id22242
push userkgupta@mozilla.com
push dateWed, 14 Mar 2012 15:19:09 +0000
treeherdermozilla-central@936ef50fa498 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Initial import of the flexible GL surface view
mobile/android/base/Makefile.in
mobile/android/base/gfx/FlexibleGLSurfaceView.java
mobile/android/base/gfx/GLController.java
mobile/android/base/gfx/GLThread.java
mobile/android/base/gfx/LayerView.java
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -103,18 +103,21 @@ FENNEC_JAVA_FILES = \
   TabsTray.java \
   gfx/AbstractLayerView.java \
   gfx/BitmapUtils.java \
   gfx/BufferedCairoImage.java \
   gfx/CairoGLInfo.java \
   gfx/CairoImage.java \
   gfx/CairoUtils.java \
   gfx/CheckerboardImage.java \
+  gfx/FlexibleGLSurfaceView.java \
   gfx/FloatSize.java \
   gfx/GeckoSoftwareLayerClient.java \
+  gfx/GLController.java \
+  gfx/GLThread.java \
   gfx/InputConnectionHandler.java \
   gfx/IntSize.java \
   gfx/Layer.java \
   gfx/LayerClient.java \
   gfx/LayerController.java \
   gfx/LayerRenderer.java \
   gfx/LayerView.java \
   gfx/MultiTileLayer.java \
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/gfx/FlexibleGLSurfaceView.java
@@ -0,0 +1,164 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011-2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko.gfx;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+public class FlexibleGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+    private static final String LOGTAG = "GeckoFlexibleGLSurfaceView";
+
+    private GLSurfaceView.Renderer mRenderer;
+    private GLThread mGLThread; // Protected by this class's monitor.
+    private GLController mController;
+
+    public FlexibleGLSurfaceView(Context context) {
+        super(context);
+        init();
+    }
+
+    public FlexibleGLSurfaceView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+        init();
+    }
+
+    public void init() {
+        SurfaceHolder holder = getHolder();
+        holder.addCallback(this);
+        holder.setFormat(PixelFormat.RGB_565);
+
+        mController = new GLController(this);
+    }
+
+    public void setRenderer(GLSurfaceView.Renderer renderer) {
+        mRenderer = renderer;
+    }
+
+    public GLSurfaceView.Renderer getRenderer() {
+        return mRenderer;
+    }
+
+    public synchronized void requestRender() {
+        if (mGLThread != null) {
+            mGLThread.renderFrame();
+        }
+    }
+
+    /**
+     * Creates a Java GL thread. After this is called, the FlexibleGLSurfaceView may be used just
+     * like a GLSurfaceView. It is illegal to access the controller after this has been called.
+     */
+    public synchronized void createGLThread() {
+        if (mGLThread != null) {
+            throw new FlexibleGLSurfaceViewException("createGLThread() called with a GL thread " +
+                                                     "already in place!");
+        }
+
+        mGLThread = new GLThread(mController);
+        mGLThread.start();
+    }
+
+    /**
+     * Destroys the Java GL thread. Returns a Thread that completes when the Java GL thread is
+     * fully shut down.
+     */
+    public synchronized Thread destroyGLThread() {
+        if (mGLThread == null) {
+            throw new FlexibleGLSurfaceViewException("destroyGLThread() called with no GL " +
+                                                     "thread active!");
+        }
+
+        Thread glThread = mGLThread;
+        mGLThread.shutdown();
+        mGLThread = null;
+        return glThread;
+    }
+
+    public synchronized void recreateSurface() {
+        if (mGLThread == null) {
+            throw new FlexibleGLSurfaceViewException("recreateSurface() called with no GL " +
+                                                     "thread active!");
+        }
+
+        mGLThread.recreateSurface();
+    }
+
+    public synchronized GLController getGLController() {
+        if (mGLThread != null) {
+            throw new FlexibleGLSurfaceViewException("getGLController() called with a GL thread " +
+                                                     "active; shut down the GL thread first!");
+        }
+
+        return mController;
+    }
+
+    public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width,
+                                            int height) {
+        mController.sizeChanged(width, height);
+        if (mGLThread != null) {
+            mGLThread.surfaceChanged(width, height);
+        }
+    }
+
+    public synchronized void surfaceCreated(SurfaceHolder holder) {
+        mController.surfaceCreated();
+        if (mGLThread != null) {
+            mGLThread.surfaceCreated();
+        }
+    }
+
+    public synchronized void surfaceDestroyed(SurfaceHolder holder) {
+        mController.surfaceDestroyed();
+        if (mGLThread != null) {
+            mGLThread.surfaceDestroyed();
+        }
+    }
+
+    public static class FlexibleGLSurfaceViewException extends RuntimeException {
+        public static final long serialVersionUID = 1L;
+
+        FlexibleGLSurfaceViewException(String e) {
+            super(e);
+        }
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/gfx/GLController.java
@@ -0,0 +1,256 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011-2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko.gfx;
+
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+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;
+import javax.microedition.khronos.opengles.GL10;
+
+public class GLController {
+    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+    private static final String LOGTAG = "GeckoGLController";
+
+    private FlexibleGLSurfaceView mView;
+    private int mGLVersion;
+    private boolean mSurfaceValid;
+    private int mWidth, mHeight;
+
+    private EGL10 mEGL;
+    private EGLDisplay mEGLDisplay;
+    private EGLConfig mEGLConfig;
+    private EGLContext mEGLContext;
+    private EGLSurface mEGLSurface;
+
+    private GL mGL;
+
+    private static final int[] CONFIG_SPEC = {
+        EGL10.EGL_RED_SIZE, 5,
+        EGL10.EGL_GREEN_SIZE, 6,
+        EGL10.EGL_BLUE_SIZE, 5,
+        EGL10.EGL_NONE
+    };
+
+    public GLController(FlexibleGLSurfaceView view) {
+        mView = view;
+        mGLVersion = 1;
+        mSurfaceValid = false;
+    }
+
+    public void setGLVersion(int version) {
+        mGLVersion = version;
+    }
+
+    /** You must call this on the same thread you intend to use OpenGL on. */
+    public void initGLContext() {
+        initEGLContext();
+        createEGLSurface();
+    }
+
+    public void disposeGLContext() {
+        if (!mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE,
+                                 EGL10.EGL_NO_CONTEXT)) {
+            throw new GLControllerException("EGL context could not be released!");
+        }
+
+        if (mEGLSurface != null) {
+            if (!mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)) {
+                throw new GLControllerException("EGL surface could not be destroyed!");
+            }
+
+            mEGLSurface = null;
+        }
+
+        if (mEGLContext == null) {
+            if (!mEGL.eglDestroyContext(mEGLDisplay, mEGLContext)) {
+                throw new GLControllerException("EGL context could not be destroyed!");
+            }
+
+            mGL = null;
+            mEGLDisplay = null;
+            mEGLConfig = null;
+            mEGLContext = null;
+        }
+    }
+
+    public GL getGL()                       { return mEGLContext.getGL(); }
+    public EGLDisplay getEGLDisplay()       { return mEGLDisplay;         }
+    public EGLConfig getEGLConfig()         { return mEGLConfig;          }
+    public EGLContext getEGLContext()       { return mEGLContext;         }
+    public EGLSurface getEGLSurface()       { return mEGLSurface;         }
+    public FlexibleGLSurfaceView getView()  { return mView;               }
+
+    public boolean hasSurface() {
+        return mEGLSurface != null;
+    }
+
+    public boolean swapBuffers() {
+        return mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface);
+    }
+
+    public boolean checkForLostContext() {
+        if (mEGL.eglGetError() != EGL11.EGL_CONTEXT_LOST) {
+            return false;
+        }
+
+        mEGLDisplay = null;
+        mEGLConfig = null;
+        mEGLContext = null;
+        mEGLSurface = null;
+        mGL = null;
+        return true;
+    }
+
+    public synchronized void waitForValidSurface() {
+        while (!mSurfaceValid) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public synchronized int getWidth() {
+        return mWidth;
+    }
+
+    public synchronized int getHeight() {
+        return mHeight;
+    }
+
+    synchronized void surfaceCreated() {
+        mSurfaceValid = true;
+        notifyAll();
+    }
+
+    synchronized void surfaceDestroyed() {
+        mSurfaceValid = false;
+        notifyAll();
+    }
+
+    synchronized void sizeChanged(int newWidth, int newHeight) {
+        mWidth = newWidth;
+        mHeight = newHeight;
+    }
+
+    private void initEGLContext() {
+        mEGL = (EGL10)EGLContext.getEGL();
+
+        mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        if (mEGLDisplay == EGL10.EGL_NO_DISPLAY) {
+            throw new GLControllerException("eglGetDisplay() failed");
+        }
+
+        int[] version = new int[2];
+        if (!mEGL.eglInitialize(mEGLDisplay, version)) {
+            throw new GLControllerException("eglInitialize() failed");
+        }
+
+        mEGLConfig = chooseConfig();
+
+        int[] attribList = { EGL_CONTEXT_CLIENT_VERSION, mGLVersion, EGL10.EGL_NONE };
+        mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL10.EGL_NO_CONTEXT,
+                                            attribList);
+        if (mEGLContext == null || mEGLContext == EGL10.EGL_NO_CONTEXT) {
+            throw new GLControllerException("createContext() failed");
+        }
+    }
+
+    private EGLConfig chooseConfig() {
+        int[] numConfigs = new int[1];
+        if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, null, 0, numConfigs) ||
+                numConfigs[0] <= 0) {
+            throw new GLControllerException("No available EGL configurations");
+        }
+
+        EGLConfig[] configs = new EGLConfig[numConfigs[0]];
+        if (!mEGL.eglChooseConfig(mEGLDisplay, CONFIG_SPEC, configs, numConfigs[0], numConfigs)) {
+            throw new GLControllerException("No EGL configuration for that specification");
+        }
+
+        // Select the first 565 RGB configuration.
+        int[] red = new int[1], green = new int[1], blue = new int[1];
+        for (EGLConfig config : configs) {
+            mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_RED_SIZE, red);
+            mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_GREEN_SIZE, green);
+            mEGL.eglGetConfigAttrib(mEGLDisplay, config, EGL10.EGL_BLUE_SIZE, blue);
+            if (red[0] == 5 && green[0] == 6 && blue[0] == 5) {
+                return config;
+            }
+        }
+
+        throw new GLControllerException("No suitable EGL configuration found");
+    }
+
+    private void createEGLSurface() {
+        SurfaceHolder surfaceHolder = mView.getHolder();
+        mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surfaceHolder, null);
+        if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) {
+            throw new GLControllerException("EGL window surface could not be created!");
+        }
+
+        if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
+            throw new GLControllerException("EGL surface could not be made into the current " +
+                                            "surface!");
+        }
+
+        mGL = mEGLContext.getGL();
+
+        if (mView.getRenderer() != null) {
+            mView.getRenderer().onSurfaceCreated((GL10)mGL, mEGLConfig);
+            mView.getRenderer().onSurfaceChanged((GL10)mGL, mView.getWidth(), mView.getHeight());
+        }
+    }
+
+    public static class GLControllerException extends RuntimeException {
+        public static final long serialVersionUID = 1L;
+
+        GLControllerException(String e) {
+            super(e);
+        }
+    }
+}
+
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/gfx/GLThread.java
@@ -0,0 +1,181 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (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.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011-2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick Walton <pcwalton@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko.gfx;
+
+import android.opengl.GLSurfaceView;
+import android.view.SurfaceHolder;
+import javax.microedition.khronos.opengles.GL10;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+// A GL thread managed by Java. It is not necessary to use this class to use the
+// FlexibleGLSurfaceView, but it can be helpful, especially if the GL rendering is to be done
+// entirely in Java.
+class GLThread extends Thread {
+    private LinkedBlockingQueue<Runnable> mQueue;
+    private GLController mController;
+    private boolean mRenderQueued;
+
+    public GLThread(GLController controller) {
+        mQueue = new LinkedBlockingQueue<Runnable>();
+        mController = controller;
+    }
+
+    @Override
+    public void run() {
+        while (true) {
+            Runnable runnable;
+            try {
+                runnable = mQueue.take();
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+
+            runnable.run();
+            if (runnable instanceof ShutdownMessage) {
+                break;
+            }
+        }
+    }
+
+    public void recreateSurface() {
+        mQueue.add(new RecreateSurfaceMessage());
+    }
+
+    public void renderFrame() {
+        // Make sure there's only one render event in the queue at a time.
+        synchronized (this) {
+            if (!mRenderQueued) {
+                mQueue.add(new RenderFrameMessage());
+                mRenderQueued = true;
+            }
+        }
+    }
+
+    public void shutdown() {
+        mQueue.add(new ShutdownMessage());
+    }
+
+    public void surfaceChanged(int width, int height) {
+        mQueue.add(new SizeChangedMessage(width, height));
+    }
+
+    public void surfaceCreated() {
+        mQueue.add(new SurfaceCreatedMessage());
+    }
+
+    public void surfaceDestroyed() {
+        mQueue.add(new SurfaceDestroyedMessage());
+    }
+
+    private void doRecreateSurface() {
+        mController.disposeGLContext();
+        mController.initGLContext();
+    }
+
+    private GLSurfaceView.Renderer getRenderer() {
+        return mController.getView().getRenderer();
+    }
+
+    private class RecreateSurfaceMessage implements Runnable {
+        public void run() {
+            doRecreateSurface();
+        }
+    }
+
+    private class RenderFrameMessage implements Runnable {
+        public void run() {
+            synchronized (GLThread.this) {
+                mRenderQueued = false;
+            }
+
+            // Bail out if the surface was lost.
+            if (mController.getEGLSurface() == null) {
+                return;
+            }
+
+            GLSurfaceView.Renderer renderer = getRenderer();
+            if (renderer != null) {
+                renderer.onDrawFrame((GL10)mController.getGL());
+            }
+
+            mController.swapBuffers();
+            //if (!mController.swapBuffers() && mController.checkForLostContext()) {
+            //    doRecreateSurface();
+            //}
+        }
+    }
+
+    private class ShutdownMessage implements Runnable {
+        public void run() {
+            mController.disposeGLContext();
+            mController = null;
+        }
+    }
+
+    private class SizeChangedMessage implements Runnable {
+        private int mWidth, mHeight;
+
+        public SizeChangedMessage(int width, int height) {
+            mWidth = width;
+            mHeight = height;
+        }
+
+        public void run() {
+            GLSurfaceView.Renderer renderer = getRenderer();
+            if (renderer != null) {
+                renderer.onSurfaceChanged((GL10)mController.getGL(), mWidth, mHeight);
+            }
+        }
+    }
+
+    private class SurfaceCreatedMessage implements Runnable {
+        public void run() {
+            if (!mController.hasSurface()) {
+                mController.initGLContext();
+            }
+        }
+    }
+
+    private class SurfaceDestroyedMessage implements Runnable {
+        public void run() {
+            mController.disposeGLContext();
+        }
+    }
+}
+
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -64,18 +64,18 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 /**
  * A view rendered by the layer compositor.
  *
  * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
  * mediator between the LayerRenderer and the LayerController.
  */
-public class LayerView extends GLSurfaceView
-    implements AbstractLayerView, GeckoEventListener {
+public class LayerView extends FlexibleGLSurfaceView implements AbstractLayerView,
+                                                                GeckoEventListener {
     private Context mContext;
     private LayerController mController;
     private InputConnectionHandler mInputConnectionHandler;
     private LayerRenderer mRenderer;
     private GestureDetector mGestureDetector;
     private SimpleScaleGestureDetector mScaleGestureDetector;
     private long mRenderTime;
     private boolean mRenderTimeReset;
@@ -87,31 +87,32 @@ public class LayerView extends GLSurface
 
     public LayerView(Context context, LayerController controller) {
         super(context);
 
         mContext = context;
         mController = controller;
         mRenderer = new LayerRenderer(this);
         setRenderer(mRenderer);
-        setRenderMode(RENDERMODE_WHEN_DIRTY);
         mGestureDetector = new GestureDetector(context, controller.getGestureListener());
         mScaleGestureDetector =
             new SimpleScaleGestureDetector(controller.getScaleGestureListener());
         mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener());
         mInputConnectionHandler = null;
 
         setFocusable(true);
         setFocusableInTouchMode(true);
 
         GeckoAppShell.registerGeckoEventListener("Preferences:Data", this);
         JSONArray jsonPrefs = new JSONArray();
         jsonPrefs.put(touchEventsPrefName);
         GeckoEvent event = new GeckoEvent("Preferences:Get", jsonPrefs.toString());
         GeckoAppShell.sendEventToGecko(event);
+
+        createGLThread();
     }
 
     public void handleMessage(String event, JSONObject message) {
         if (event.equals("Preferences:Data")) {
             try {
                 JSONArray jsonPrefs = message.getJSONArray("preferences");
                 for (int i = 0; i < jsonPrefs.length(); i++) {
                     JSONObject jPref = jsonPrefs.getJSONObject(i);