Subversion Repositories AndroidProjects

Rev

Rev 150 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
149 chris 1
/*
2
 * Copyright (C) 2010 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
 
150 chris 17
package com.gebauz.Bauzoid.app;
149 chris 18
 
19
import java.io.Writer;
20
import java.util.ArrayList;
21
 
22
import javax.microedition.khronos.egl.EGL10;
23
import javax.microedition.khronos.egl.EGL11;
24
import javax.microedition.khronos.egl.EGLConfig;
25
import javax.microedition.khronos.egl.EGLContext;
26
import javax.microedition.khronos.egl.EGLDisplay;
27
import javax.microedition.khronos.egl.EGLSurface;
28
import javax.microedition.khronos.opengles.GL;
29
import javax.microedition.khronos.opengles.GL10;
30
 
31
import android.content.Context;
32
import android.content.pm.ConfigurationInfo;
33
import android.opengl.GLDebugHelper;
34
import android.util.AttributeSet;
35
import android.view.SurfaceHolder;
36
import android.view.SurfaceView;
37
 
38
/**
39
 * An implementation of SurfaceView that uses the dedicated surface for
40
 * displaying OpenGL rendering.
41
 * <p>
42
 * A GLSurfaceView provides the following features:
43
 * <p>
44
 * <ul>
45
 * <li>Manages a surface, which is a special piece of memory that can be
46
 * composited into the Android view system.
47
 * <li>Manages an EGL display, which enables OpenGL to render into a surface.
48
 * <li>Accepts a user-provided Renderer object that does the actual rendering.
49
 * <li>Renders on a dedicated thread to decouple rendering performance from the
50
 * UI thread.
51
 * <li>Supports both on-demand and continuous rendering.
52
 * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
53
 * </ul>
54
 *
55
 * <h3>Using GLSurfaceView</h3>
56
 * <p>
57
 * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
58
 * View system input event methods. If your application does not need to override event
59
 * methods then GLSurfaceView can be used as-is. For the most part
60
 * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
61
 * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
62
 * is registered with the GLSurfaceView
63
 * using the {@link #setRenderer(Renderer)} call.
64
 * <p>
65
 * <h3>Initializing GLSurfaceView</h3>
66
 * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
67
 * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
68
 * more of these methods before calling setRenderer:
69
 * <ul>
70
 * <li>{@link #setDebugFlags(int)}
71
 * <li>{@link #setEGLConfigChooser(boolean)}
72
 * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
73
 * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
74
 * <li>{@link #setGLWrapper(GLWrapper)}
75
 * </ul>
76
 * <p>
77
 * <h4>Choosing an EGL Configuration</h4>
78
 * A given Android device may support multiple possible types of drawing surfaces.
79
 * The available surfaces may differ in how may channels of data are present, as
80
 * well as how many bits are allocated to each channel. Therefore, the first thing
81
 * GLSurfaceView has to do when starting to render is choose what type of surface to use.
82
 * <p>
83
 * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface
84
 * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example,
85
 * if you do not need a depth buffer) you can override the default behavior by calling one of the
86
 * setEGLConfigChooser methods.
87
 * <p>
88
 * <h4>Debug Behavior</h4>
89
 * You can optionally modify the behavior of GLSurfaceView by calling
90
 * one or more of the debugging methods {@link #setDebugFlags(int)},
91
 * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
92
 * typically they are called before setRenderer so that they take effect immediately.
93
 * <p>
94
 * <h4>Setting a Renderer</h4>
95
 * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
96
 * The renderer is
97
 * responsible for doing the actual OpenGL rendering.
98
 * <p>
99
 * <h3>Rendering Mode</h3>
100
 * Once the renderer is set, you can control whether the renderer draws
101
 * continuously or on-demand by calling
102
 * {@link #setRenderMode}. The default is continuous rendering.
103
 * <p>
104
 * <h3>Activity Life-cycle</h3>
105
 * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
106
 * are required to call {@link #onPause()} when the activity pauses and
107
 * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
108
 * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
109
 * the OpenGL display.
110
 * <p>
111
 * <h3>Handling events</h3>
112
 * <p>
113
 * To handle an event you will typically subclass GLSurfaceView and override the
114
 * appropriate method, just as you would with any other View. However, when handling
115
 * the event, you may need to communicate with the Renderer object
116
 * that's running in the rendering thread. You can do this using any
117
 * standard Java cross-thread communication mechanism. In addition,
118
 * one relatively easy way to communicate with your renderer is
119
 * to call
120
 * {@link #queueEvent(Runnable)}. For example:
121
 * <pre class="prettyprint">
122
 * class MyGLSurfaceView extends GLSurfaceView {
123
 *
124
 *     private MyRenderer mMyRenderer;
125
 *
126
 *     public void start() {
127
 *         mMyRenderer = ...;
128
 *         setRenderer(mMyRenderer);
129
 *     }
130
 *
131
 *     public boolean onKeyDown(int keyCode, KeyEvent event) {
132
 *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
133
 *             queueEvent(new Runnable() {
134
 *                 // This method will be called on the rendering
135
 *                 // thread:
136
 *                 public void run() {
137
 *                     mMyRenderer.handleDpadCenter();
138
 *                 }});
139
 *             return true;
140
 *         }
141
 *         return super.onKeyDown(keyCode, event);
142
 *     }
143
 * }
144
 * </pre>
145
 *
146
 */
147
public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
148
    private final static boolean LOG_THREADS = false;
149
    private final static boolean LOG_SURFACE = true;
150
    private final static boolean LOG_RENDERER = false;
151
    // Work-around for bug 2263168
152
    private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
153
    /**
154
     * The renderer only renders
155
     * when the surface is created, or when {@link #requestRender} is called.
156
     *
157
     * @see #getRenderMode()
158
     * @see #setRenderMode(int)
159
     */
160
    public final static int RENDERMODE_WHEN_DIRTY = 0;
161
    /**
162
     * The renderer is called
163
     * continuously to re-render the scene.
164
     *
165
     * @see #getRenderMode()
166
     * @see #setRenderMode(int)
167
     * @see #requestRender()
168
     */
169
    public final static int RENDERMODE_CONTINUOUSLY = 1;
170
 
171
    /**
172
     * Check glError() after every GL call and throw an exception if glError indicates
173
     * that an error has occurred. This can be used to help track down which OpenGL ES call
174
     * is causing an error.
175
     *
176
     * @see #getDebugFlags
177
     * @see #setDebugFlags
178
     */
179
    public final static int DEBUG_CHECK_GL_ERROR = 1;
180
 
181
    /**
182
     * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
183
     *
184
     * @see #getDebugFlags
185
     * @see #setDebugFlags
186
     */
187
    public final static int DEBUG_LOG_GL_CALLS = 2;
188
 
189
    /**
190
     * Standard View constructor. In order to render something, you
191
     * must call {@link #setRenderer} to register a renderer.
192
     */
193
    public GLSurfaceView(Context context) {
194
        super(context);
195
        init();
196
    }
197
 
198
    /**
199
     * Standard View constructor. In order to render something, you
200
     * must call {@link #setRenderer} to register a renderer.
201
     */
202
    public GLSurfaceView(Context context, AttributeSet attrs) {
203
        super(context, attrs);
204
        init();
205
    }
206
 
