GLSurfaceViewpublic class GLSurfaceView extends android.view.SurfaceView implements SurfaceHolder.CallbackAn implementation of SurfaceView that uses the dedicated surface for
displaying OpenGL rendering.
A GLSurfaceView provides the following features:
- Manages a surface, which is a special piece of memory that can be
composited into the Android view system.
- Manages an EGL display, which enables OpenGL to render into a surface.
- Accepts a user-provided Renderer object that does the actual rendering.
- Renders on a dedicated thread to decouple rendering performance from the
UI thread.
- Supports both on-demand and continuous rendering.
- Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
Developer Guides
For more information about how to use OpenGL, read the
OpenGL developer guide.
Using GLSurfaceView
Typically you use GLSurfaceView by subclassing it and overriding one or more of the
View system input event methods. If your application does not need to override event
methods then GLSurfaceView can be used as-is. For the most part
GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
For example, unlike a regular View, drawing is delegated to a separate Renderer object which
is registered with the GLSurfaceView
using the {@link #setRenderer(Renderer)} call.
Initializing GLSurfaceView
All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
more of these methods before calling setRenderer:
- {@link #setDebugFlags(int)}
- {@link #setEGLConfigChooser(boolean)}
- {@link #setEGLConfigChooser(EGLConfigChooser)}
- {@link #setEGLConfigChooser(int, int, int, int, int, int)}
- {@link #setGLWrapper(GLWrapper)}
Specifying the android.view.Surface
By default GLSurfaceView will create a PixelFormat.RGB_888 format surface. If a translucent
surface is required, call getHolder().setFormat(PixelFormat.TRANSLUCENT).
The exact format of a TRANSLUCENT surface is device dependent, but it will be
a 32-bit-per-pixel surface with 8 bits per component.
Choosing an EGL Configuration
A given Android device may support multiple EGLConfig rendering configurations.
The available configurations may differ in how may channels of data are present, as
well as how many bits are allocated to each channel. Therefore, the first thing
GLSurfaceView has to do when starting to render is choose what EGLConfig to use.
By default GLSurfaceView chooses a EGLConfig that has an RGB_888 pixel format,
with at least a 16-bit depth buffer and no stencil.
If you would prefer a different EGLConfig
you can override the default behavior by calling one of the
setEGLConfigChooser methods.
Debug Behavior
You can optionally modify the behavior of GLSurfaceView by calling
one or more of the debugging methods {@link #setDebugFlags(int)},
and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
typically they are called before setRenderer so that they take effect immediately.
Setting a Renderer
Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
The renderer is
responsible for doing the actual OpenGL rendering.
Rendering Mode
Once the renderer is set, you can control whether the renderer draws
continuously or on-demand by calling
{@link #setRenderMode}. The default is continuous rendering.
Activity Life-cycle
A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
are required to call {@link #onPause()} when the activity pauses and
{@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
the OpenGL display.
Handling events
To handle an event you will typically subclass GLSurfaceView and override the
appropriate method, just as you would with any other View. However, when handling
the event, you may need to communicate with the Renderer object
that's running in the rendering thread. You can do this using any
standard Java cross-thread communication mechanism. In addition,
one relatively easy way to communicate with your renderer is
to call
{@link #queueEvent(Runnable)}. For example:
class MyGLSurfaceView extends GLSurfaceView {
private MyRenderer mMyRenderer;
public void start() {
mMyRenderer = ...;
setRenderer(mMyRenderer);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
queueEvent(new Runnable() {
// This method will be called on the rendering
// thread:
public void run() {
mMyRenderer.handleDpadCenter();
}});
return true;
}
return super.onKeyDown(keyCode, event);
}
}
|
Fields Summary |
---|
private static final String | TAG | private static final boolean | LOG_ATTACH_DETACH | private static final boolean | LOG_THREADS | private static final boolean | LOG_PAUSE_RESUME | private static final boolean | LOG_SURFACE | private static final boolean | LOG_RENDERER | private static final boolean | LOG_RENDERER_DRAW_FRAME | private static final boolean | LOG_EGL | public static final int | RENDERMODE_WHEN_DIRTYThe renderer only renders
when the surface is created, or when {@link #requestRender} is called. | public static final int | RENDERMODE_CONTINUOUSLYThe renderer is called
continuously to re-render the scene. | public static final int | DEBUG_CHECK_GL_ERRORCheck glError() after every GL call and throw an exception if glError indicates
that an error has occurred. This can be used to help track down which OpenGL ES call
is causing an error. | public static final int | DEBUG_LOG_GL_CALLSLog GL calls to the system log at "verbose" level with tag "GLSurfaceView". | private static final GLThreadManager | sGLThreadManager | private final WeakReference | mThisWeakRef | private GLThread | mGLThread | private Renderer | mRenderer | private boolean | mDetached | private EGLConfigChooser | mEGLConfigChooser | private EGLContextFactory | mEGLContextFactory | private EGLWindowSurfaceFactory | mEGLWindowSurfaceFactory | private GLWrapper | mGLWrapper | private int | mDebugFlags | private int | mEGLContextClientVersion | private boolean | mPreserveEGLContextOnPause |
Constructors Summary |
---|
public GLSurfaceView(android.content.Context context)Standard View constructor. In order to render something, you
must call {@link #setRenderer} to register a renderer.
super(context);
init();
| public GLSurfaceView(android.content.Context context, android.util.AttributeSet attrs)Standard View constructor. In order to render something, you
must call {@link #setRenderer} to register a renderer.
super(context, attrs);
init();
|
Methods Summary |
---|
private void | checkRenderThreadState()
if (mGLThread != null) {
throw new IllegalStateException(
"setRenderer has already been called for this instance.");
}
| protected void | finalize()
try {
if (mGLThread != null) {
// GLThread may still be running if this view was never
// attached to a window.
mGLThread.requestExitAndWait();
}
} finally {
super.finalize();
}
| public int | getDebugFlags()Get the current value of the debug flags.
return mDebugFlags;
| public boolean | getPreserveEGLContextOnPause()
return mPreserveEGLContextOnPause;
| public int | getRenderMode()Get the current rendering mode. May be called
from any thread. Must not be called before a renderer has been set.
return mGLThread.getRenderMode();
| private void | init()
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment
// this statement if back-porting to 2.2 or older:
// holder.setFormat(PixelFormat.RGB_565);
//
// setType is not needed for SDK 2.0 or newer. Uncomment this
// statement if back-porting this code to older SDKs.
// holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
| protected void | onAttachedToWindow()This method is used as part of the View class and is not normally
called or subclassed by clients of GLSurfaceView.
super.onAttachedToWindow();
if (LOG_ATTACH_DETACH) {
Log.d(TAG, "onAttachedToWindow reattach =" + mDetached);
}
if (mDetached && (mRenderer != null)) {
int renderMode = RENDERMODE_CONTINUOUSLY;
if (mGLThread != null) {
renderMode = mGLThread.getRenderMode();
}
mGLThread = new GLThread(mThisWeakRef);
if (renderMode != RENDERMODE_CONTINUOUSLY) {
mGLThread.setRenderMode(renderMode);
}
mGLThread.start();
}
mDetached = false;
| protected void | onDetachedFromWindow()
if (LOG_ATTACH_DETACH) {
Log.d(TAG, "onDetachedFromWindow");
}
if (mGLThread != null) {
mGLThread.requestExitAndWait();
}
mDetached = true;
super.onDetachedFromWindow();
| public void | onPause()Inform the view that the activity is paused. The owner of this view must
call this method when the activity is paused. Calling this method will
pause the rendering thread.
Must not be called before a renderer has been set.
mGLThread.onPause();
| public void | onResume()Inform the view that the activity is resumed. The owner of this view must
call this method when the activity is resumed. Calling this method will
recreate the OpenGL display and resume the rendering
thread.
Must not be called before a renderer has been set.
mGLThread.onResume();
| public void | queueEvent(java.lang.Runnable r)Queue a runnable to be run on the GL rendering thread. This can be used
to communicate with the Renderer on the rendering thread.
Must not be called before a renderer has been set.
mGLThread.queueEvent(r);
| public void | requestRender()Request that the renderer render a frame.
This method is typically used when the render mode has been set to
{@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
May be called
from any thread. Must not be called before a renderer has been set.
mGLThread.requestRender();
| public void | setDebugFlags(int debugFlags)Set the debug flags to a new value. The value is
constructed by OR-together zero or more
of the DEBUG_CHECK_* constants. The debug flags take effect
whenever a surface is created. The default value is zero.
mDebugFlags = debugFlags;
| public void | setEGLConfigChooser(android.opengl.GLSurfaceView$EGLConfigChooser configChooser)Install a custom EGLConfigChooser.
If this method is
called, it must be called before {@link #setRenderer(Renderer)}
is called.
If no setEGLConfigChooser method is called, then by default the
view will choose an EGLConfig that is compatible with the current
android.view.Surface, with a depth buffer depth of
at least 16 bits.
checkRenderThreadState();
mEGLConfigChooser = configChooser;
| public void | setEGLConfigChooser(boolean needDepth)Install a config chooser which will choose a config
as close to 16-bit RGB as possible, with or without an optional depth
buffer as close to 16-bits as possible.
If this method is
called, it must be called before {@link #setRenderer(Renderer)}
is called.
If no setEGLConfigChooser method is called, then by default the
view will choose an RGB_888 surface with a depth buffer depth of
at least 16 bits.
setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
| public void | setEGLConfigChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize)Install a config chooser which will choose a config
with at least the specified depthSize and stencilSize,
and exactly the specified redSize, greenSize, blueSize and alphaSize.
If this method is
called, it must be called before {@link #setRenderer(Renderer)}
is called.
If no setEGLConfigChooser method is called, then by default the
view will choose an RGB_888 surface with a depth buffer depth of
at least 16 bits.
setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
blueSize, alphaSize, depthSize, stencilSize));
| public void | setEGLContextClientVersion(int version)Inform the default EGLContextFactory and default EGLConfigChooser
which EGLContext client version to pick.
Use this method to create an OpenGL ES 2.0-compatible context.
Example:
public MyView(Context context) {
super(context);
setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
setRenderer(new MyRenderer());
}
Note: Activities which require OpenGL ES 2.0 should indicate this by
setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
AndroidManifest.xml file.
If this method is called, it must be called before {@link #setRenderer(Renderer)}
is called.
This method only affects the behavior of the default EGLContexFactory and the
default EGLConfigChooser. If
{@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
If
{@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
checkRenderThreadState();
mEGLContextClientVersion = version;
| public void | setEGLContextFactory(android.opengl.GLSurfaceView$EGLContextFactory factory)Install a custom EGLContextFactory.
If this method is
called, it must be called before {@link #setRenderer(Renderer)}
is called.
If this method is not called, then by default
a context will be created with no shared context and
with a null attribute list.
checkRenderThreadState();
mEGLContextFactory = factory;
| public void | setEGLWindowSurfaceFactory(android.opengl.GLSurfaceView$EGLWindowSurfaceFactory factory)Install a custom EGLWindowSurfaceFactory.
If this method is
called, it must be called before {@link #setRenderer(Renderer)}
is called.
If this method is not called, then by default
a window surface will be created with a null attribute list.
checkRenderThreadState();
mEGLWindowSurfaceFactory = factory;
| public void | setGLWrapper(android.opengl.GLSurfaceView$GLWrapper glWrapper)Set the glWrapper. If the glWrapper is not null, its
{@link GLWrapper#wrap(GL)} method is called
whenever a surface is created. A GLWrapper can be used to wrap
the GL object that's passed to the renderer. Wrapping a GL
object enables examining and modifying the behavior of the
GL calls made by the renderer.
Wrapping is typically used for debugging purposes.
The default value is null.
mGLWrapper = glWrapper;
| public void | setPreserveEGLContextOnPause(boolean preserveOnPause)Control whether the EGL context is preserved when the GLSurfaceView is paused and
resumed.
If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.
Whether the EGL context is actually preserved or not depends upon whether the
Android device that the program is running on can support an arbitrary number of EGL
contexts or not. Devices that can only support a limited number of EGL contexts must
release the EGL context in order to allow multiple applications to share the GPU.
If set to false, the EGL context will be released when the GLSurfaceView is paused,
and recreated when the GLSurfaceView is resumed.
The default is false.
mPreserveEGLContextOnPause = preserveOnPause;
| public void | setRenderMode(int renderMode)Set the rendering mode. When renderMode is
RENDERMODE_CONTINUOUSLY, the renderer is called
repeatedly to re-render the scene. When renderMode
is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
by allowing the GPU and CPU to idle when the view does not need to be updated.
This method can only be called after {@link #setRenderer(Renderer)}
mGLThread.setRenderMode(renderMode);
| public void | setRenderer(android.opengl.GLSurfaceView$Renderer renderer)Set the renderer associated with this view. Also starts the thread that
will call the renderer, which in turn causes the rendering to start.
This method should be called once and only once in the life-cycle of
a GLSurfaceView.
The following GLSurfaceView methods can only be called before
setRenderer is called:
- {@link #setEGLConfigChooser(boolean)}
- {@link #setEGLConfigChooser(EGLConfigChooser)}
- {@link #setEGLConfigChooser(int, int, int, int, int, int)}
The following GLSurfaceView methods can only be called after
setRenderer is called:
- {@link #getRenderMode()}
- {@link #onPause()}
- {@link #onResume()}
- {@link #queueEvent(Runnable)}
- {@link #requestRender()}
- {@link #setRenderMode(int)}
checkRenderThreadState();
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
| public void | surfaceChanged(android.view.SurfaceHolder holder, int format, int w, int h)This method is part of the SurfaceHolder.Callback interface, and is
not normally called or subclassed by clients of GLSurfaceView.
mGLThread.onWindowResize(w, h);
| public void | surfaceCreated(android.view.SurfaceHolder holder)This method is part of the SurfaceHolder.Callback interface, and is
not normally called or subclassed by clients of GLSurfaceView.
mGLThread.surfaceCreated();
| public void | surfaceDestroyed(android.view.SurfaceHolder holder)This method is part of the SurfaceHolder.Callback interface, and is
not normally called or subclassed by clients of GLSurfaceView.
// Surface will be destroyed when we return
mGLThread.surfaceDestroyed();
|
|