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 java.lang.ref.ReferenceQueue;
     20 import java.lang.ref.WeakReference;
     21 import java.util.ArrayList;
     22 
     23 import javax.microedition.khronos.egl.EGLConfig;
     24 import javax.microedition.khronos.opengles.GL10;
     25 import javax.microedition.khronos.opengles.GL11;
     26 import javax.microedition.khronos.opengles.GL11Ext;
     27 
     28 import android.content.Context;
     29 import android.graphics.Bitmap;
     30 import android.graphics.Canvas;
     31 import android.graphics.Rect;
     32 import android.hardware.Sensor;
     33 import android.hardware.SensorEvent;
     34 import android.hardware.SensorEventListener;
     35 import android.hardware.SensorManager;
     36 import android.opengl.GLU;
     37 import android.opengl.GLUtils;
     38 import android.opengl.GLSurfaceView;
     39 import android.os.Process;
     40 import android.os.SystemClock;
     41 import android.util.Log;
     42 import android.util.SparseArray;
     43 import android.view.KeyEvent;
     44 import android.view.MotionEvent;
     45 import android.view.SurfaceHolder;
     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 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, (int) (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     /** Renders a frame of the UI. */
    583     // @Override
    584     public void onDrawFrame(GL10 gl1) {
    585         GL11 gl = (GL11) gl1;
    586         if (!mFirstDraw) {
    587             Log.i(TAG, "First Draw");
    588         }
    589         mFirstDraw = true;
    590         // setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    591         // Rebuild the display lists if the render tree has changed.
    592         if (mListsDirty) {
    593             updateLists();
    594         }
    595 
    596         boolean wasLoadingExpensiveTextures = isLoadingExpensiveTextures();
    597         boolean loadingExpensiveTextures = false;
    598         int numTextureThreads = sTextureLoadThreads.length;
    599         for (int i = 2; i < numTextureThreads; ++i) {
    600             if (sTextureLoadThreads[i].mIsLoading) {
    601                 loadingExpensiveTextures = true;
    602                 break;
    603             }
    604         }
    605         if (loadingExpensiveTextures != wasLoadingExpensiveTextures) {
    606             mLoadingExpensiveTexturesStartTime = loadingExpensiveTextures ? SystemClock.uptimeMillis() : 0;
    607         }
    608 
    609         // Upload new textures.
    610         processTextures(false);
    611 
    612         // Update the current time and frame time interval.
    613         long now = SystemClock.uptimeMillis();
    614         final float dt = 0.001f * Math.min(50, now - mFrameTime);
    615         mFrameInterval = dt;
    616         mFrameTime = now;
    617 
    618         // Dispatch the current touch event.
    619         processCurrentEvent();
    620         processTouchEvent();
    621         // Run the update pass.
    622         final Lists lists = sLists;
    623         synchronized (lists) {
    624             final ArrayList<Layer> updateList = lists.updateList;
    625             boolean isDirty = false;
    626             for (int i = 0, size = updateList.size(); i != size; ++i) {
    627                 boolean retVal = updateList.get(i).update(this, mFrameInterval);
    628                 isDirty |= retVal;
    629             }
    630             if (isDirty) {
    631                 requestRender();
    632             }
    633 
    634             // Clear the depth buffer.
    635             gl.glClear(GL11.GL_DEPTH_BUFFER_BIT);
    636             gl.glEnable(GL11.GL_SCISSOR_TEST);
    637             gl.glScissor(0, 0, getWidth(), getHeight());
    638 
    639             // Run the opaque pass.
    640             gl.glDisable(GL11.GL_BLEND);
    641             final ArrayList<Layer> opaqueList = lists.opaqueList;
    642             for (int i = opaqueList.size() - 1; i >= 0; --i) {
    643                 final Layer layer = opaqueList.get(i);
    644                 if (!layer.mHidden) {
    645                     layer.renderOpaque(this, gl);
    646                 }
    647             }
    648 
    649             // Run the blended pass.
    650             gl.glEnable(GL11.GL_BLEND);
    651             final ArrayList<Layer> blendedList = lists.blendedList;
    652             for (int i = 0, size = blendedList.size(); i != size; ++i) {
    653                 final Layer layer = blendedList.get(i);
    654                 if (!layer.mHidden) {
    655                     layer.renderBlended(this, gl);
    656                 }
    657             }
    658             gl.glDisable(GL11.GL_BLEND);
    659         }
    660     }
    661 
    662     private void processCurrentEvent() {
    663         final int type = mCurrentEventType;
    664         switch (type) {
    665         case EVENT_KEY:
    666             processKeyEvent();
    667             break;
    668         case EVENT_FOCUS:
    669             processFocusEvent();
    670             break;
    671         default:
    672             break;
    673         }
    674         synchronized (this) {
    675             mCurrentEventType = EVENT_NONE;
    676             this.notify();
    677         }
    678     }
    679 
    680     private void processTouchEvent() {
    681         MotionEvent event = null;
    682         int numEvents = mTouchEventQueue.size();
    683         int i = 0;
    684         do {
    685             // We look at the touch event queue and process one event at a time
    686             synchronized (mTouchEventQueue) {
    687                 event = mTouchEventQueue.pollFirst();
    688             }
    689             if (event == null)
    690                 return;
    691 
    692             // Detect the hit layer.
    693             final int action = event.getAction();
    694             Layer target;
    695             if (action == MotionEvent.ACTION_DOWN) {
    696                 target = hitTest(event.getX(), event.getY());
    697                 mTouchEventTarget = target;
    698             } else {
    699                 target = mTouchEventTarget;
    700             }
    701 
    702             // Dispatch event to the hit layer.
    703             if (target != null) {
    704                 target.onTouchEvent(event);
    705             }
    706 
    707             // Clear the hit layer.
    708             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
    709                 mTouchEventTarget = null;
    710             }
    711             event.recycle();
    712             ++i;
    713         } while (event != null && i < numEvents);
    714         synchronized (this) {
    715             this.notify();
    716         }
    717     }
    718 
    719     private void processKeyEvent() {
    720         // Get the event.
    721         final KeyEvent event = mCurrentKeyEvent;
    722         boolean result = false;
    723         mCurrentKeyEvent = null;
    724 
    725         // Dispatch the event to the root layer.
    726         if (mRootLayer != null) {
    727             if (event.getAction() == KeyEvent.ACTION_DOWN) {
    728                 result = mRootLayer.onKeyDown(event.getKeyCode(), event);
    729             } else {
    730                 result = mRootLayer.onKeyUp(event.getKeyCode(), event);
    731             }
    732         }
    733         mCurrentKeyEventResult = result;
    734     }
    735 
    736     private void processFocusEvent() {
    737         // Get event information.
    738         if (mRootLayer != null) {
    739 
    740         }
    741     }
    742 
    743     private Layer hitTest(float x, float y) {
    744         final ArrayList<Layer> hitTestList = sLists.hitTestList;
    745         for (int i = hitTestList.size() - 1; i >= 0; --i) {
    746             final Layer layer = hitTestList.get(i);
    747             if (layer != null && !layer.mHidden) {
    748                 final float layerX = layer.mX;
    749                 final float layerY = layer.mY;
    750                 if (x >= layerX && y >= layerY && x < layerX + layer.mWidth && y < layerY + layer.mHeight
    751                         && layer.containsPoint(x, y)) {
    752                     return layer;
    753                 }
    754             }
    755         }
    756         return null;
    757     }
    758 
    759     private void updateLists() {
    760         if (mRootLayer != null) {
    761             synchronized (sLists) {
    762                 sLists.clear();
    763                 mRootLayer.generate(this, sLists);
    764             }
    765         }
    766     }
    767 
    768     /**
    769      * Called when the OpenGL surface is recreated without destroying the
    770      * context.
    771      */
    772     public void onSurfaceChanged(GL10 gl1, int width, int height) {
    773         GL11 gl = (GL11) gl1;
    774         mFirstDraw = false;
    775         mViewWidth = width;
    776         mViewHeight = height;
    777         if (mRootLayer != null) {
    778             mRootLayer.setSize(width, height);
    779         }
    780 
    781         // Set the viewport and projection matrix.
    782         final float zNear = 0.1f;
    783         final float zFar = 100.0f;
    784         gl.glViewport(0, 0, width, height);
    785         gl.glMatrixMode(GL11.GL_PROJECTION);
    786         gl.glLoadIdentity();
    787         GLU.gluPerspective(gl, 45.0f, (float) width / height, zNear, zFar);
    788         if (mRootLayer != null) {
    789             mRootLayer.onSurfaceChanged(this, width, height);
    790         }
    791         gl.glMatrixMode(GL11.GL_MODELVIEW);
    792     }
    793 
    794     public void setFov(float fov) {
    795         GL11 gl = mGL;
    796         gl.glMatrixMode(GL11.GL_PROJECTION);
    797         gl.glLoadIdentity();
    798         final float zNear = 0.1f;
    799         final float zFar = 100.0f;
    800         GLU.gluPerspective(gl, fov, (float) getWidth() / getHeight(), zNear, zFar);
    801         gl.glMatrixMode(GL11.GL_MODELVIEW);
    802     }
    803 
    804     /**
    805      * Called when the context is created, possibly after automatic destruction.
    806      */
    807     public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
    808         // Clear the resource texture cache.
    809         clearCache();
    810 
    811         GL11 gl = (GL11) gl1;
    812         if (mGL == null) {
    813             mGL = gl;
    814         } else {
    815             // The GL Object has changed.
    816             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
    817             mGL = gl;
    818         }
    819         setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    820         // Increase the priority of the render thread.
    821         // This is commented out to give other threads more CPU.
    822         //Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
    823 
    824         // Disable unused state.
    825         gl.glEnable(GL11.GL_DITHER);
    826         gl.glDisable(GL11.GL_LIGHTING);
    827 
    828         // Set global state.
    829         // gl.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
    830 
    831         // Enable textures.
    832         gl.glEnable(GL11.GL_TEXTURE_2D);
    833         gl.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_REPLACE);
    834 
    835         // Set up state for multitexture operations. Since multitexture is
    836         // currently used
    837         // only for layered crossfades the needed state can be factored out into
    838         // one-time
    839         // initialization. This section may need to be folded into drawMixed2D()
    840         // if multitexture
    841         // is used for other effects.
    842 
    843         // Enable Vertex Arrays
    844         gl.glEnableClientState(GL11.GL_VERTEX_ARRAY);
    845         gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    846         gl.glClientActiveTexture(GL11.GL_TEXTURE1);
    847         gl.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
    848         gl.glClientActiveTexture(GL11.GL_TEXTURE0);
    849 
    850         // Enable depth test.
    851         gl.glEnable(GL11.GL_DEPTH_TEST);
    852         gl.glDepthFunc(GL11.GL_LEQUAL);
    853 
    854         // Set the blend function for premultiplied alpha.
    855         gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
    856 
    857         // Set the background color.
    858         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    859         gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
    860 
    861         // Start reloading textures if the context was automatically destroyed.
    862         if (!mActiveTextureList.isEmpty()) {
    863             DirectLinkedList.Entry<TextureReference> iter = mActiveTextureList.getHead();
    864             while (iter != null) {
    865                 final Texture texture = iter.value.get();
    866                 if (texture != null) {
    867                     texture.mState = Texture.STATE_UNLOADED;
    868                 }
    869                 iter = iter.next;
    870             }
    871         }
    872         mActiveTextureList.clear();
    873         if (mRootLayer != null) {
    874             mRootLayer.onSurfaceCreated(this, gl);
    875         }
    876         synchronized (sLists) {
    877             ArrayList<Layer> systemList = sLists.systemList;
    878             for (int i = systemList.size() - 1; i >= 0; --i) {
    879                 systemList.get(i).onSurfaceCreated(this, gl);
    880             }
    881         }
    882     }
    883 
    884     /** Indicates that the accuracy of a sensor value has changed. */
    885     public void onAccuracyChanged(Sensor sensor, int accuracy) {
    886     }
    887 
    888     /** Indicates that a sensor value has changed. */
    889     public void onSensorChanged(SensorEvent event) {
    890         final int type = event.sensor.getType();
    891         if (!mPendingSensorEvent && type == Sensor.TYPE_ACCELEROMETER) {
    892             final SensorEvent e = event;
    893             if (mRootLayer != null)
    894                 mRootLayer.onSensorChanged(RenderView.this, e);
    895         }
    896     }
    897 
    898     @Override
    899     public boolean onTouchEvent(MotionEvent event) {
    900         // Ignore events received before the surface is created to avoid
    901         // deadlocking with GLSurfaceView's needToWait().
    902         if (mGL == null) {
    903             return false;
    904         }
    905         // Wait for the render thread to process this event.
    906         if (mTouchEventQueue.size() > 8 && event.getAction() == MotionEvent.ACTION_MOVE)
    907             return true;
    908         synchronized (mTouchEventQueue) {
    909             MotionEvent eventCopy = MotionEvent.obtain(event);
    910             mTouchEventQueue.addLast(eventCopy);
    911             requestRender();
    912         }
    913         return true;
    914     }
    915 
    916     @Override
    917     protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
    918         super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
    919 
    920         // Ignore events received before the surface is created to avoid
    921         // deadlocking with GLSurfaceView's needToWait().
    922         /*
    923          * if (mGL == null) { return; }
    924          *
    925          * // Wait for the render thread to process this event. try {
    926          * synchronized (this) { mCurrentFocusEventGain = gainFocus;
    927          * mCurrentFocusEventDirection = direction; mCurrentEventType =
    928          * EVENT_FOCUS; do { wait(); } while (mCurrentEventType != EVENT_NONE);
    929          * } } catch (InterruptedException e) { // Stop waiting for the render
    930          * thread if interrupted. }
    931          */
    932         requestRender();
    933     }
    934 
    935     @Override
    936     public boolean onKeyDown(int keyCode, KeyEvent event) {
    937         // Ignore events received before the surface is created to avoid
    938         // deadlocking with GLSurfaceView's needToWait().
    939         if (mGL == null) {
    940             return false;
    941         }
    942 
    943         // Wait for the render thread to process this event.
    944         try {
    945             synchronized (this) {
    946                 mCurrentKeyEvent = event;
    947                 mCurrentEventType = EVENT_KEY;
    948                 requestRender();
    949                 long timeout = SystemClock.uptimeMillis() + 50;
    950                 do {
    951                     wait(50);
    952                 } while (mCurrentEventType != EVENT_NONE && SystemClock.uptimeMillis() < timeout);
    953             }
    954         } catch (InterruptedException e) {
    955             // Stop waiting for the render thread if interrupted.
    956         }
    957 
    958         // Key events are handled on the main thread.
    959         boolean retVal = false;
    960         if (!mCurrentKeyEventResult) {
    961             retVal = super.onKeyDown(keyCode, event);
    962         } else {
    963             retVal = true;
    964         }
    965         requestRender();
    966         return retVal;
    967     }
    968 
    969     @Override
    970     public void surfaceDestroyed(SurfaceHolder holder) {
    971         super.surfaceDestroyed(holder);
    972     }
    973 
    974     @Override
    975     protected void onAttachedToWindow() {
    976         super.onAttachedToWindow();
    977     }
    978 
    979     @Override
    980     protected void onDetachedFromWindow() {
    981         super.onDetachedFromWindow();
    982     }
    983 
    984     private final class TextureLoadThread extends Thread {
    985         public boolean mIsLoading;
    986 
    987         public TextureLoadThread() {
    988             super("TextureLoad");
    989         }
    990 
    991         public void run() {
    992             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    993             Deque<Texture> inputQueue = (sVideoTextureLoadThread == this) ? sLoadInputQueueVideo
    994                     : ((sCachedTextureLoadThread == this) ? sLoadInputQueueCached : sLoadInputQueue);
    995             Deque<Texture> outputQueue = sLoadOutputQueue;
    996             try {
    997                 for (;;) {
    998                     // Pop the next texture from the input queue.
    999                     Texture texture = null;
   1000                     synchronized (inputQueue) {
   1001                         while ((texture = inputQueue.pollFirst()) == null) {
   1002                             inputQueue.wait();
   1003                         }
   1004                     }
   1005                     if (sCachedTextureLoadThread != this)
   1006                         mIsLoading = true;
   1007                     // Load the texture bitmap.
   1008                     load(texture);
   1009                     mIsLoading = false;
   1010 
   1011                     // Push the texture onto the output queue.
   1012                     synchronized (outputQueue) {
   1013                         outputQueue.addLast(texture);
   1014                     }
   1015                 }
   1016             } catch (InterruptedException e) {
   1017                 // Terminate the thread.
   1018             }
   1019         }
   1020 
   1021         private void load(Texture texture) {
   1022             // Generate the texture bitmap.
   1023             RenderView view = RenderView.this;
   1024             view.loadTextureAsync(texture);
   1025             view.requestRender();
   1026         }
   1027     }
   1028 
   1029     public void shutdown() {
   1030         mRootLayer = null;
   1031         synchronized (sLists) {
   1032             sLists.clear();
   1033         }
   1034     }
   1035 
   1036     public void handleLowMemory() {
   1037         Log.i(TAG, "Handling low memory condition");
   1038         if (mRootLayer != null) {
   1039             mRootLayer.handleLowMemory();
   1040         }
   1041     }
   1042 
   1043     public Lists getLists() {
   1044         return sLists;
   1045     }
   1046 }
   1047