207
    private void init() {
208
        // Install a SurfaceHolder.Callback so we get notified when the
209
        // underlying surface is created and destroyed
210
        SurfaceHolder holder = getHolder();
211
        holder.addCallback(this);
212
        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
213
 
214
    }
215
 
216
    /**
217
     * Set the glWrapper. If the glWrapper is not null, its
218
     * {@link GLWrapper#wrap(GL)} method is called
219
     * whenever a surface is created. A GLWrapper can be used to wrap
220
     * the GL object that's passed to the renderer. Wrapping a GL
221
     * object enables examining and modifying the behavior of the
222
     * GL calls made by the renderer.
223
     * <p>
224
     * Wrapping is typically used for debugging purposes.
225
     * <p>
226
     * The default value is null.
227
     * @param glWrapper the new GLWrapper
228
     */
229
    public void setGLWrapper(GLWrapper glWrapper) {
230
        mGLWrapper = glWrapper;
231
    }
232
 
233
    /**
234
     * Set the debug flags to a new value. The value is
235
     * constructed by OR-together zero or more
236
     * of the DEBUG_CHECK_* constants. The debug flags take effect
237
     * whenever a surface is created. The default value is zero.
238
     * @param debugFlags the new debug flags
239
     * @see #DEBUG_CHECK_GL_ERROR
240
     * @see #DEBUG_LOG_GL_CALLS
241
     */
242
    public void setDebugFlags(int debugFlags) {
243
        mDebugFlags = debugFlags;
244
    }
245
 
246
    /**
247
     * Get the current value of the debug flags.
248
     * @return the current value of the debug flags.
249
     */
250
    public int getDebugFlags() {
251
        return mDebugFlags;
252
    }
253
 
254
    /**
255
     * Set the renderer associated with this view. Also starts the thread that
256
     * will call the renderer, which in turn causes the rendering to start.
257
     * <p>This method should be called once and only once in the life-cycle of
258
     * a GLSurfaceView.
259
     * <p>The following GLSurfaceView methods can only be called <em>before</em>
260
     * setRenderer is called:
261
     * <ul>
262
     * <li>{@link #setEGLConfigChooser(boolean)}
263
     * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
264
     * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
265
     * </ul>
266
     * <p>
267
     * The following GLSurfaceView methods can only be called <em>after</em>
268
     * setRenderer is called:
269
     * <ul>
270
     * <li>{@link #getRenderMode()}
271
     * <li>{@link #onPause()}
272
     * <li>{@link #onResume()}
273
     * <li>{@link #queueEvent(Runnable)}
274
     * <li>{@link #requestRender()}
275
     * <li>{@link #setRenderMode(int)}
276
     * </ul>
277
     *
278
     * @param renderer the renderer to use to perform OpenGL drawing.
279
     */
280
    public void setRenderer(Renderer renderer) {
281
        checkRenderThreadState();
282
        if (mEGLConfigChooser == null) {
283
            mEGLConfigChooser = new SimpleEGLConfigChooser(true);
284
        }
285
        if (mEGLContextFactory == null) {
286
            mEGLContextFactory = new DefaultContextFactory();
287
        }
288
        if (mEGLWindowSurfaceFactory == null) {
289
            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
290
        }
291
        mGLThread = new GLThread(renderer);
292
        mGLThread.start();
293
    }
294
 
295
    /**
296
     * Install a custom EGLContextFactory.
297
     * <p>If this method is
298
     * called, it must be called before {@link #setRenderer(Renderer)}
299
     * is called.
300
     * <p>
301
     * If this method is not called, then by default
302
     * a context will be created with no shared context and
303
     * with a null attribute list.
304
     */
305
    public void setEGLContextFactory(EGLContextFactory factory) {
306
        checkRenderThreadState();
307
        mEGLContextFactory = factory;
308
    }
309
 
310
    /**
311
     * Install a custom EGLWindowSurfaceFactory.
312
     * <p>If this method is
313
     * called, it must be called before {@link #setRenderer(Renderer)}
314
     * is called.
315
     * <p>
316
     * If this method is not called, then by default
317
     * a window surface will be created with a null attribute list.
318
     */
319
    public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
320
        checkRenderThreadState();
321
        mEGLWindowSurfaceFactory = factory;
322
    }
323
 
324
    /**
325
     * Install a custom EGLConfigChooser.
326
     * <p>If this method is
327
     * called, it must be called before {@link #setRenderer(Renderer)}
328
     * is called.
329
     * <p>
330
     * If no setEGLConfigChooser method is called, then by default the
331
     * view will choose a config as close to 16-bit RGB as possible, with
332
     * a depth buffer as close to 16 bits as possible.
333
     * @param configChooser
334
     */
335
    public void setEGLConfigChooser(EGLConfigChooser configChooser) {
336
        checkRenderThreadState();
337
        mEGLConfigChooser = configChooser;
338
    }
339
 
340
    /**
341
     * Install a config chooser which will choose a config
342
     * as close to 16-bit RGB as possible, with or without an optional depth
343
     * buffer as close to 16-bits as possible.
344
     * <p>If this method is
345
     * called, it must be called before {@link #setRenderer(Renderer)}
346
     * is called.
347
     * <p>
348
      * If no setEGLConfigChooser method is called, then by default the
349
     * view will choose a config as close to 16-bit RGB as possible, with
350
     * a depth buffer as close to 16 bits as possible.
351
     *
352
     * @param needDepth
353
     */
354
    public void setEGLConfigChooser(boolean needDepth) {
355
        setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
356
    }
357
 
358
    /**
359
     * Install a config chooser which will choose a config
360
     * with at least the specified component sizes, and as close
361
     * to the specified component sizes as possible.
362
     * <p>If this method is
363
     * called, it must be called before {@link #setRenderer(Renderer)}
364
     * is called.
365
     * <p>
366
     * If no setEGLConfigChooser method is called, then by default the
367
     * view will choose a config as close to 16-bit RGB as possible, with
368
     * a depth buffer as close to 16 bits as possible.
369
     *
370
     */
371
    public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
372
            int alphaSize, int depthSize, int stencilSize) {
373
        setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
374
                blueSize, alphaSize, depthSize, stencilSize));
375
    }
376
 
377
    /**
378
     * Inform the default EGLContextFactory and default EGLConfigChooser
379
     * which EGLContext client version to pick.
380
     * <p>Use this method to create an OpenGL ES 2.0-compatible context.
381
     * Example:
382
     * <pre class="prettyprint">
383
     *     public MyView(Context context) {
384
     *         super(context);
385
     *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
386
     *         setRenderer(new MyRenderer());
387
     *     }
388
     * </pre>
389
     * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
390
     * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
391
     * AndroidManifest.xml file.
392
     * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
393
     * is called.
394
     * <p>This method only affects the behavior of the default EGLContexFactory and the
395
     * default EGLConfigChooser. If
396
     * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
397
     * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
398
     * If
399
     * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
400
     * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
401
     * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
402
     */
403
    public void setEGLContextClientVersion(int version) {
404
        checkRenderThreadState();
405
        mEGLContextClientVersion = version;
406
    }
407
 
