page.title=Introducing GLSurfaceView @jd:body

The {@link android android.opengl.GLSurfaceView} class makes it easier for you to use OpenGL ES rendering in your applications by:

GLSurfaceView is a good base for building an application that uses OpenGL ES for part or all of its rendering. A 2D or 3D action game would be a good candidate, as would a 2D or 3D data visualization application such as Google Maps StreetView.

A simple GLSurfaceView application

Here's the source code to the simplest possible OpenGL ES application:

package com.example.android.apis.graphics;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

public class ClearActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new GLSurfaceView(this);
        mGLView.setRenderer(new ClearRenderer());
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }
}

This program doesn't do much: it clears the screen to black on every frame. But it is a complete OpenGL application that correctly implements the Android activity life-cycle. It pauses rendering when the activity is paused, and resumes it when the activity is resumed. You could use this application as the basis for non-interactive demonstration programs. Just add more OpenGL calls to the ClearRenderer.onDrawFrame() method. Notice that you don't even need to subclass the GLSurfaceView view.

The {@link android.opengl.GLSurfaceView.Renderer} interface has three methods:

How about user input?

If you want an interactive application (such as a game), you will typically subclass GLSurfaceView, because that's an easy way of obtaining input events. Here's a slightly longer example showing how to do that:

package com.google.android.ClearTest;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.MotionEvent;

public class ClearActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mGLView = new ClearGLSurfaceView(this);
        setContentView(mGLView);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
    }

    private GLSurfaceView mGLView;
}

class ClearGLSurfaceView extends GLSurfaceView {
    public ClearGLSurfaceView(Context context) {
        super(context);
        mRenderer = new ClearRenderer();
        setRenderer(mRenderer);
    }

    public boolean onTouchEvent(final MotionEvent event) {
        queueEvent(new Runnable(){
            public void run() {
                mRenderer.setColor(event.getX() / getWidth(),
                        event.getY() / getHeight(), 1.0f);
            }});
            return true;
        }

        ClearRenderer mRenderer;
}

class ClearRenderer implements GLSurfaceView.Renderer {
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // Do nothing special.
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    public void onDrawFrame(GL10 gl) {
        gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    }

    public void setColor(float r, float g, float b) {
        mRed = r;
        mGreen = g;
        mBlue = b;
    }

    private float mRed;
    private float mGreen;
    private float mBlue;
}

This application clears the screen for every frame. When you tap on the screen, it sets the clear color based on the (x,y) coordinates of your touch event. Note the use of queueEvent() in ClearGLSurfaceView.onTouchEvent(). The queueEvent() method is used to safely communicate between the UI thread and the rendering thread. If you prefer, you can use some other Java cross-thread communication technique, such as synchronized methods on the Renderer class itself. However, queueing events is often the simplest way of dealing with cross-thread communication.

Other GLSurfaceView samples

Tired of just clearing the screen? You can find more interesting samples in the API Demos sample included in the Android SDK. All the OpenGL ES samples have been converted to use the GLSurfaceView view:

Choosing a surface

GLSurfaceView helps you choose the type of surface to render to. Different Android devices support different types of surfaces, with no common subset. This makes it tricky problem to choose the best available surface on each device.

By default, GLSurfaceView tries to find a surface that's as close as possible to a 16-bit RGB frame buffer with a 16-bit depth buffer. Depending upon your application's needs you may want to change this behavior. For example, the Translucent GLSurfaceView sample needs an Alpha channel in order to render translucent data. GLSurfaceView provides an overloaded setEGLSurfaceChooser() method to give you control over which surface type is chosen:

setEGLConfigChooser(boolean needDepth)
Choose a config that's closest to R5G6B5 with or without a 16-bit framebuffer
setEGLConfigChooser(int redSize, int greenSize,int blueSize, int alphaSize,int depthSize, int stencilSize)
Choose the config with the fewest number of bits per pixel that has at least as many bits-per-channel as specified in the constructor.
setEGLConfigChooser(EGLConfigChooser configChooser)
Allow total control over choosing a configuration. You pass in your own implementation of EGLConfigChooser, which gets to inspect the device's capabilities and choose a configuration.

Continuous rendering versus render-when-dirty

Most 3D applications, such as games or simulations, are continuously animated. But some 3D applications are more reactive: they wait passively until the user does something, and then react to it. For those types of applications, the default GLSurfaceView behavior of continuously redrawing the screen is a waste of time. If you are developing a reactive application, you can call GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY), which turns off the continuous animation. Then you call GLSurfaceView.requestRender() whenever you want to re-render.

Help With debugging

GLSurfaceView has a handy built-in feature for debugging OpenGL ES applications: the GLSurfaceView.setDebugFlags() method can be used to enable logging and/or error checking your OpenGL ES calls. Call this method in your GLSurfaceView's constructor, before calling setRenderer():

public ClearGLSurfaceView(Context context) {
    super(context);
    // Turn on error-checking and logging
    setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
    mRenderer = new ClearRenderer();
    setRenderer(mRenderer);
}