Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 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 
     17 package com.cooliris.media;
     18 
     19 import android.content.Context;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Canvas;
     22 import android.graphics.Rect;
     23 import android.hardware.Sensor;
     24 import android.hardware.SensorEvent;
     25 import android.hardware.SensorEventListener;
     26 import android.hardware.SensorManager;
     27 import android.opengl.GLSurfaceView;
     28 import android.opengl.GLU;
     29 import android.opengl.GLUtils;
     30 import android.os.Process;
     31 import android.os.SystemClock;
     32 import android.util.Log;
     33 import android.util.SparseArray;
     34 import android.view.KeyEvent;
     35 import android.view.MotionEvent;
     36 import android.view.SurfaceHolder;
     37 
     38 import java.lang.ref.ReferenceQueue;
     39 import java.lang.ref.WeakReference;
     40 import java.util.ArrayList;
     41 
     42 import javax.microedition.khronos.egl.EGLConfig;
     43 import javax.microedition.khronos.opengles.GL10;
     44 import javax.microedition.khronos.opengles.GL11;
     45 import javax.microedition.khronos.opengles.GL11Ext;
     46 
     47 public final class RenderView extends GLSurfaceView implements GLSurfaceView.Renderer, SensorEventListener {
     48     private static final String TAG = "RenderView";
     49     private static final int NUM_TEXTURE_LOAD_THREADS = 4;
     50     private static final int MAX_LOADING_COUNT = 8;
     51 
     52     private static final int EVENT_NONE = 0;
     53     // private static final int EVENT_TOUCH = 1;
     54     private static final int EVENT_KEY = 2;
     55     private static final int EVENT_FOCUS = 3;
     56 
     57     private final SensorManager mSensorManager;
     58 
     59     private GL11 mGL = null;
     60     private int mViewWidth = 0;
     61     private int mViewHeight = 0;
     62 
     63     private RootLayer mRootLayer = null;
     64     private boolean mListsDirty = false;
     65     private static final Lists sLists = new Lists();
     66 
     67     private Layer mTouchEventTarget = null;
     68 
     69     private int mCurrentEventType = EVENT_NONE;
     70     private KeyEvent mCurrentKeyEvent = null;
     71     private boolean mCurrentKeyEventResult = false;
     72     private volatile boolean mPendingSensorEvent = false;
     73 
     74     private int mLoadingCount = 0;
     75     private static final Deque<Texture> sLoadInputQueue = new Deque<Texture>();
     76     private static final Deque<Texture> sLoadInputQueueCached = new Deque<Texture>();
     77     private static final Deque<Texture> sLoadInputQueueVideo = new Deque<Texture>();
     78     private static final Deque<Texture> sLoadOutputQueue = new Deque<Texture>();
     79     private static TextureLoadThread sCachedTextureLoadThread = null;
     80     private static TextureLoadThread sVideoTextureLoadThread = null;
     81     private static final TextureLoadThread[] sTextureLoadThreads = new TextureLoadThread[NUM_TEXTURE_LOAD_THREADS];
     82 
     83     private final Deque<MotionEvent> mTouchEventQueue = new Deque<MotionEvent>();
     84     private final DirectLinkedList<TextureReference> mActiveTextureList = new DirectLinkedList<TextureReference>();
     85     @SuppressWarnings("unchecked")
     86     private final ReferenceQueue mUnreferencedTextureQueue = new ReferenceQueue();
     87 
     88     // Frame time in milliseconds and delta since last frame in seconds. Uses
     89     // SystemClock.getUptimeMillis().
     90     private long mFrameTime = 0;
     91     private float mFrameInterval = 0.0f;
     92     private float mAlpha;
     93 
     94     private long mLoadingExpensiveTexturesStartTime = 0;
     95     private final SparseArray<ResourceTexture> sCacheScaled = new SparseArray<ResourceTexture>();
     96     private final SparseArray<ResourceTexture> sCacheUnscaled = new SparseArray<ResourceTexture>();
     97 
     98     private boolean mFirstDraw;
     99     // The cached texture that is bound to Texture Unit 0.
    100     // We need to reset this to null whenever the active texture unit changes.
    101     private Texture mBoundTexture;
    102 
    103     // Weak reference to a texture that stores the associated texture ID.
    104     private static final class TextureReference extends WeakReference<Texture> {
    105         @SuppressWarnings("unchecked")
    106         public TextureReference(Texture texture, GL11 gl, ReferenceQueue referenceQueue, int textureId) {
    107             super(texture, referenceQueue);
    108             this.textureId = textureId;
    109             this.gl = gl;
    110         }
    111 
    112         public final int textureId;
    113         public final GL11 gl;
    114         public final DirectLinkedList.Entry<TextureReference> activeListEntry = new DirectLinkedList.Entry<TextureReference>(this);
    115     }
    116 
    117     public static final class Lists {
    118         public final ArrayList<Layer> updateList = new ArrayList<Layer>();
    119         public final ArrayList<Layer> opaqueList = new ArrayList<Layer>();
    120         public final ArrayList<Layer> blendedList = new ArrayList<Layer>();
    121         public final ArrayList<Layer> hitTestList = new ArrayList<Layer>();
    122         public final ArrayList<Layer> systemList = new ArrayList<Layer>();
    123 
    124         void clear() {
    125             updateList.clear();
    126             opaqueList.clear();
    127             blendedList.clear();
    128             hitTestList.clear();
    129             systemList.clear();
    130         }
    131     }
    132 
    133     public RenderView(final Context context) {
    134         super(context);
    135         setBackgroundDrawable(null);
    136         setFocusable(true);
    137         setRenderer(this);
    138         mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
    139         if (sCachedTextureLoadThread == null) {
    140             for (int i = 0; i != NUM_TEXTURE_LOAD_THREADS; ++i) {
    141                 TextureLoadThread thread = new TextureLoadThread();
    142                 if (i == 0) {
    143                     sCachedTextureLoadThread = thread;
    144                 }
    145                 if (i == 1) {
    146                     sVideoTextureLoadThread = thread;
    147                 }
    148                 sTextureLoadThreads[i] = thread;
    149                 thread.start();
    150             }
    151         }
    152     }
    153 
    154     public void setRootLayer(RootLayer layer) {
    155         if (mRootLayer != layer) {
    156             mRootLayer = layer;
    157             mListsDirty = true;
    158             if (layer != null) {
    159                 mRootLayer.setSize(mViewWidth, mViewHeight);
    160             }
    161         }
    162     }
    163 
    164     public ResourceTexture getResource(int resourceId) {
    165         return getResourceInternal(resourceId, true);
    166     }
    167 
    168     public ResourceTexture getResource(int resourceId, boolean scaled) {
    169         return getResourceInternal(resourceId, scaled);
    170     }
    171 
    172     private ResourceTexture getResourceInternal(int resourceId, boolean scaled) {
    173         final SparseArray<ResourceTexture> cache = (scaled) ? sCacheScaled : sCacheUnscaled;
    174         ResourceTexture texture = cache.get(resourceId);
    175         if (texture == null && resourceId != 0) {
    176             texture = new ResourceTexture(resourceId, scaled);
    177             cache.put(resourceId, texture);
    178         }
    179         return texture;
    180     }
    181 
    182     public void clearCache() {
    183         clearTextureArray(sCacheScaled);
    184         clearTextureArray(sCacheUnscaled);
    185     }
    186 
    187     private void clearTextureArray(SparseArray<ResourceTexture> array) {
    188         /*
    189          * final int size = array.size(); for (int i = 0; i < size; ++i) {
    190          * ResourceTexture texture = array.get(array.keyAt(i)); if (texture !=
    191          * null) { texture.clear(); } }
    192          */
    193         array.clear();
    194     }
    195 
    196     /** Render API */
    197 
    198     public long getFrameTime() {
    199         return mFrameTime;
    200     }
    201 
    202     public float getFrameInterval() {
    203         return mFrameInterval;
    204     }
    205 
    206     public void prime(Texture texture, boolean highPriority) {
    207         if (texture != null && texture.mState == Texture.STATE_UNLOADED && (highPriority || mLoadingCount < MAX_LOADING_COUNT)) {
    208             queueLoad(texture, highPriority);
    209         }
    210     }
    211 
    212     public void loadTexture(Texture texture) {
    213         if (texture != null) {
    214             switch (texture.mState) {
    215             case Texture.STATE_UNLOADED:
    216             case Texture.STATE_QUEUED:
    217                 int[] textureId = new int[1];
    218                 texture.mState = Texture.STATE_LOADING;
    219                 loadTextureAsync(texture);
    220                 uploadTexture(texture, textureId);
    221                 break;
    222             }
    223         }
    224     }
    225 
    226     private void loadTextureAsync(Texture texture) {
    227         try {
    228             Bitmap bitmap = texture.load(this);
    229             if (bitmap != null) {
    230                 bitmap = Utils.resizeBitmap(bitmap, 1024);
    231                 int width = bitmap.getWidth();
    232                 int height = bitmap.getHeight();
    233                 texture.mWidth = width;
    234                 texture.mHeight = height;
    235                 // Create a padded bitmap if the natural size is not a power of
    236                 // 2.
    237                 if (!Shared.isPowerOf2(width) || !Shared.isPowerOf2(height)) {
    238                     int paddedWidth = Shared.nextPowerOf2(width);
    239                     int paddedHeight = Shared.nextPowerOf2(height);
    240                     Bitmap.Config config = bitmap.getConfig();
    241                     if (config == null)
    242                         config = Bitmap.Config.RGB_565;
    243                     if (width * height >= 512 * 512)
    244                         config = Bitmap.Config.RGB_565;
    245                     Bitmap padded = Bitmap.createBitmap(paddedWidth, paddedHeight, config);
    246                     Canvas canvas = new Canvas(padded);
    247                     canvas.drawBitmap(bitmap, 0, 0, null);
    248                     bitmap.recycle();
    249                     bitmap = padded;
    250                     // Store normalized width and height for use in texture
    251                     // coordinates.
    252                     texture.mNormalizedWidth = (float) width / (float) paddedWidth;
    253                     texture.mNormalizedHeight = (float) height / (float) paddedHeight;
    254                 } else {
    255                     texture.mNormalizedWidth = 1.0f;
    256                     texture.mNormalizedHeight = 1.0f;
    257                 }
    258             }
    259             texture.mBitmap = bitmap;
    260         } catch (Exception e) {
    261             texture.mBitmap = null;
    262         } catch (OutOfMemoryError eMem) {
    263             Log.i(TAG, "Bitmap power of 2 creation fail, outofmemory");
    264             handleLowMemory();
    265         }
    266     }
    267 
    268     public boolean bind(Texture texture) {
    269         if (texture != null) {
    270             if (texture == mBoundTexture)
    271                 return true;
    272             switch (texture.mState) {
    273             case Texture.STATE_UNLOADED:
    274                 if (texture.getClass().equals(ResourceTexture.class)) {
    275                     loadTexture(texture);
    276                     return false;
    277                 }
    278                 if (mLoadingCount < MAX_LOADING_COUNT) {
    279                     queueLoad(texture, false);
    280                 }
    281                 break;
    282             case Texture.STATE_LOADED:
    283                 mGL.glBindTexture(GL11.GL_TEXTURE_2D, texture.mId);
    284                 mBoundTexture = texture;
    285                 return true;
    286             default:
    287                 break;
    288             }
    289         }
    290         return false;
    291     }
    292 
    293     public void setAlpha(float alpha) {
    294         GL11 gl = mGL;
    295         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
    296         gl.glColor4f(alpha, alpha, alpha, alpha);
    297         mAlpha = alpha;
    298     }
    299 
    300     public float getAlpha() {
    301         return mAlpha;
    302     }
    303 
    304     public void setColor(float red, float green, float blue, float alpha) {
    305         GL11 gl = mGL;
    306         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
    307         gl.glColor4f(red, green, blue, alpha);
    308         mAlpha = alpha;
    309     }
    310 
    311     public void resetColor() {
    312         GL11 gl = mGL;
    313         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
    314         gl.glColor4f(1, 1, 1, 1);
    315     }
    316 
    317     public boolean isLoadingExpensiveTextures() {
    318         return mLoadingExpensiveTexturesStartTime != 0;
    319     }
    320 
    321     public long elapsedLoadingExpensiveTextures() {
    322         long startTime = mLoadingExpensiveTexturesStartTime;
    323         if (startTime != 0) {
    324             return SystemClock.uptimeMillis() - startTime;
    325         } else {
    326             return -1;
    327         }
    328     }
    329 
    330     private void queueLoad(final Texture texture, boolean highPriority) {
    331     	// Allow the texture to defer queuing.
    332         if (!texture.shouldQueue()) {
    333             return;
    334         }
    335 
    336         // Change the texture state to loading.
    337         texture.mState = Texture.STATE_LOADING;
    338 
    339         // Push the texture onto the load input queue.
    340         Deque<Texture> inputQueue = (texture.isUncachedVideo()) ? sLoadInputQueueVideo
    341                 : (texture.isCached()) ? sLoadInputQueueCached : sLoadInputQueue;
    342         ;
    343         synchronized (inputQueue) {
    344             if (highPriority) {
    345                 inputQueue.addFirst(texture);
    346                 // Enforce the maximum loading count by removing something from the end of
    347                 // the loading queue, if necessary.
    348                 if (mLoadingCount >= MAX_LOADING_COUNT) {
    349                 	Texture unloadTexture = inputQueue.pollLast();
    350                 	unloadTexture.mState = Texture.STATE_UNLOADED;
    351                 	--mLoadingCount;
    352                 }
    353             } else {
    354                 inputQueue.addLast(texture);
    355             }
    356             inputQueue.notify();
    357         }
    358         ++mLoadingCount;
    359     }
    360 
    361     public void draw2D(Texture texture, float x, float y) {
    362         if (bind(texture)) {
    363             final float width = texture.getWidth();
    364             final float height = texture.getHeight();
    365             ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
    366         }
    367     }
    368 
    369     public void draw2D(Texture texture, float x, float y, float width, float height) {
    370         if (bind(texture)) {
    371             ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, 0f, width, height);
    372         }
    373     }
    374 
    375     public void draw2D(Texture texture, int x, int y, int width, int height) {
    376         if (bind(texture)) {
    377             ((GL11Ext) mGL).glDrawTexiOES(x, (mViewHeight - y - height), 0, width, height);
    378         }
    379     }
    380 
    381     public void draw2D(float x, float y, float z, float width, float height) {
    382         ((GL11Ext) mGL).glDrawTexfOES(x, mViewHeight - y - height, z, width, height);
    383     }
    384 
    385     public boolean bindMixed(Texture from, Texture to, float ratio) {
    386         // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
    387         final GL11 gl = mGL;
    388         boolean bind = true;
    389         bind &= bind(from);
    390         gl.glActiveTexture(GL11.GL_TEXTURE1);
    391         mBoundTexture = null;
    392         bind &= bind(to);
    393         if (!bind) {
    394             return false;
    395         }
    396 
    397         // Enable TEXTURE1.
    398         gl.glEnable(GL11.GL_TEXTURE_2D);
    399 
    400         // Interpolate the RGB and alpha values between both textures.
    401         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_COMBINE);
    402         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
    403         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
    404 
    405         // Specify the interpolation factor via the alpha component of
    406         // GL_TEXTURE_ENV_COLORes.
    407         final float[] color = { 1f, 1f, 1f, ratio };
    408         gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, color, 0);
    409 
    410         // Wire up the interpolation factor for RGB.
    411         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
    412         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
    413 
    414         // Wire up the interpolation factor for alpha.
    415         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
    416         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
    417         return true;
    418     }
    419 
    420     public void unbindMixed() {
    421         // Disable TEXTURE1.
    422         final GL11 gl = mGL;
    423         gl.glDisable(GL11.GL_TEXTURE_2D);
    424 
    425         // Switch back to the default texture unit.
    426         gl.glActiveTexture(GL11.GL_TEXTURE0);
    427         mBoundTexture = null;
    428     }
    429 
    430     public void drawMixed2D(Texture from, Texture to, float ratio, float x, float y, float z, float width, float height) {
    431         final GL11 gl = mGL;
    432 
    433         // Bind "from" and "to" to TEXTURE0 and TEXTURE1, respectively.
    434         if (bind(from)) {
    435             gl.glActiveTexture(GL11.GL_TEXTURE1);
    436             mBoundTexture = null;
    437             if (bind(to)) {
    438                 // Enable TEXTURE1.
    439                 gl.glEnable(GL11.GL_TEXTURE_2D);
    440 
    441                 // Interpolate the RGB and alpha values between both textures.
    442                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_COMBINE);
    443                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_RGB, GL11.GL_INTERPOLATE);
    444                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_COMBINE_ALPHA, GL11.GL_INTERPOLATE);
    445 
    446                 // Specify the interpolation factor via the alpha component of
    447                 // GL_TEXTURE_ENV_COLORes.
    448                 final float[] color = { 1f, 1f, 1f, ratio };
    449                 gl.glTexEnvfv(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_COLOR, color, 0);
    450 
    451                 // Wire up the interpolation factor for RGB.
    452                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_RGB, GL11.GL_CONSTANT);
    453                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_RGB, GL11.GL_SRC_ALPHA);
    454 
    455                 // Wire up the interpolation factor for alpha.
    456                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_SRC2_ALPHA, GL11.GL_CONSTANT);
    457                 gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_OPERAND2_ALPHA, GL11.GL_SRC_ALPHA);
    458 
    459                 // Draw the combined texture.
    460                 ((GL11Ext) gl).glDrawTexfOES(x, mViewHeight - y - height, z, width, height);
    461 
    462                 // Disable TEXTURE1.
    463                 gl.glDisable(GL11.GL_TEXTURE_2D);
    464             }
    465 
    466             // Switch back to the default texture unit.
    467             gl.glActiveTexture(GL11.GL_TEXTURE0);
    468             mBoundTexture = null;
    469         }
    470     }
    471 
    472     public void processAllTextures() {
    473         processTextures(true);
    474     }
    475 
    476     final static int[] textureId = new int[1];
    477 
    478     /** Uploads at most one texture to GL. */
    479     private void processTextures(boolean processAll) {
    480         // Destroy any textures that are no longer referenced.
    481         GL11 gl = mGL;
    482         TextureReference textureReference;
    483         while ((textureReference = (TextureReference) mUnreferencedTextureQueue.poll()) != null) {
    484             textureId[0] = textureReference.textureId;
    485             GL11 glOld = textureReference.gl;
    486             if (glOld == gl) {
    487                 gl.glDeleteTextures(1, textureId, 0);
    488             }
    489             mActiveTextureList.remove(textureReference.activeListEntry);
    490         }
    491         Deque<Texture> outputQueue = sLoadOutputQueue;
    492         Texture texture;
    493         do {
    494             // Upload loaded textures to the GPU one frame at a time.
    495             synchronized (outputQueue) {
    496                 texture = outputQueue.pollFirst();
    497             }
    498             if (texture != null) {
    499                 // Extract the bitmap from the texture.
    500                 uploadTexture(texture, textureId);
    501 
    502                 // Decrement the loading count.
    503                 --mLoadingCount;
    504             } else {
    505                 break;
    506             }
    507         } while (processAll);
    508     }
    509 
    510     private void uploadTexture(Texture texture, int[] textureId) {
    511         Bitmap bitmap = texture.mBitmap;
    512         GL11 gl = mGL;
    513         int glError = GL11.GL_NO_ERROR;
    514         if (bitmap != null) {
    515             final int width = texture.mWidth;
    516             final int height = texture.mHeight;
    517 
    518             // Define a vertically flipped crop rectangle for OES_draw_texture.
    519             int[] cropRect = { 0, height, width, -height };
    520 
    521             // Upload the bitmap to a new texture.
    522             gl.glGenTextures(1, textureId, 0);
    523             gl.glBindTexture(GL11.GL_TEXTURE_2D, textureId[0]);
    524             gl.glTexParameteriv(GL11.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, 0);
    525             gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
    526             gl.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
    527             gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    528             gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    529             GLUtils.texImage2D(GL11.GL_TEXTURE_2D, 0, bitmap, 0);
    530             glError = gl.glGetError();
    531 
    532             bitmap.recycle();
    533             if (glError == GL11.GL_OUT_OF_MEMORY) {
    534                 handleLowMemory();
    535             }
    536             if (glError != GL11.GL_NO_ERROR) {
    537                 // There was an error, we need to retry this texture at some
    538                 // later time
    539                 Log.i(TAG, "Texture creation fail, glError " + glError);
    540                 texture.mId = 0;
    541                 texture.mBitmap = null;
    542                 texture.mState = Texture.STATE_UNLOADED;
    543             } else {
    544                 // Update texture state.
    545                 texture.mBitmap = null;
    546                 texture.mId = textureId[0];
    547                 texture.mState = Texture.STATE_LOADED;
    548 
    549                 // Add to the active list.
    550                 final TextureReference textureRef = new TextureReference(texture, gl, mUnreferencedTextureQueue, textureId[0]);
    551                 mActiveTextureList.add(textureRef.activeListEntry);
    552                 requestRender();
    553             }
    554         } else {
    555             texture.mState = Texture.STATE_ERROR;
    556         }
    557 
    558     }
    559 
    560     @Override
    561     public void onResume() {
    562         super.onResume();
    563         Sensor sensorAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    564         if (sensorAccelerometer != null) {
    565             mSensorManager.registerListener(this, sensorAccelerometer, SensorManager.SENSOR_DELAY_UI);
    566         }
    567         if (mRootLayer != null) {
    568             mRootLayer.onResume();
    569         }
    570     }
    571 
    572     @Override
    573     public void onPause() {
    574         super.onPause();
    575         Log.i(TAG, "OnPause RenderView " + this);
    576         mSensorManager.unregisterListener(this);
    577         if (mRootLayer != null) {
    578             mRootLayer.onPause();
    579         }
    580     }
    581 
    582     private final boolean ENABLE_FPS_TEST = false;
    583     private int mFrameCount = 0;
    584     private long mFrameCountingStart = 0;
    585 
    586     /** Renders a frame of the UI. */
    587     // @Override
    588     public void onDrawFrame(GL10 gl1) {
    589 
    590         if (ENABLE_FPS_TEST) {
    591             long now = System.nanoTime();
    592             if (mFrameCountingStart == 0) {
    593                 mFrameCountingStart = now;
    594             } else if ((now - mFrameCountingStart) > 1000000000) {
    595                 Log.v(TAG, "fps: " + (double) mFrameCount
    596                         * 1000000000 / (now - mFrameCountingStart));
    597                 mFrameCountingStart = now;
    598                 mFrameCount = 0;
    599             }
    600             ++mFrameCount;
    601         }
    602 
    603         GL11 gl = (GL11) gl1;
    604         if (!mFirstDraw) {
    605             Log.i(TAG, "First Draw");
    606         }
    607         mFirstDraw = true;
    608         //setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    609         // Rebuild the display lists if the render tree has changed.
    610         if (mListsDirty) {
    611             updateLists();
    612         }
    613 
    614         boolean wasLoadingExpensiveTextures = isLoadingExpensiveTextures();
    615         boolean loadingExpensiveTextures = false;
    616         int numTextureThreads = sTextureLoadThreads.length;
    617         for (int i = 2; i < numTextureThreads; ++i) {
    618             if (sTextureLoadThreads[i].mIsLoading) {
    619                 loadingExpensiveTextures = true;
    620                 break;
    621             }
    622         }
    623         if (loadingExpensiveTextures != wasLoadingExpensiveTextures) {
    624             mLoadingExpensiveTexturesStartTime = loadingExpensiveTextures ? SystemClock.uptimeMillis() : 0;
    625         }
    626 
    627         // Upload new textures.
    628         processTextures(false);
    629 
    630         // Update the current time and frame time interval.
    631         long now = SystemClock.uptimeMillis();
    632         final float dt = 0.001f * Math.min(50, now - mFrameTime);
    633         mFrameInterval = dt;
    634         mFrameTime = now;
    635 
    636         // Dispatch the current touch event.
    637         processCurrentEvent();
    638         processTouchEvent();
    639         // Run the update pass.
    640         final Lists lists = sLists;
    641         synchronized (lists) {
    642             final ArrayList<Layer> updateList = lists.updateList;
    643             boolean isDirty = false;
    644             for (int i = 0, size = updateList.size(); i != size; ++i) {
    645                 boolean retVal = updateList.get(i).update(this, mFrameInterval);
    646                 isDirty |= retVal;
    647             }
    648             if (isDirty) {
    649                 requestRender();
    650             }
    651 
    652             // Clear the depth buffer.
    653             gl.glClear(GL11.GL_DEPTH_BUFFER_BIT);
    654             gl.glEnable(GL11.GL_SCISSOR_TEST);
    655             gl.glScissor(0, 0, getWidth(), getHeight());
    656 
    657             // Run the opaque pass.
    658             gl.glDisable(GL11.GL_BLEND);
    659             final ArrayList<Layer> opaqueList = lists.opaqueList;
    660             for (int i = opaqueList.size() - 1; i >= 0; --i) {
    661                 final Layer layer = opaqueList.get(i);
    662                 if (!layer.mHidden) {
    663                     layer.renderOpaque(this, gl);
    664                 }
    665             }
    666 
    667             // Run the blended pass.
    668             gl.glEnable(GL11.GL_BLEND);
    669             final ArrayList<Layer> blendedList = lists.blendedList;
    670             for (int i = 0, size = blendedList.size(); i != size; ++i) {
    671                 final Layer layer = blendedList.get(i);
    672                 if (!layer.mHidden) {
    673                     layer.renderBlended(this, gl);
    674                 }
    675             }
    676             gl.glDisable(GL11.GL_BLEND);
    677         }
    678     }
    679 
    680     private void processCurrentEvent() {
    681         final int type = mCurrentEventType;
    682         switch (type) {
    683         case EVENT_KEY:
    684             processKeyEvent();
    685             break;
    686         case EVENT_FOCUS:
    687             processFocusEvent();
    688             break;
    689         default:
    690             break;
    691         }
    692         synchronized (this) {
    693             mCurrentEventType = EVENT_NONE;
    694             this.notify();
    695         }
    696     }
    697 
    698     private void processTouchEvent() {
    699         MotionEvent event = null;
    700         int numEvents = mTouchEventQueue.size();
    701         int i = 0;
    702         do {
    703             // We look at the touch event queue and process one event at a time
    704             synchronized (mTouchEventQueue) {
    705                 event = mTouchEventQueue.pollFirst();
    706             }
    707             if (event == null)
    708                 return;
    709 
    710             // Detect the hit layer.
    711             final int action = event.getAction();
    712             Layer target;
    713             if (action == MotionEvent.ACTION_DOWN) {
    714                 target = hitTest(event.getX(), event.getY());
    715                 mTouchEventTarget = target;
    716             } else {
    717                 target = mTouchEventTarget;
    718             }
    719 
    720             // Dispatch event to the hit layer.
    721             if (target != null) {
    722                 target.onTouchEvent(event);
    723             }
    724 
    725             // Clear the hit layer.
    726             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
    727                 mTouchEventTarget = null;
    728             }
    729             event.recycle();
    730             ++i;
    731         } while (event != null && i < numEvents);
    732         synchronized (this) {
    733             this.notify();
    734         }
    735     }
    736 
    737     private void processKeyEvent() {
    738         // Get the event.
    739         final KeyEvent event = mCurrentKeyEvent;
    740         boolean result = false;
    741         mCurrentKeyEvent = null;
    742 
    743         // Dispatch the event to the root layer.
    744         if (mRootLayer != null) {
    745             if (event.getAction() == KeyEvent.ACTION_DOWN) {
    746                 result = mRootLayer.onKeyDown(event.getKeyCode(), event);
    747             } else {
    748                 result = mRootLayer.onKeyUp(event.getKeyCode(), event);
    749             }
    750         }
    751         mCurrentKeyEventResult = result;
    752     }
    753 
    754     private void processFocusEvent() {
    755         // Get event information.
    756         if (mRootLayer != null) {
    757 
    758         }
    759     }
    760 
    761     private Layer hitTest(float x, float y) {
    762         final ArrayList<Layer> hitTestList = sLists.hitTestList;
    763         for (int i = hitTestList.size() - 1; i >= 0; --i) {
    764             final Layer layer = hitTestList.get(i);
    765             if (layer != null && !layer.mHidden) {
    766                 final float layerX = layer.mX;
    767                 final float layerY = layer.mY;
    768                 if (x >= layerX && y >= layerY && x < layerX + layer.mWidth && y < layerY + layer.mHeight
    769                         && layer.containsPoint(x, y)) {
    770                     return layer;
    771                 }
    772             }
    773         }
    774         return null;
    775     }
    776 
    777     private void updateLists() {
    778         if (mRootLayer != null) {
    779             synchronized (sLists) {
    780                 sLists.clear();
    781                 mRootLayer.generate(this, sLists);
    782             }
    783         }
    784     }
    785 
    786     /**
    787      * Called when the OpenGL surface is recreated without destroying the
    788      * context.
    789      */
    790     public void onSurfaceChanged(GL10 gl1, int width, int height) {
    791         GL11 gl = (GL11) gl1;
    792         mFirstDraw = false;
    793         mViewWidth = width;
    794         mViewHeight = height;
    795         if (mRootLayer != null) {
    796             mRootLayer.setSize(width, height);
    797         }
    798 
    799         // Set the viewport and projection matrix.
    800         final float zNear = 0.1f;
    801         final float zFar = 100.0f;
    802         gl.glViewport(0, 0, width, height);
    803         gl.glMatrixMode(GL11.GL_PROJECTION);
    804         gl.glLoadIdentity();
    805         GLU.gluPerspective(gl, 45.0f, (float) width / height, zNear, zFar);
    806         if (mRootLayer != null) {
    807             mRootLayer.onSurfaceChanged(this, width, height);
    808         }
    809         gl.glMatrixMode(GL11.GL_MODELVIEW);
    810     }
    811 
    812     public void setFov(float fov) {
    813         GL11 gl = mGL;
    814         gl.glMatrixMode(GL11.GL_PROJECTION);
    815         gl.glLoadIdentity();
    816         final float zNear = 0.1f;
    817         final float zFar = 100.0f;
    818         GLU.gluPerspective(gl, fov, (float) getWidth() / getHeight(), zNear, zFar);
    819         gl.glMatrixMode(GL11.GL_MODELVIEW);
    820     }
    821 
    822     /**
    823      * Called when the context is created, possibly after automatic destruction.
    824      */
    825     public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
    826         // Clear the resource texture cache.
    827         clearCache();
    828 
    829         GL11 gl = (GL11) gl1;
    830         if (mGL == null) {
    831             mGL = gl;
    832         } else {
    833             // The GL Object has changed.
    834             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
    835             mGL = gl;
    836         }
    837 
    838         if (ENABLE_FPS_TEST) {
    839             setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
    840         } else {
    841             setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    842         }
    843 
    844         // Increase the priority of the render thread.
    845         // This is commented out to give other threads more CPU.
    846         //Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
    847 
    848         // Disable unused state.
    849         gl.glEnable(GL11.GL_DITHER);
    850         gl.glDisable(GL11.GL_LIGHTING);
    851 
    852         // Set global state.
    853         // gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
    854 
    855         // Enable textures.
    856         gl.glEnable(GL11.GL_TEXTURE_2D);
    857         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
    858 
    859         // Set up state for multitexture operations. Since multitexture is
    860         // currently used
    861         // only for layered crossfades the needed state can be factored out into
    862         // one-time
    863         // initialization. This section may need to be folded into drawMixed2D()
    864         // if multitexture
    865         // is used for other effects.
    866 
    867         // Enable Vertex Arrays
    868         gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
    869         gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    870         gl.glClientActiveTexture(GL11.GL_TEXTURE1);
    871         gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    872         gl.glClientActiveTexture(GL11.GL_TEXTURE0);
    873 
    874         // Enable depth test.
    875         gl.glEnable(GL11.GL_DEPTH_TEST);
    876         gl.glDepthFunc(GL11.GL_LEQUAL);
    877 
    878         // Set the blend function for premultiplied alpha.
    879         gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
    880 
    881         // Set the background color.
    882         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    883         gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
    884 
    885         // Start reloading textures if the context was automatically destroyed.
    886         if (!mActiveTextureList.isEmpty()) {
    887             DirectLinkedList.Entry<TextureReference> iter = mActiveTextureList.getHead();
    888             while (iter != null) {
    889                 final Texture texture = iter.value.get();
    890                 if (texture != null) {
    891                     texture.mState = Texture.STATE_UNLOADED;
    892                 }
    893                 iter = iter.next;
    894             }
    895         }
    896         mActiveTextureList.clear();
    897         if (mRootLayer != null) {
    898             mRootLayer.onSurfaceCreated(this, gl);
    899         }
    900         synchronized (sLists) {
    901             ArrayList<Layer> systemList = sLists.systemList;
    902             for (int i = systemList.size() - 1; i >= 0; --i) {
    903                 systemList.get(i).onSurfaceCreated(this, gl);
    904             }
    905         }
    906     }
    907 
    908     /** Indicates that the accuracy of a sensor value has changed. */
    909     public void onAccuracyChanged(Sensor sensor, int accuracy) {
    910     }
    911 
    912     /** Indicates that a sensor value has changed. */
    913     public void onSensorChanged(SensorEvent event) {
    914         final int type = event.sensor.getType();
    915         if (!mPendingSensorEvent && type == Sensor.TYPE_ACCELEROMETER) {
    916             final SensorEvent e = event;
    917             if (mRootLayer != null)
    918                 mRootLayer.onSensorChanged(RenderView.this, e);
    919         }
    920     }
    921 
    922     @Override
    923     public boolean onTouchEvent(MotionEvent event) {
    924         // Ignore events received before the surface is created to avoid
    925         // deadlocking with GLSurfaceView's needToWait().
    926         if (mGL == null) {
    927             return false;
    928         }
    929         // Wait for the render thread to process this event.
    930         if (mTouchEventQueue.size() > 8 && event.getAction() == MotionEvent.ACTION_MOVE)
    931             return true;
    932         synchronized (mTouchEventQueue) {
    933             MotionEvent eventCopy = MotionEvent.obtain(event);
    934             mTouchEventQueue.addLast(eventCopy);
    935             requestRender();
    936         }
    937         return true;
    938     }
    939 
    940     @Override
    941     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    942         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    943 
    944         // Ignore events received before the surface is created to avoid
    945         // deadlocking with GLSurfaceView's needToWait().
    946         /*
    947          * if (mGL == null) { return; }
    948          *
    949          * // Wait for the render thread to process this event. try {
    950          * synchronized (this) { mCurrentFocusEventGain = gainFocus;
    951          * mCurrentFocusEventDirection = direction; mCurrentEventType =
    952          * EVENT_FOCUS; do { wait(); } while (mCurrentEventType != EVENT_NONE);
    953          * } } catch (InterruptedException e) { // Stop waiting for the render
    954          * thread if interrupted. }
    955          */
    956         requestRender();
    957     }
    958 
    959     @Override
    960     public boolean onKeyDown(int keyCode, KeyEvent event) {
    961         // Ignore events received before the surface is created to avoid
    962         // deadlocking with GLSurfaceView's needToWait().
    963         if (mGL == null) {
    964             return false;
    965         }
    966 
    967         // Wait for the render thread to process this event.
    968         try {
    969             synchronized (this) {
    970                 mCurrentKeyEvent = event;
    971                 mCurrentEventType = EVENT_KEY;
    972                 requestRender();
    973                 long timeout = SystemClock.uptimeMillis() + 50;
    974                 do {
    975                     wait(50);
    976                 } while (mCurrentEventType != EVENT_NONE && SystemClock.uptimeMillis() < timeout);
    977             }
    978         } catch (InterruptedException e) {
    979             // Stop waiting for the render thread if interrupted.
    980         }
    981 
    982         // Key events are handled on the main thread.
    983         boolean retVal = false;
    984         if (!mCurrentKeyEventResult) {
    985             retVal = super.onKeyDown(keyCode, event);
    986         } else {
    987             retVal = true;
    988         }
    989         requestRender();
    990         return retVal;
    991     }
    992 
    993     @Override
    994     public void surfaceDestroyed(SurfaceHolder holder) {
    995         super.surfaceDestroyed(holder);
    996     }
    997 
    998     @Override
    999     protected void onAttachedToWindow() {
   1000         super.onAttachedToWindow();
   1001     }
   1002 
   1003     @Override
   1004     protected void onDetachedFromWindow() {
   1005         super.onDetachedFromWindow();
   1006     }
   1007 
   1008     private final class TextureLoadThread extends Thread {
   1009         public boolean mIsLoading;
   1010 
   1011         public TextureLoadThread() {
   1012             super("TextureLoad");
   1013         }
   1014 
   1015         @Override
   1016         public void run() {
   1017             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
   1018             Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo
   1019                     : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
   1020             Deque<Texture> outputQueue = sLoadOutputQueue;
   1021             try {
   1022                 for (;;) {
   1023                     // Pop the next texture from the input queue.
   1024                     Texture texture = null;
   1025                     synchronized (inputQueue) {
   1026                         while ((texture = inputQueue.pollFirst()) == null) {
   1027                             inputQueue.wait();
   1028                         }
   1029                     }
   1030                     if (sCachedTextureLoadThread != this)
   1031                         mIsLoading = true;
   1032                     // Load the texture bitmap.
   1033                     load(texture);
   1034                     mIsLoading = false;
   1035 
   1036                     // Push the texture onto the output queue.
   1037                     synchronized (outputQueue) {
   1038                         outputQueue.addLast(texture);
   1039                     }
   1040                 }
   1041             } catch (InterruptedException e) {
   1042                 // Terminate the thread.
   1043             }
   1044         }
   1045 
   1046         private void load(Texture texture) {
   1047             // Generate the texture bitmap.
   1048             RenderView view = RenderView.this;
   1049             view.loadTextureAsync(texture);
   1050             view.requestRender();
   1051         }
   1052     }
   1053 
   1054     public void shutdown() {
   1055         mRootLayer = null;
   1056         synchronized (sLists) {
   1057             sLists.clear();
   1058         }
   1059     }
   1060 
   1061     public void handleLowMemory() {
   1062         Log.i(TAG, "Handling low memory condition");
   1063         if (mRootLayer != null) {
   1064             mRootLayer.handleLowMemory();
   1065         }
   1066     }
   1067 
   1068     public Lists getLists() {
   1069         return sLists;
   1070     }
   1071 }
   1072