408
    /**
409
     * Set the rendering mode. When renderMode is
410
     * RENDERMODE_CONTINUOUSLY, the renderer is called
411
     * repeatedly to re-render the scene. When renderMode
412
     * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
413
     * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
414
     * <p>
415
     * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
416
     * by allowing the GPU and CPU to idle when the view does not need to be updated.
417
     * <p>
418
     * This method can only be called after {@link #setRenderer(Renderer)}
419
     *
420
     * @param renderMode one of the RENDERMODE_X constants
421
     * @see #RENDERMODE_CONTINUOUSLY
422
     * @see #RENDERMODE_WHEN_DIRTY
423
     */
424
    public void setRenderMode(int renderMode) {
425
        mGLThread.setRenderMode(renderMode);
426
    }
427
 
428
    /**
429
     * Get the current rendering mode. May be called
430
     * from any thread. Must not be called before a renderer has been set.
431
     * @return the current rendering mode.
432
     * @see #RENDERMODE_CONTINUOUSLY
433
     * @see #RENDERMODE_WHEN_DIRTY
434
     */
435
    public int getRenderMode() {
436
        return mGLThread.getRenderMode();
437
    }
438
 
439
    /**
440
     * Request that the renderer render a frame.
441
     * This method is typically used when the render mode has been set to
442
     * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
443
     * May be called
444
     * from any thread. Must not be called before a renderer has been set.
445
     */
446
    public void requestRender() {
447
        mGLThread.requestRender();
448
    }
449
 
450
    /**
451
     * This method is part of the SurfaceHolder.Callback interface, and is
452
     * not normally called or subclassed by clients of GLSurfaceView.
453
     */
454
    public void surfaceCreated(SurfaceHolder holder) {
455
        mGLThread.surfaceCreated();
456
    }
457
 
458
    /**
459
     * This method is part of the SurfaceHolder.Callback interface, and is
460
     * not normally called or subclassed by clients of GLSurfaceView.
461
     */
462
    public void surfaceDestroyed(SurfaceHolder holder) {
463
        // Surface will be destroyed when we return
464
        mGLThread.surfaceDestroyed();
465
    }
466
 
467
    /**
468
     * This method is part of the SurfaceHolder.Callback interface, and is
469
     * not normally called or subclassed by clients of GLSurfaceView.
470
     */
471
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
472
        mGLThread.onWindowResize(w, h);
473
    }
474
 
475
    /**
476
     * Inform the view that the activity is paused. The owner of this view must
477
     * call this method when the activity is paused. Calling this method will
478
     * pause the rendering thread.
479
     * Must not be called before a renderer has been set.
480
     */
481
    public void onPause() {
482
        mGLThread.onPause();
483
    }
484
 
485
    /**
486
     * Inform the view that the activity is resumed. The owner of this view must
487
     * call this method when the activity is resumed. Calling this method will
488
     * recreate the OpenGL display and resume the rendering
489
     * thread.
490
     * Must not be called before a renderer has been set.
491
     */
492
    public void onResume() {
493
        mGLThread.onResume();
494
    }
495
 
496
/*    public void flushTextures(TextureLibrary library) {
497
        mGLThread.flushTextures(library);
498
    }
499
 
500
    public void loadTextures(TextureLibrary library) {
501
        mGLThread.loadTextures(library);
502
    }
503
 
504
    public void flushBuffers(BufferLibrary library) {
505
        mGLThread.flushBuffers(library);
506
    }
507
 
508
    public void loadBuffers(BufferLibrary library) {
509
        mGLThread.loadBuffers(library);
510
    }*/
511
 
512
    public void setSafeMode(boolean safeMode) {
513
        mGLThread.setSafeMode(safeMode);
514
    }
515
 
516
    /**
517
     * Queue a runnable to be run on the GL rendering thread. This can be used
518
     * to communicate with the Renderer on the rendering thread.
519
     * Must not be called before a renderer has been set.
520
     * @param r the runnable to be run on the GL rendering thread.
521
     */
522
    public void queueEvent(Runnable r) {
523
        mGLThread.queueEvent(r);
524
    }
525
 
526
    /**
527
     * Inform the view that the window focus has changed.
528
     */
529
    @Override
530
    public void onWindowFocusChanged(boolean hasFocus) {
531
        super.onWindowFocusChanged(hasFocus);
532
        mGLThread.onWindowFocusChanged(hasFocus);
533
    }
534
 
535
    /**
536
     * This method is used as part of the View class and is not normally
537
     * called or subclassed by clients of GLSurfaceView.
538
     * Must not be called before a renderer has been set.
539
     */
540
    @Override
541
    protected void onDetachedFromWindow() {
542
        super.onDetachedFromWindow();
543
        mGLThread.requestExitAndWait();
544
    }
545
 
546
    // ----------------------------------------------------------------------
547
 
548
    /**
549
     * An interface used to wrap a GL interface.
550
     * <p>Typically
551
     * used for implementing debugging and tracing on top of the default
552
     * GL interface. You would typically use this by creating your own class
553
     * that implemented all the GL methods by delegating to another GL instance.
554
     * Then you could add your own behavior before or after calling the
555
     * delegate. All the GLWrapper would do was instantiate and return the
556
     * wrapper GL instance:
557
     * <pre class="prettyprint">
558
     * class MyGLWrapper implements GLWrapper {
559
     *     GL wrap(GL gl) {
560
     *         return new MyGLImplementation(gl);
561
     *     }
562
     *     static class MyGLImplementation implements GL,GL10,GL11,... {
563
     *         ...
564
     *     }
565
     * }
566
     * </pre>
567
     * @see #setGLWrapper(GLWrapper)
568
     */
569
    public interface GLWrapper {
570
        /**
571
         * Wraps a gl interface in another gl interface.
572
         * @param gl a GL interface that is to be wrapped.
573
         * @return either the input argument or another GL object that wraps the input argument.
574
         */
575
        GL wrap(GL gl);
576
    }
577
 
578
    /**
579
     * A generic renderer interface.
580
     * <p>
581
     * The renderer is responsible for making OpenGL calls to render a frame.
582
     * <p>
583
     * GLSurfaceView clients typically create their own classes that implement
584
     * this interface, and then call {@link GLSurfaceView#setRenderer} to
585
     * register the renderer with the GLSurfaceView.
586
     * <p>
587
     * <h3>Threading</h3>
588
     * The renderer will be called on a separate thread, so that rendering
589
     * performance is decoupled from the UI thread. Clients typically need to
590
     * communicate with the renderer from the UI thread, because that's where
591
     * input events are received. Clients can communicate using any of the
592
     * standard Java techniques for cross-thread communication, or they can
593
     * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
594
     * <p>
595
     * <h3>EGL Context Lost</h3>
596
     * There are situations where the EGL rendering context will be lost. This
597
     * typically happens when device wakes up after going to sleep. When
598
     * the EGL context is lost, all OpenGL resources (such as textures) that are
599
     * associated with that context will be automatically deleted. In order to
600
     * keep rendering correctly, a renderer must recreate any lost resources
601
     * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
602
     * is a convenient place to do this.
603
     *
604
     *
605
     * @see #setRenderer(Renderer)
606
     */
