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