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