607
    public interface Renderer {
608
        /**
609
         * Called when the surface is created or recreated.
610
         * <p>
611
         * Called when the rendering thread
612
         * starts and whenever the EGL context is lost. The context will typically
613
         * be lost when the Android device awakes after going to sleep.
614
         * <p>
615
         * Since this method is called at the beginning of rendering, as well as
616
         * every time the EGL context is lost, this method is a convenient place to put
617
         * code to create resources that need to be created when the rendering
618
         * starts, and that need to be recreated when the EGL context is lost.
619
         * Textures are an example of a resource that you might want to create
620
         * here.
621
         * <p>
622
         * Note that when the EGL context is lost, all OpenGL resources associated
623
         * with that context will be automatically deleted. You do not need to call
624
         * the corresponding "glDelete" methods such as glDeleteTextures to
625
         * manually delete these lost resources.
626
         * <p>
627
         * @param gl the GL interface. Use <code>instanceof</code> to
628
         * test if the interface supports GL11 or higher interfaces.
629
         * @param config the EGLConfig of the created surface. Can be used
630
         * to create matching pbuffers.
631
         */
632
        void onSurfaceCreated(GL10 gl, EGLConfig config);
633
 
634
        /**
635
         * Called when the surface changed size.
636
         * <p>
637
         * Called after the surface is created and whenever
638
         * the OpenGL ES surface size changes.
639
         * <p>
640
         * Typically you will set your viewport here. If your camera
641
         * is fixed then you could also set your projection matrix here:
642
         * <pre class="prettyprint">
643
         * void onSurfaceChanged(GL10 gl, int width, int height) {
644
         *     gl.glViewport(0, 0, width, height);
645
         *     // for a fixed camera, set the projection too
646
         *     float ratio = (float) width / height;
647
         *     gl.glMatrixMode(GL10.GL_PROJECTION);
648
         *     gl.glLoadIdentity();
649
         *     gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
650
         * }
651
         * </pre>
652
         * @param gl the GL interface. Use <code>instanceof</code> to
653
         * test if the interface supports GL11 or higher interfaces.
654
         * @param width
655
         * @param height
656
         */
657
        void onSurfaceChanged(GL10 gl, int width, int height);
658
 
659
        /**
660
         * Called when the OpenGL context has been lost is about
661
         * to be recreated.  onSurfaceCreated() will be called after
662
         * onSurfaceLost().
663
         * */
664
        void onSurfaceLost();
665
 
666
        /**
667
         * Called to draw the current frame.
668
         * <p>
669
         * This method is responsible for drawing the current frame.
670
         * <p>
671
         * The implementation of this method typically looks like this:
672
         * <pre class="prettyprint">
673
         * void onDrawFrame(GL10 gl) {
674
         *     gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
675
         *     //... other gl calls to render the scene ...
676
         * }
677
         * </pre>
678
         * @param gl the GL interface. Use <code>instanceof</code> to
679
         * test if the interface supports GL11 or higher interfaces.
680
         */
681
        void onDrawFrame(GL10 gl);
682
 
683
/*              void loadTextures(GL10 gl, TextureLibrary library);
684
        void flushTextures(GL10 gl, TextureLibrary library);
685
        void loadBuffers(GL10 gl, BufferLibrary library);
686
        void flushBuffers(GL10 gl, BufferLibrary library);
687
 
688
                */
689
 
690
    }
691
 
692
    /**
693
     * An interface for customizing the eglCreateContext and eglDestroyContext calls.
694
     * <p>
695
     * This interface must be implemented by clients wishing to call
696
     * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
697
     */
698
    public interface EGLContextFactory {
699
        EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
700
        void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
701
    }
702
 
703
    private class DefaultContextFactory implements EGLContextFactory {
704
        private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
705
 
706
        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
707
            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
708
                    EGL10.EGL_NONE };
709
 
710
            return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
711
                    mEGLContextClientVersion != 0 ? attrib_list : null);
712
        }
713
 
714
        public void destroyContext(EGL10 egl, EGLDisplay display,
715
                EGLContext context) {
716
            egl.eglDestroyContext(display, context);
717
        }
718
    }
719
 
720
    /**
721
     * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
722
     * <p>
723
     * This interface must be implemented by clients wishing to call
724
     * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
725
     */
726
    public interface EGLWindowSurfaceFactory {
727
        EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
728
                Object nativeWindow);
729
        void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
730
    }
731
 
732
    private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
733
 
734
        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
735
                EGLConfig config, Object nativeWindow) {
736
            return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
737
        }
738
 
739
        public void destroySurface(EGL10 egl, EGLDisplay display,
740
                EGLSurface surface) {
741
            egl.eglDestroySurface(display, surface);
742
        }
743
    }
744
 
745
    /**
746
     * An interface for choosing an EGLConfig configuration from a list of
747
     * potential configurations.
748
     * <p>
749
     * This interface must be implemented by clients wishing to call
750
     * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
751
     */
752
    public interface EGLConfigChooser {
753
        /**
754
         * Choose a configuration from the list. Implementors typically
755
         * implement this method by calling
756
         * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
757
         * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
758
         * @param egl the EGL10 for the current display.
759
         * @param display the current display.
760
         * @return the chosen configuration.
761
         */
762
        EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
763
    }
764
 
765
    private abstract class BaseConfigChooser
766
            implements EGLConfigChooser {
767
        public BaseConfigChooser(int[] configSpec) {
768
            mConfigSpec = filterConfigSpec(configSpec);
769
        }
770
 
771
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
772
            int[] num_config = new int[1];
773
            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
774
                    num_config)) {
775
                throw new IllegalArgumentException("eglChooseConfig failed");
776
            }
777
 
778
            int numConfigs = num_config[0];
779
 
780
            if (numConfigs <= 0) {
781
                throw new IllegalArgumentException(
782
                        "No configs match configSpec");
783
            }
784
 
785
            EGLConfig[] configs = new EGLConfig[numConfigs];
786
            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
787
                    num_config)) {
788
                throw new IllegalArgumentException("eglChooseConfig#2 failed");
789
            }
790
            EGLConfig config = chooseConfig(egl, display, configs);
791
            if (config == null) {
792
                throw new IllegalArgumentException("No config chosen");
793
            }
794
            return config;
795
        }
796
 
797
        abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
798
                EGLConfig[] configs);
799
 
800
        protected int[] mConfigSpec;
801
 
802
        private int[] filterConfigSpec(int[] configSpec) {
803
            if (mEGLContextClientVersion != 2) {
804
                return configSpec;
805
            }
806
            /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
807
             * And we know the configSpec is well formed.
808
             */
809
            int len = configSpec.length;
810
            int[] newConfigSpec = new int[len + 2];
811
            System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
812
            newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
813
            newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
814
            newConfigSpec[len+1] = EGL10.EGL_NONE;
815
            return newConfigSpec;
816
        }
817
    }
818
 
819
    private class ComponentSizeChooser extends BaseConfigChooser {
820
        public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
821
                int alphaSize, int depthSize, int stencilSize) {
822
            super(new int[] {
823
                    EGL10.EGL_RED_SIZE, redSize,
824
                    EGL10.EGL_GREEN_SIZE, greenSize,
825
                    EGL10.EGL_BLUE_SIZE, blueSize,
826
                    EGL10.EGL_ALPHA_SIZE, alphaSize,
827
                    EGL10.EGL_DEPTH_SIZE, depthSize,
828
                    EGL10.EGL_STENCIL_SIZE, stencilSize,
829
                    EGL10.EGL_NONE});
830
            mValue = new int[1];
831
            mRedSize = redSize;
832
            mGreenSize = greenSize;
833
            mBlueSize = blueSize;
834
            mAlphaSize = alphaSize;
835
            mDepthSize = depthSize;
836
            mStencilSize = stencilSize;
837
       }
838
 
839
        @Override
840
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
841
                EGLConfig[] configs) {
842
            EGLConfig closestConfig = null;
843
            int closestDistance = 1000;
844
            for(EGLConfig config : configs) {
845
                int d = findConfigAttrib(egl, display, config,
846
                        EGL10.EGL_DEPTH_SIZE, 0);
847
                int s = findConfigAttrib(egl, display, config,
848
                        EGL10.EGL_STENCIL_SIZE, 0);
849
                if (d >= mDepthSize && s>= mStencilSize) {
850
                    int r = findConfigAttrib(egl, display, config,
851
                            EGL10.EGL_RED_SIZE, 0);
852
                    int g = findConfigAttrib(egl, display, config,
853
                             EGL10.EGL_GREEN_SIZE, 0);
854
                    int b = findConfigAttrib(egl, display, config,
855
                              EGL10.EGL_BLUE_SIZE, 0);
856
                    int a = findConfigAttrib(egl, display, config,
857
                            EGL10.EGL_ALPHA_SIZE, 0);
858
                    int distance = Math.abs(r - mRedSize)
859
                                + Math.abs(g - mGreenSize)
860
                                + Math.abs(b - mBlueSize)
861
                                + Math.abs(a - mAlphaSize);
862
                    if (distance < closestDistance) {
863
                        closestDistance = distance;
864
                        closestConfig = config;
865
                    }
866
                }
867
            }
868
            return closestConfig;
869
        }
870
 
871
        private int findConfigAttrib(EGL10 egl, EGLDisplay display,
872
                EGLConfig config, int attribute, int defaultValue) {
873
 
874
            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
875
                return mValue[0];
876
            }
877
            return defaultValue;
878
        }
879
 
880
        private int[] mValue;
881
        // Subclasses can adjust these values:
882
        protected int mRedSize;
883
        protected int mGreenSize;
884
        protected int mBlueSize;
885
        protected int mAlphaSize;
886
        protected int mDepthSize;
887
        protected int mStencilSize;
888
        }
889
 
890
    /**
891
     * This class will choose a supported surface as close to
892
     * RGB565 as possible, with or without a depth buffer.
893
     *
894
     */
895
    private class SimpleEGLConfigChooser extends ComponentSizeChooser {
896
        public SimpleEGLConfigChooser(boolean withDepthBuffer) {
897
            super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
898
            // Adjust target values. This way we'll accept a 4444 or
899
            // 555 buffer if there's no 565 buffer available.
900
            mRedSize = 5;
901
            mGreenSize = 6;
902
            mBlueSize = 5;
903
        }
904
    }
905
 
906
    /**
907
     * An EGL helper class.
908
     */
909
 
910
    private class EglHelper {
911
        public EglHelper() {
912
 
913
        }
914
 
915
        /**
916
         * Initialize EGL for a given configuration spec.
917
         * @param configSpec
918
         */
919
        public void start(){
920
            /*
921
             * Get an EGL instance
922
             */
923
            mEgl = (EGL10) EGLContext.getEGL();
924
 
925
            /*
926
             * Get to the default display.
927
             */
928
            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
929
 
930
            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
931
                throw new RuntimeException("eglGetDisplay failed");
932
            }
933
 
934
            /*
935
             * We can now initialize EGL for that display
936
             */
937
            int[] version = new int[2];
938
            if(!mEgl.eglInitialize(mEglDisplay, version)) {
939
                throw new RuntimeException("eglInitialize failed");
940
            }
941
            mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
942
 
943
            /*
944
            * Create an OpenGL ES context. This must be done only once, an
945
            * OpenGL context is a somewhat heavy object.
946
            */
947
            mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
948
            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
949
                throwEglException("createContext");
950
            }
951
 
952
            mEglSurface = null;
953
        }
954
 
955
        /*
956
         * React to the creation of a new surface by creating and returning an
957
         * OpenGL interface that renders to that surface.
958
         */
959
        public GL createSurface(SurfaceHolder holder) {
960
            /*
961
             *  The window size has changed, so we need to create a new
962
             *  surface.
963
             */
964
            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
965
 
966
                /*
967
                 * Unbind and destroy the old EGL surface, if
968
                 * there is one.
969
                 */
970
                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
971
                        EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
972
                mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
973
            }
974
 
975
            /*
976
             * Create an EGL surface we can render into.
977
             */
978
            mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
979
                    mEglDisplay, mEglConfig, holder);
980
 
981
            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
982
                throwEglException("createWindowSurface");
983
            }
984
 
985
            /*
986
             * Before we can issue GL commands, we need to make sure
987
             * the context is current and bound to a surface.
988
             */
989
            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
990
                throwEglException("eglMakeCurrent");
991
            }
992
 
993
            GL gl = mEglContext.getGL();
994
            if (mGLWrapper != null) {
995
                gl = mGLWrapper.wrap(gl);
996
            }
997
 
998
            if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
999
                int configFlags = 0;
1000
                Writer log = null;
1001
                if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
1002
                    configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
1003
                }
1004
                if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
1005
                    log = new LogWriter();
1006
                }
1007
                gl = GLDebugHelper.wrap(gl, configFlags, log);
1008
            }
1009
            return gl;
1010
        }
1011
 
1012
        /**
1013
         * Display the current render surface.
1014
         * @return false if the context has been lost.
1015
         */
1016
        public boolean swap() {
1017
            mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
1018
 
1019
            /*
1020
             * Always check for EGL_CONTEXT_LOST, which means the context
1021
             * and all associated data were lost (For instance because
1022
             * the device went to sleep). We need to sleep until we
1023
             * get a new surface.
1024
             */
1025
            return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
1026
        }
1027
 
1028
        public void destroySurface() {
1029
            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
1030
                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
1031
                        EGL10.EGL_NO_SURFACE,
1032
                        EGL10.EGL_NO_CONTEXT);
1033
                mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
1034
                mEglSurface = null;
1035
            }
1036
        }
1037
 
1038
        public void finish() {
1039
            if (mEglContext != null) {
1040
                mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
1041
                mEglContext = null;
1042
            }
1043
            if (mEglDisplay != null) {
1044
                mEgl.eglTerminate(mEglDisplay);
1045
                mEglDisplay = null;
1046
            }
1047
        }
1048
 
1049
        private void throwEglException(String function) {
1050
            throw new RuntimeException(function + " failed: " + mEgl.eglGetError());
1051
        }
1052
 
1053
        /** Checks to see if the current context is valid.  **/
1054
        public boolean verifyContext() {
1055
                EGLContext currentContext = mEgl.eglGetCurrentContext();
1056
                        return currentContext != EGL10.EGL_NO_CONTEXT && mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
1057
                }
1058
 
1059
        EGL10 mEgl;
1060
        EGLDisplay mEglDisplay;
1061
        EGLSurface mEglSurface;
1062
        EGLConfig mEglConfig;
1063
        EGLContext mEglContext;
1064
 
1065
    }
1066
 
1067
    /**
1068
     * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
1069
     * to a Renderer instance to do the actual drawing. Can be configured to
1070
     * render continuously or on request.
1071
     *
1072
     * All potentially blocking synchronization is done through the
1073
     * sGLThreadManager object. This avoids multiple-lock ordering issues.
1074
     *
1075
     */
1076
    private class GLThread extends Thread {
1077
                public GLThread(Renderer renderer) {
1078
            super();
1079
            mWidth = 0;
1080
            mHeight = 0;
1081
            mRequestRender = true;
1082
            mRenderMode = RENDERMODE_CONTINUOUSLY;
1083
            mRenderer = renderer;
1084
        }
1085
 
1086
 
1087
                @Override
1088
        public void run() {
1089
            setName("GLThread " + getId());
1090
            if (LOG_THREADS) {
1091
                //DebugLog.i("GLThread", "starting tid=" + getId());
1092
            }
1093
 
1094
            try {
1095
                guardedRun();
1096
            } catch (InterruptedException e) {
1097
                // fall thru and exit normally
1098
            } finally {
1099
                sGLThreadManager.threadExiting(this);
1100
            }
1101
        }
1102
 
1103
        /*
1104
         * This private method should only be called inside a
1105
         * synchronized(sGLThreadManager) block.
1106
         */
1107
        private void stopEglLocked() {
1108
            if (mHaveEglSurface) {
1109
                mHaveEglSurface = false;
1110
                mEglHelper.destroySurface();
1111
                sGLThreadManager.releaseEglSurfaceLocked(this);
1112
            }
1113
        }
1114
 
1115
        private void guardedRun() throws InterruptedException {
1116
            mEglHelper = new EglHelper();
1117
            mHaveEglContext = false;
1118
            mHaveEglSurface = false;
1119
            try {
1120
                GL10 gl = null;
1121
                boolean createEglSurface = false;
1122
                boolean sizeChanged = false;
1123
                boolean wantRenderNotification = false;
1124
                boolean doRenderNotification = false;
1125
                int w = 0;
1126
                int h = 0;
1127
                Runnable event = null;
1128
                int framesSinceResetHack = 0;
1129
                while (true) {
1130
                    synchronized (sGLThreadManager) {
1131
                        while (true) {
1132
                            if (mShouldExit) {
1133
                                return;
1134
                            }
1135
 
1136
                            if (! mEventQueue.isEmpty()) {
1137
                                event = mEventQueue.remove(0);
1138
                                break;
1139
                            }
1140
 
1141
                            // Do we need to release the EGL surface?
1142
                            if (mHaveEglSurface && mPaused) {
1143
                                if (LOG_SURFACE) {
1144
                                    //DebugLog.i("GLThread", "releasing EGL surface because paused tid=" + getId());
1145
                                }
1146
                                stopEglLocked();
1147
                            }
1148
 
1149
                            // Have we lost the surface view surface?
1150
                            if ((! mHasSurface) && (! mWaitingForSurface)) {
1151
                                if (LOG_SURFACE) {
1152
                                    //DebugLog.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
1153
                                }
1154
                                if (mHaveEglSurface) {
1155
                                    stopEglLocked();
1156
                                }
1157
                                mWaitingForSurface = true;
1158
                                sGLThreadManager.notifyAll();
1159
                            }
1160
 
1161
                            // Have we acquired the surface view surface?
1162
                            if (mHasSurface && mWaitingForSurface) {
1163
                                if (LOG_SURFACE) {
1164
                                    //DebugLog.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
1165
                                }
1166
                                mWaitingForSurface = false;
1167
                                sGLThreadManager.notifyAll();
1168
                            }
1169
 
1170
                            if (doRenderNotification) {
1171
                                wantRenderNotification = false;
1172
                                doRenderNotification = false;
1173
                                mRenderComplete = true;
1174
                                sGLThreadManager.notifyAll();
1175
                            }
1176
 
1177
                            // Ready to draw?
1178
                            if ((!mPaused) && mHasSurface
1179
                                && (mWidth > 0) && (mHeight > 0)
1180
                                && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
1181
 
1182
                                if (mHaveEglContext && !mHaveEglSurface) {
1183
                                        // Let's make sure the context hasn't been lost.
1184
                                        if (!mEglHelper.verifyContext()) {
1185
                                                mEglHelper.finish();
1186
                                                mRenderer.onSurfaceLost();
1187
                                                mHaveEglContext = false;
1188
                                        }
1189
                                }
1190
                                // If we don't have an egl surface, try to acquire one.
1191
                                if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
1192
                                        mHaveEglContext = true;
1193
                                    mEglHelper.start();
1194
 
1195
                                    sGLThreadManager.notifyAll();
1196
                                }
1197
 
1198
                                if (mHaveEglContext && !mHaveEglSurface) {
1199
                                        mHaveEglSurface = true;
1200
                                        createEglSurface = true;
1201
                                    sizeChanged = true;
1202
                                }
1203
 
1204
                                if (mHaveEglSurface) {
1205
                                    if (mSizeChanged) {
1206
                                        sizeChanged = true;
1207
                                        w = mWidth;
1208
                                        h = mHeight;
1209
                                        wantRenderNotification = true;
1210
 
1211
                                        if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
1212
                                            // We keep mRequestRender true so that we draw twice after the size changes.
1213
                                            // (Once because of mSizeChanged, the second time because of mRequestRender.)
1214
                                            // This forces the updated graphics onto the screen.
1215
                                        } else {
1216
                                            mRequestRender = false;
1217
                                        }
1218
                                        mSizeChanged = false;
1219
                                    } else {
1220
                                        mRequestRender = false;
1221
                                    }
1222
                                    sGLThreadManager.notifyAll();
1223
                                    break;
1224
                                }
1225
                            }
1226
 
1227
                            // By design, this is the only place in a GLThread thread where we wait().
1228
                            if (LOG_THREADS) {
1229
                                //DebugLog.i("GLThread", "waiting tid=" + getId());
1230
                            }
1231
                            sGLThreadManager.wait();
1232
                        }
1233
                    } // end of synchronized(sGLThreadManager)
1234
 
1235
                    if (event != null) {
1236
                        event.run();
1237
                        event = null;
1238
                        continue;
1239
                    }
1240
 
1241
                    if (mHasFocus) {
1242
                            if (createEglSurface) {
1243
                                gl = (GL10) mEglHelper.createSurface(getHolder());
1244
                                sGLThreadManager.checkGLDriver(gl);
1245
                                if (LOG_RENDERER) {
1246
                                   // DebugLog.w("GLThread", "onSurfaceCreated");
1247
                                }
1248
                                mGL = gl;
1249
                                mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
1250
                                createEglSurface = false;
1251
                                framesSinceResetHack = 0;
1252
                            }
1253
 
1254
 
1255
                            if (sizeChanged) {
1256
                                if (LOG_RENDERER) {
1257
                                  //  DebugLog.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
1258
                                }
1259
                                mRenderer.onSurfaceChanged(gl, w, h);
1260
                                sizeChanged = false;
1261
                            }
1262
 
1263
 
1264
 
1265
                            if (LOG_RENDERER) {
1266
                              //  DebugLog.w("GLThread", "onDrawFrame");
1267
                            }
1268
 
1269
                            // Some phones (Motorola Cliq, Backflip; also the 
1270
                            // Huawei Pulse, and maybe the Samsung Behold II), use a
1271
                            // broken graphics driver from Qualcomm.  It fails in a
1272
                            // very specific case: when the EGL context is lost due to
1273
                            // resource constraints, and then recreated, if GL commands
1274
                            // are sent within two frames of the surface being created
1275
                            // then eglSwapBuffers() will hang.  Normally, applications using
1276
                            // the stock GLSurfaceView never run into this problem because it
1277
                            // discards the EGL context explicitly on every pause.  But
1278
                            // I've modified this class to not do that--I only want to reload
1279
                            // textures when the context is actually lost--so this bug
1280
                            // revealed itself as black screens on devices like the Cliq.
1281
                            // Thus, in "safe mode," I force two swaps to occur before 
1282
                            // issuing any GL commands.  Don't ask me how long it took
1283
                            // to figure this out.
1284
                            if (framesSinceResetHack > 1 || !mSafeMode) {
1285
                                mRenderer.onDrawFrame(gl);
1286
                            } else {
1287
                                //DebugLog.w("GLThread", "Safe Mode Wait...");
1288
                            }
1289
 
1290
                            framesSinceResetHack++;
1291
 
1292
                            if(!mEglHelper.swap()) {
1293
                                if (LOG_SURFACE) {
1294
                                   // DebugLog.i("GLThread", "egl surface lost tid=" + getId());
1295
                                }
1296
 
1297
                                stopEglLocked();
1298
                            }
1299
 
1300
                    }
1301
                        if (wantRenderNotification) {
1302
                            doRenderNotification = true;
1303
                        }
1304
                }
1305
 
1306
            } finally {
1307
                mGL = null;
1308
                /*
1309
                 * clean-up everything...
1310
                 */
1311
                synchronized (sGLThreadManager) {
1312
                    stopEglLocked();
1313
                    mEglHelper.finish();
1314
                }
1315
            }
1316
        }
1317
 
1318
        public void setRenderMode(int renderMode)
1319
        {
1320
            if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) )
1321
            {
1322
                throw new IllegalArgumentException("renderMode");
1323
            }
1324
            synchronized(sGLThreadManager) {
1325
                mRenderMode = renderMode;
1326
                sGLThreadManager.notifyAll();
1327
            }
1328
        }
1329
 
1330
        public int getRenderMode() {
1331
            synchronized(sGLThreadManager) {
1332
                return mRenderMode;
1333
            }
1334
        }
1335
 
1336
        public void requestRender() {
1337
            synchronized(sGLThreadManager) {
1338
                mRequestRender = true;
1339
                sGLThreadManager.notifyAll();
1340
            }
1341
        }
1342
 
1343
        public void surfaceCreated() {
1344
            synchronized(sGLThreadManager) {
1345
                if (LOG_THREADS) {
1346
                  //  DebugLog.i("GLThread", "surfaceCreated tid=" + getId());
1347
                }
1348
                mHasSurface = true;
1349
                sGLThreadManager.notifyAll();
1350
            }
1351
        }
1352
 
1353
        public void surfaceDestroyed() {
1354
            synchronized(sGLThreadManager) {
1355
                if (LOG_THREADS) {
1356
                  // DebugLog.i("GLThread", "surfaceDestroyed tid=" + getId());
1357
                }
1358
                mHasSurface = false;
1359
                sGLThreadManager.notifyAll();
1360
                while((!mWaitingForSurface) && (!mExited)) {
1361
                    try {
1362
                        sGLThreadManager.wait();
1363
                    } catch (InterruptedException e) {
1364
                        Thread.currentThread().interrupt();
1365
                    }
1366
                }
1367
            }
1368
        }
1369
 
1370
        public void onPause() {
1371
            synchronized (sGLThreadManager) {
1372
                mPaused = true;
1373
                sGLThreadManager.notifyAll();
1374
            }
1375
        }
1376
 
1377
        public void onResume() {
1378
            synchronized (sGLThreadManager) {
1379
                mPaused = false;
1380
                mRequestRender = true;
1381
                sGLThreadManager.notifyAll();
1382
            }
1383
        }
1384
 
1385
        public void onWindowResize(int w, int h) {
1386
            synchronized (sGLThreadManager) {
1387
                mWidth = w;
1388
                mHeight = h;
1389
                mSizeChanged = true;
1390
                mRequestRender = true;
1391
                mRenderComplete = false;
1392
                sGLThreadManager.notifyAll();
1393
 
1394
                // Wait for thread to react to resize and render a frame
1395
                while (! mExited && !mPaused && !mRenderComplete ) {
1396
                    if (LOG_SURFACE) {
1397
                      //  DebugLog.i("Main thread", "onWindowResize waiting for render complete.");
1398
                    }
1399
                    try {
1400
                        sGLThreadManager.wait();
1401
                    } catch (InterruptedException ex) {
1402
                        Thread.currentThread().interrupt();
1403
                    }
1404
                }
1405
            }
1406
        }
1407
/*
1408
                public void loadTextures(TextureLibrary library) {
1409
            synchronized (this) {
1410
                assert mGL != null;
1411
                if (mGL != null && mHasSurface) {
1412
                    mRenderer.loadTextures(mGL, library);
1413
                }
1414
            }
1415
        }
1416
 
1417
        public void flushTextures(TextureLibrary library) {
1418
            synchronized (this) {
1419
                assert mGL != null;
1420
                if (mGL != null) {
1421
                    mRenderer.flushTextures(mGL, library);
1422
                }
1423
            }
1424
        }
1425
 
1426
        public void loadBuffers(BufferLibrary library) {
1427
            synchronized (this) {
1428
                assert mGL != null;
1429
                if (mGL != null) {
1430
                    mRenderer.loadBuffers(mGL, library);
1431
                }
1432
            }
1433
        }
1434
 
1435
        public void flushBuffers(BufferLibrary library) {
1436
            synchronized (this) {
1437
                assert mGL != null;
1438
                if (mGL != null) {
1439
                    mRenderer.flushBuffers(mGL, library);
1440
                }
1441
            }
1442
        }*/
1443
 
1444
        // On some Qualcomm devices (such as the HTC Magic running Android 1.6),
1445
        // there's a bug in the graphics driver that will cause glViewport() to
1446
        // do the wrong thing in a very specific situation.  When the screen is
1447
        // rotated, if a surface is created in one layout (say, portrait view)
1448
        // and then rotated to another, subsequent calls to glViewport are clipped. 
1449
        // So, if the window is, say, 320x480 when the surface is created, and 
1450
        // then the rotation occurs and glViewport() is called with the new 
1451
        // size of 480x320, devices with the buggy driver will clip the viewport 
1452
        // to the old width (which means 320x320...ugh!).  This is fixed in
1453
        // Android 2.1 Qualcomm devices (like Nexus One) and doesn't affect
1454
        // non-Qualcomm devices (like the Motorola DROID).
1455
        //
1456
        // Unfortunately, under Android 1.6 this exact case occurs when the 
1457
        // screen is put to sleep and then wakes up again.  The lock screen
1458
        // comes up in portrait mode, but at the same time the window surface
1459
        // is also created in the backgrounded game.  When the lock screen is closed
1460
        // and the game comes forward, the window is fixed to the correct size
1461
        // which causes the bug to occur.
1462
 
1463
        // The solution used here is to simply never render when the window surface
1464
        // does not have the focus.  When the lock screen (or menu) is up, rendering
1465
        // will stop.  This resolves the driver bug (as the egl surface won't be created
1466
        // until after the screen size has been fixed), and is generally good practice
1467
        // since you don't want to be doing a lot of CPU intensive work when the lock
1468
        // screen is up (to preserve battery life).
1469
 
1470
        public void onWindowFocusChanged(boolean hasFocus) {
1471
                synchronized(sGLThreadManager) {
1472
                        mHasFocus = hasFocus;
1473
                sGLThreadManager.notifyAll();
1474
                }
1475
                if (LOG_SURFACE) {
1476
                //DebugLog.i("Main thread", "Focus " + (mHasFocus ? "gained" : "lost"));
1477
            }
1478
 
1479
                }
1480
 
1481
        public void requestExitAndWait() {
1482
            // don't call this from GLThread thread or it is a guaranteed
1483
            // deadlock!
1484
            synchronized(sGLThreadManager) {
1485
                mShouldExit = true;
1486
                sGLThreadManager.notifyAll();
1487
                while (! mExited) {
1488
                    try {
1489
                        sGLThreadManager.wait();
1490
                    } catch (InterruptedException ex) {
1491
                        Thread.currentThread().interrupt();
1492
                    }
1493
                }
1494
            }
1495
        }
1496
 
1497
        /**
1498
         * Queue an "event" to be run on the GL rendering thread.
1499
         * @param r the runnable to be run on the GL rendering thread.
1500
         */
1501
        public void queueEvent(Runnable r) {
1502
            if (r == null) {
1503
                throw new IllegalArgumentException("r must not be null");
1504
            }
1505
            synchronized(sGLThreadManager) {
1506
                mEventQueue.add(r);
1507
                sGLThreadManager.notifyAll();
1508
            }
1509
        }
1510
 
1511
        public void setSafeMode(boolean on) {
1512
                mSafeMode = on;
1513
        }
1514
 
1515
        // Once the thread is started, all accesses to the following member
1516
        // variables are protected by the sGLThreadManager monitor
1517
        private boolean mShouldExit;
1518
        private boolean mExited;
1519
        private boolean mPaused;
1520
        private boolean mHasSurface;
1521
        private boolean mWaitingForSurface;
1522
        private boolean mHaveEglContext;
1523
        private boolean mHaveEglSurface;
1524
        private int mWidth;
1525
        private int mHeight;
1526
        private int mRenderMode;
1527
        private boolean mRequestRender;
1528
        private boolean mRenderComplete;
1529
        private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
1530
        private GL10 mGL;
1531
        private boolean mHasFocus;
1532
        private boolean mSafeMode = false;
1533
 
1534
        // End of member variables protected by the sGLThreadManager monitor.
1535
 
1536
        private Renderer mRenderer;
1537
        private EglHelper mEglHelper;
1538
    }
1539
 
1540
    static class LogWriter extends Writer {
1541
 
1542
        @Override public void close() {
1543
            flushBuilder();
1544
        }
1545
 
1546
        @Override public void flush() {
1547
            flushBuilder();
1548
        }
1549
 
1550
        @Override public void write(char[] buf, int offset, int count) {
1551
            for(int i = 0; i < count; i++) {
1552
                char c = buf[offset + i];
1553
                if ( c == '\n') {
1554
                    flushBuilder();
1555
                }
1556
                else {
1557
                    mBuilder.append(c);
1558
                }
1559
            }
1560
        }
1561
 
1562
        private void flushBuilder() {
1563
            if (mBuilder.length() > 0) {
1564
                //DebugLog.v("GLSurfaceView", mBuilder.toString());
1565
                mBuilder.delete(0, mBuilder.length());
1566
            }
1567
        }
1568
 
1569
        private StringBuilder mBuilder = new StringBuilder();
1570
    }
1571
 
1572
 
1573
    private void checkRenderThreadState() {
1574
        if (mGLThread != null) {
1575
            throw new IllegalStateException(
1576
                    "setRenderer has already been called for this instance.");
1577
        }
1578
    }
1579
 
1580
    private static class GLThreadManager {
1581
 
1582
        public synchronized void threadExiting(GLThread thread) {
1583
            if (LOG_THREADS) {
1584
                //DebugLog.i("GLThread", "exiting tid=" +  thread.getId());
1585
            }
1586
            thread.mExited = true;
1587
            if (mEglOwner == thread) {
1588
                mEglOwner = null;
1589
            }
1590
            notifyAll();
1591
        }
1592
 
1593
        /*
1594
         * Tries once to acquire the right to use an EGL
1595
         * surface. Does not block. Requires that we are already
1596
         * in the sGLThreadManager monitor when this is called.
1597
         *
1598
         * @return true if the right to use an EGL surface was acquired.
1599
         */
1600
        public boolean tryAcquireEglSurfaceLocked(GLThread thread) {
1601
            if (mEglOwner == thread || mEglOwner == null) {
1602
                mEglOwner = thread;
1603
                notifyAll();
1604
                return true;
1605
            }
1606
            checkGLESVersion();
1607
            if (mMultipleGLESContextsAllowed) {
1608
                return true;
1609
            }
1610
            return false;
1611
        }
1612
        /*
1613
         * Releases the EGL surface. Requires that we are already in the
1614
         * sGLThreadManager monitor when this is called.
1615
         */
1616
        public void releaseEglSurfaceLocked(GLThread thread) {
1617
            if (mEglOwner == thread) {
1618
                mEglOwner = null;
1619
            }
1620
            notifyAll();
1621
        }
1622
 
1623
        public synchronized void checkGLDriver(GL10 gl) {
1624
            if (! mGLESDriverCheckComplete) {
1625
                checkGLESVersion();
1626
                if (mGLESVersion < kGLES_20) {
1627
                    String renderer = gl.glGetString(GL10.GL_RENDERER);
1628
                    mMultipleGLESContextsAllowed = false;
1629
                    notifyAll();
1630
                }
1631
                mGLESDriverCheckComplete = true;
1632
            }
1633
        }
1634
 
1635
        private void checkGLESVersion() {
1636
            if (! mGLESVersionCheckComplete) {
1637
                mGLESVersion = ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
1638
                if (mGLESVersion >= kGLES_20) {
1639
                    mMultipleGLESContextsAllowed = true;
1640
                }
1641
                mGLESVersionCheckComplete = true;
1642
            }
1643
 
1644
        }
1645
 
1646
        private boolean mGLESVersionCheckComplete;
1647
        private int mGLESVersion;
1648
        private boolean mGLESDriverCheckComplete;
1649
        private boolean mMultipleGLESContextsAllowed;
1650
        private int mGLContextCount;
1651
        private static final int kGLES_20 = 0x20000;
1652
        private GLThread mEglOwner;
1653
 
1654
    }
1655
 
1656
    private static final GLThreadManager sGLThreadManager = new GLThreadManager();
1657
    private boolean mSizeChanged = true;
1658
 
1659
    private GLThread mGLThread;
1660
    private EGLConfigChooser mEGLConfigChooser;
1661
    private EGLContextFactory mEGLContextFactory;
1662
    private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
1663
    private GLWrapper mGLWrapper;
1664
    private int mDebugFlags;
1665
    private int mEGLContextClientVersion;
1666
 
1667
 
1668
}